diff --git a/src/blueapi/utils/base_model.py b/src/blueapi/utils/base_model.py index 03d8b1de3..73d8a7742 100644 --- a/src/blueapi/utils/base_model.py +++ b/src/blueapi/utils/base_model.py @@ -14,6 +14,7 @@ class BlueapiModelConfig(BaseConfig): alias_generator = _to_camel extra = Extra.forbid + allow_population_by_field_name = True class BlueapiPlanModelConfig(BlueapiModelConfig): diff --git a/src/blueapi/utils/serialization.py b/src/blueapi/utils/serialization.py index 141d0b702..bf1774013 100644 --- a/src/blueapi/utils/serialization.py +++ b/src/blueapi/utils/serialization.py @@ -17,7 +17,9 @@ def serialize(obj: Any) -> Any: """ if isinstance(obj, BaseModel): - return obj.dict() + # Serialize by alias so that our camelCase models leave the service + # with camelCase field names + return obj.dict(by_alias=True) elif hasattr(obj, "__pydantic_model__"): return serialize(getattr(obj, "__pydantic_model__")) else: diff --git a/tests/utils/test_base_model.py b/tests/utils/test_base_model.py new file mode 100644 index 000000000..24171dd23 --- /dev/null +++ b/tests/utils/test_base_model.py @@ -0,0 +1,25 @@ +from blueapi.utils import BlueapiBaseModel + + +class FooBar(BlueapiBaseModel): + hello: str = "hello" + hello_world: str = "hello world" + + +def test_snake_case_constructor() -> None: + FooBar( + hello="hello", + hello_world="hello world", + ) + + +def test_camel_case_parsing() -> None: + assert FooBar.parse_obj( + { + "hello": "hello", + "helloWorld": "hello world", + } + ) == FooBar( + hello="hello", + hello_world="hello world", + )