feat(shared): pass config data via init (#2042)

This commit is contained in:
Jonas L 2022-08-12 15:12:39 +02:00 committed by GitHub
parent 2bde574487
commit 1147853c63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 65 additions and 44 deletions

View File

@ -34,7 +34,7 @@ def cli(
Run analyzer. Run analyzer.
""" """
setup_logger(level_from_name(log_level), log_filepath) setup_logger(level_from_name(log_level), log_filepath)
config = Config(filepath=config_filepath) config = Config(config_filepath)
# Start up the StatusReporter process # Start up the StatusReporter process
StatusReporter.start_thread(retry_queue_filepath) StatusReporter.start_thread(retry_queue_filepath)

View File

@ -75,7 +75,7 @@ class ApiClient:
def __init__(self, logger=None, config_path="/etc/libretime/config.yml"): def __init__(self, logger=None, config_path="/etc/libretime/config.yml"):
self.logger = logger or logging self.logger = logger or logging
config = Config(filepath=config_path) config = Config(config_path)
self.base_url = config.general.public_url self.base_url = config.general.public_url
self.api_key = config.general.api_key self.api_key = config.general.api_key

View File

@ -22,7 +22,7 @@ from ._schema import Config
LIBRETIME_LOG_FILEPATH = getenv("LIBRETIME_LOG_FILEPATH") LIBRETIME_LOG_FILEPATH = getenv("LIBRETIME_LOG_FILEPATH")
LIBRETIME_CONFIG_FILEPATH = getenv("LIBRETIME_CONFIG_FILEPATH") LIBRETIME_CONFIG_FILEPATH = getenv("LIBRETIME_CONFIG_FILEPATH")
CONFIG = Config(filepath=LIBRETIME_CONFIG_FILEPATH) CONFIG = Config(LIBRETIME_CONFIG_FILEPATH)
SECRET_KEY = CONFIG.general.api_key SECRET_KEY = CONFIG.general.api_key

View File

@ -106,7 +106,7 @@ def cli(log_level: str, log_filepath: Optional[Path], config_filepath: Optional[
Run playout. Run playout.
""" """
setup_logger(level_from_name(log_level), log_filepath) setup_logger(level_from_name(log_level), log_filepath)
config = Config(filepath=config_filepath) config = Config(config_filepath)
try: try:
for dir_path in [CACHE_DIR, RECORD_DIR]: for dir_path in [CACHE_DIR, RECORD_DIR]:

View File

@ -29,7 +29,7 @@ class Config(BaseConfig):
rabbitmq: RabbitMQConfig rabbitmq: RabbitMQConfig
analyzer: AnalyzerConfig analyzer: AnalyzerConfig
config = Config(filepath="/etc/libretime/config.yml") config = Config("/etc/libretime/config.yml")
``` ```
> Don't instantiate a sub model if it has a required field, otherwise the `Config` class import will raise a `ValidationError`. > Don't instantiate a sub model if it has a required field, otherwise the `Config` class import will raise a `ValidationError`.

View File

@ -24,20 +24,23 @@ class BaseConfig(BaseModel):
:returns: configuration class :returns: configuration class
""" """
# pylint: disable=no-self-argument
def __init__( def __init__(
self, _self,
_filepath: Optional[Union[Path, str]] = None,
*, *,
env_prefix: str = DEFAULT_ENV_PREFIX, _env_prefix: str = DEFAULT_ENV_PREFIX,
env_delimiter: str = "_", _env_delimiter: str = "_",
filepath: Optional[Union[Path, str]] = None, **kwargs: Any,
) -> None: ) -> None:
if filepath is not None: if _filepath is not None:
filepath = Path(filepath) _filepath = Path(_filepath)
env_loader = EnvLoader(self.schema(), env_prefix, env_delimiter) env_loader = EnvLoader(_self.schema(), _env_prefix, _env_delimiter)
values = deep_merge_dict( values = deep_merge_dict(
self._load_file_values(filepath), kwargs,
_self._load_file_values(_filepath),
env_loader.load(), env_loader.load(),
) )
@ -67,36 +70,39 @@ class BaseConfig(BaseModel):
return {} return {}
def deep_merge_dict(base: Dict[str, Any], next_: Dict[str, Any]) -> Dict[str, Any]: def deep_merge_dict(base: Dict[str, Any], *elements: Dict[str, Any]) -> Dict[str, Any]:
result = base.copy() result = base.copy()
for key, value in next_.items():
if key in result:
if isinstance(result[key], dict) and isinstance(value, dict):
result[key] = deep_merge_dict(result[key], value)
continue
if isinstance(result[key], list) and isinstance(value, list): for element in elements:
result[key] = deep_merge_list(result[key], value) for key, value in element.items():
continue if key in result:
if isinstance(result[key], dict) and isinstance(value, dict):
result[key] = deep_merge_dict(result[key], value)
continue
if value: if isinstance(result[key], list) and isinstance(value, list):
result[key] = value result[key] = deep_merge_list(result[key], value)
continue
if value:
result[key] = value
return result return result
def deep_merge_list(base: List[Any], next_: List[Any]) -> List[Any]: def deep_merge_list(base: List[Any], *elements: List[Any]) -> List[Any]:
result: List[Any] = [] result: List[Any] = []
for base_item, next_item in zip_longest(base, next_): for element in elements:
if isinstance(base_item, list) and isinstance(next_item, list): for base_item, next_item in zip_longest(base, element):
result.append(deep_merge_list(base_item, next_item)) if isinstance(base_item, list) and isinstance(next_item, list):
continue result.append(deep_merge_list(base_item, next_item))
continue
if isinstance(base_item, dict) and isinstance(next_item, dict): if isinstance(base_item, dict) and isinstance(next_item, dict):
result.append(deep_merge_dict(base_item, next_item)) result.append(deep_merge_dict(base_item, next_item))
continue continue
if next_item: if next_item:
result.append(next_item) result.append(next_item)
return result return result

View File

@ -57,7 +57,7 @@ def test_base_config(tmp_path: Path):
"WRONGPREFIX_API_KEY": "invalid", "WRONGPREFIX_API_KEY": "invalid",
}, },
): ):
config = FixtureConfig(filepath=config_filepath) config = FixtureConfig(config_filepath)
assert config.public_url == "http://libretime.example.com" assert config.public_url == "http://libretime.example.com"
assert config.api_key == "f3bf04fc" assert config.api_key == "f3bf04fc"
@ -69,7 +69,7 @@ def test_base_config(tmp_path: Path):
# Optional model: loading default values (rabbitmq) # Optional model: loading default values (rabbitmq)
with mock.patch.dict(environ, {}): with mock.patch.dict(environ, {}):
config = FixtureConfig(filepath=config_filepath) config = FixtureConfig(config_filepath)
assert config.allowed_hosts == ["example.com", "sub.example.com"] assert config.allowed_hosts == ["example.com", "sub.example.com"]
assert config.rabbitmq.host == "localhost" assert config.rabbitmq.host == "localhost"
assert config.rabbitmq.port == 5672 assert config.rabbitmq.port == 5672
@ -82,7 +82,7 @@ def test_base_config(tmp_path: Path):
"LIBRETIME_ALLOWED_HOSTS": "example.com, changed.example.com", "LIBRETIME_ALLOWED_HOSTS": "example.com, changed.example.com",
}, },
): ):
config = FixtureConfig(filepath=config_filepath) config = FixtureConfig(config_filepath)
assert config.allowed_hosts == ["example.com", "changed.example.com"] assert config.allowed_hosts == ["example.com", "changed.example.com"]
assert config.rabbitmq.host == "changed" assert config.rabbitmq.host == "changed"
assert config.rabbitmq.port == 5672 assert config.rabbitmq.port == 5672
@ -111,19 +111,19 @@ def test_base_config_required_submodel(tmp_path: Path):
# With config file # With config file
with mock.patch.dict(environ, {}): with mock.patch.dict(environ, {}):
config = FixtureWithRequiredSubmodelConfig(filepath=config_filepath) config = FixtureWithRequiredSubmodelConfig(config_filepath)
assert config.required.api_key == "test_key" assert config.required.api_key == "test_key"
assert config.required.with_default == "original" assert config.required.with_default == "original"
# With env variables # With env variables
with mock.patch.dict(environ, {"LIBRETIME_REQUIRED_API_KEY": "test_key"}): with mock.patch.dict(environ, {"LIBRETIME_REQUIRED_API_KEY": "test_key"}):
config = FixtureWithRequiredSubmodelConfig(filepath=None) config = FixtureWithRequiredSubmodelConfig(None)
assert config.required.api_key == "test_key" assert config.required.api_key == "test_key"
assert config.required.with_default == "original" assert config.required.with_default == "original"
# With env variables override # With env variables override
with mock.patch.dict(environ, {"LIBRETIME_REQUIRED_API_KEY": "changed"}): with mock.patch.dict(environ, {"LIBRETIME_REQUIRED_API_KEY": "changed"}):
config = FixtureWithRequiredSubmodelConfig(filepath=config_filepath) config = FixtureWithRequiredSubmodelConfig(config_filepath)
assert config.required.api_key == "changed" assert config.required.api_key == "changed"
assert config.required.with_default == "original" assert config.required.with_default == "original"
@ -135,14 +135,29 @@ def test_base_config_required_submodel(tmp_path: Path):
"LIBRETIME_REQUIRED_WITH_DEFAULT": "changed", "LIBRETIME_REQUIRED_WITH_DEFAULT": "changed",
}, },
): ):
config = FixtureWithRequiredSubmodelConfig(filepath=config_filepath) config = FixtureWithRequiredSubmodelConfig(config_filepath)
assert config.required.api_key == "changed" assert config.required.api_key == "changed"
assert config.required.with_default == "changed" assert config.required.with_default == "changed"
# Raise validation error # Raise validation error
with mock.patch.dict(environ, {}): with mock.patch.dict(environ, {}):
with raises(SystemExit): with raises(SystemExit):
FixtureWithRequiredSubmodelConfig(filepath=None) FixtureWithRequiredSubmodelConfig(None)
def test_base_config_from_init() -> None:
class FromInitFixtureConfig(BaseConfig):
found: str
override: str
with mock.patch.dict(environ, {"LIBRETIME_OVERRIDE": "changed"}):
config = FromInitFixtureConfig(
found="changed",
override="invalid",
)
assert config.found == "changed"
assert config.override == "changed"
FIXTURE_CONFIG_RAW_MISSING = """ FIXTURE_CONFIG_RAW_MISSING = """
@ -169,4 +184,4 @@ def test_load_config_error(tmp_path: Path, raw, exception):
with raises(exception): with raises(exception):
with mock.patch.dict(environ, {}): with mock.patch.dict(environ, {}):
FixtureConfig(filepath=config_filepath) FixtureConfig(config_filepath)

View File

@ -11,7 +11,7 @@ class Config(BaseConfig):
LIBRETIME_CONFIG_FILEPATH = getenv("LIBRETIME_CONFIG_FILEPATH") LIBRETIME_CONFIG_FILEPATH = getenv("LIBRETIME_CONFIG_FILEPATH")
config = Config(filepath=LIBRETIME_CONFIG_FILEPATH) config = Config(LIBRETIME_CONFIG_FILEPATH)
# Celery amqp settings # Celery amqp settings
BROKER_URL = config.rabbitmq.url BROKER_URL = config.rabbitmq.url