Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions lite_bootstrap/instruments/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,15 @@ class BaseConfig:

@classmethod
def from_dict(cls, data: dict[str, typing.Any]) -> typing_extensions.Self:
"""Build a config from a dict; unknown keys are silently dropped, explicit None overrides defaults."""
field_names = {f.name for f in dataclasses.fields(cls)}
return cls(**{k: v for k, v in data.items() if k in field_names})

@classmethod
def from_object(cls, obj: object) -> typing_extensions.Self:
prepared_data = {}
"""Build a config by merging non-None attributes from obj; None or missing attributes fall back to defaults."""
field_names = {f.name for f in dataclasses.fields(cls)}

for field in field_names:
if (value := getattr(obj, field, None)) is not None:
prepared_data[field] = value
prepared_data = {field: value for field in field_names if (value := getattr(obj, field, None)) is not None}
return cls(**prepared_data)


Expand Down
43 changes: 43 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,46 @@ def test_config_from_object() -> None:
short_config = BaseConfig.from_object(big_config)
for field in dataclasses.fields(BaseConfig):
assert getattr(short_config, field.name) == getattr(big_config, field.name)


def test_from_object_skips_none_attribute() -> None:
@dataclasses.dataclass
class Source:
service_name: str | None = None
service_version: str = "2.0.0"

config = BaseConfig.from_object(Source())
assert config.service_name == "micro-service"
assert config.service_version == "2.0.0"


def test_from_object_skips_missing_attribute() -> None:
class Source:
pass

config = BaseConfig.from_object(Source())
assert config.service_name == "micro-service"
assert config.service_version == "1.0.0"
assert config.service_debug is True


def test_from_object_preserves_falsy_values() -> None:
@dataclasses.dataclass
class Source:
service_name: str = ""
service_debug: bool = False

config = BaseConfig.from_object(Source())
assert config.service_name == ""
assert config.service_debug is False


def test_from_dict_drops_unknown_keys_silently() -> None:
config = BaseConfig.from_dict({"service_name": "test", "unknown_key": "value"})
assert config.service_name == "test"
assert config.service_version == "1.0.0"


def test_from_dict_explicit_none_overrides_default() -> None:
config = BaseConfig.from_dict({"service_name": None})
assert config.service_name is None
Loading