Skip to content

Commit 9e97f17

Browse files
authored
Merge pull request #34 from modern-python/build-config-from-dict-and-object
allow building configs from dict and object
2 parents 508d62a + 1a2121f commit 9e97f17

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

lite_bootstrap/instruments/base.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import abc
22
import dataclasses
3+
import typing
4+
5+
import typing_extensions
36

47

58
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
@@ -10,6 +13,21 @@ class BaseConfig:
1013
service_environment: str | None = None
1114
service_debug: bool = True
1215

16+
@classmethod
17+
def from_dict(cls, data: dict[str, typing.Any]) -> typing_extensions.Self:
18+
field_names = {f.name for f in dataclasses.fields(cls)}
19+
return cls(**{k: v for k, v in data.items() if k in field_names})
20+
21+
@classmethod
22+
def from_object(cls, obj: object) -> typing_extensions.Self:
23+
prepared_data = {}
24+
field_names = {f.name for f in dataclasses.fields(cls)}
25+
26+
for field in field_names:
27+
if (value := getattr(obj, field, None)) is not None:
28+
prepared_data[field] = value
29+
return cls(**prepared_data)
30+
1331

1432
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
1533
class BaseInstrument(abc.ABC):

tests/test_config.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import dataclasses
2+
3+
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
4+
5+
from lite_bootstrap import FastAPIConfig
6+
from lite_bootstrap.instruments.base import BaseConfig
7+
from tests.conftest import CustomInstrumentor
8+
9+
10+
def test_config_from_dict() -> None:
11+
raw_config = {
12+
"service_name": "microservice",
13+
"service_version": "2.0.0",
14+
"service_environment": "test",
15+
"service_debug": False,
16+
"cors_allowed_origins": ["http://test"],
17+
"health_checks_path": "/custom-health/",
18+
"logging_buffer_capacity": 0,
19+
"opentelemetry_endpoint": "otl",
20+
"opentelemetry_instrumentors": [CustomInstrumentor()],
21+
"opentelemetry_span_exporter": ConsoleSpanExporter(),
22+
"prometheus_metrics_path": "/custom-metrics/",
23+
"sentry_dsn": "https://testdsn@localhost/1",
24+
"swagger_offline_docs": True,
25+
"extra_key": "extra_value",
26+
}
27+
config = FastAPIConfig.from_dict(raw_config)
28+
29+
for field in dataclasses.fields(FastAPIConfig):
30+
if field.name in raw_config:
31+
assert getattr(config, field.name) == raw_config[field.name]
32+
33+
34+
def test_config_from_object() -> None:
35+
big_config = FastAPIConfig(
36+
service_name="microservice",
37+
service_version="2.0.0",
38+
service_environment="test",
39+
service_debug=False,
40+
cors_allowed_origins=["http://test"],
41+
health_checks_path="/custom-health/",
42+
logging_buffer_capacity=0,
43+
opentelemetry_endpoint="otl",
44+
opentelemetry_instrumentors=[CustomInstrumentor()],
45+
opentelemetry_span_exporter=ConsoleSpanExporter(),
46+
prometheus_metrics_path="/custom-metrics/",
47+
sentry_dsn="https://testdsn@localhost/1",
48+
swagger_offline_docs=True,
49+
)
50+
51+
short_config = BaseConfig.from_object(big_config)
52+
for field in dataclasses.fields(BaseConfig):
53+
assert getattr(short_config, field.name) == getattr(big_config, field.name)

0 commit comments

Comments
 (0)