From d02ca3a7be5471825abd71c4993c38871ce652bb Mon Sep 17 00:00:00 2001 From: "Keto D. Zhang" Date: Tue, 19 Nov 2024 18:56:51 -0800 Subject: [PATCH 1/4] refactor: BaseModel.__field__ -> BaseModel.model_fields --- asdf_pydantic/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asdf_pydantic/model.py b/asdf_pydantic/model.py index 2d38157..6e5ba14 100644 --- a/asdf_pydantic/model.py +++ b/asdf_pydantic/model.py @@ -24,7 +24,7 @@ class AsdfPydanticModel(BaseModel): def asdf_yaml_tree(self) -> dict: d = {} for field_key, v in self.__dict__.items(): - if field_key not in self.__fields__: + if field_key not in self.model_fields: continue if isinstance(v, AsdfPydanticModel): From e8d649f4cb94d42d00ebc9513f63bae833a24605 Mon Sep 17 00:00:00 2001 From: "Keto D. Zhang" Date: Tue, 19 Nov 2024 19:18:11 -0800 Subject: [PATCH 2/4] refactor: rewrite asdf_yaml_tree using `dict(BaseModel)` over deprecated `BaseModel._get_value` --- asdf_pydantic/model.py | 45 +++++++++++++++++++++++++----------------- tests/test_model.py | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/asdf_pydantic/model.py b/asdf_pydantic/model.py index 6e5ba14..70e4ccc 100644 --- a/asdf_pydantic/model.py +++ b/asdf_pydantic/model.py @@ -22,26 +22,35 @@ class AsdfPydanticModel(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) def asdf_yaml_tree(self) -> dict: - d = {} - for field_key, v in self.__dict__.items(): - if field_key not in self.model_fields: - continue - + """Converts the model to an ASDF-compatible YAML tree (dict). + + .. note:: + Any fields that are normal Pydantic `BaseModel` will be converted to + dict. See conversion table. + + Conversion Table: + + +-------------------------+-----------------------------+ + | Value type in field | Value type in dict | + +=========================+=============================+ + | AsdfPydanticModel | No conversion | + +-------------------------+-----------------------------+ + | BaseModel | Converted to dict using | + | | BaseModel.model_dump() | + +-------------------------+-----------------------------+ + | Other types | No conversion | + +-------------------------+-----------------------------+ + """ + tree = {} + for k, v in dict(self).items(): if isinstance(v, AsdfPydanticModel): - d[field_key] = v + tree[k] = v + elif isinstance(v, BaseModel): + tree[k] = v.model_dump() else: - d[field_key] = self._get_value( - v, - to_dict=True, - by_alias=False, - include=None, - exclude=None, - exclude_unset=False, - exclude_defaults=False, - exclude_none=False, - ) - - return d + tree[k] = v + + return tree @model_validator(mode="before") @classmethod diff --git a/tests/test_model.py b/tests/test_model.py index 9f78227..00f1f3d 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -1,10 +1,52 @@ import pytest import yaml from asdf.extension import TagDefinition +from pydantic import BaseModel from asdf_pydantic import AsdfPydanticModel +######################################################################################## +# ASDF Tree +######################################################################################## +def test_asdf_yaml_tree_sanity(): + class TestModel(AsdfPydanticModel): + foo: str + + model = TestModel(foo="bar") + assert model.asdf_yaml_tree() == {"foo": "bar"} + + +def test_asdf_yaml_tree_nested_dict(): + class NestedTestModel(AsdfPydanticModel): + nested: dict + + model = NestedTestModel(nested={"foo": "bar"}) + assert model.asdf_yaml_tree() == {"nested": {"foo": "bar"}} + + +def test_asdf_yaml_tree_nested_basemodel(): + class TestModel(BaseModel): + foo: str + + class NestedTestModel(AsdfPydanticModel): + nested: TestModel + + model = NestedTestModel(nested=TestModel(foo="bar")) + assert model.asdf_yaml_tree() == {"nested": {"foo": "bar"}} + + +def test_asdf_yaml_tree_nested_asdf_pydantic_model(): + class TestModel(AsdfPydanticModel): + foo: str + + class NestedTestModel(AsdfPydanticModel): + nested: TestModel + + model = NestedTestModel(nested=TestModel(foo="bar")) + assert model.asdf_yaml_tree() == {"nested": TestModel(foo="bar")} + + ######################################################################################## # TAGS ######################################################################################## From f657af0923d9e1def0d6cc86647c14e2099d405f Mon Sep 17 00:00:00 2001 From: "Keto D. Zhang" Date: Tue, 19 Nov 2024 18:55:12 -0800 Subject: [PATCH 3/4] chore: ignore uv.lock --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 6a73c32..311267d 100644 --- a/.gitignore +++ b/.gitignore @@ -135,3 +135,6 @@ dmypy.json # Pyre type checker .pyre/ + +# uv +uv.lock From fb7b73e0f97a4044bcd775741bce38035baefa66 Mon Sep 17 00:00:00 2001 From: "Keto D. Zhang" Date: Tue, 19 Nov 2024 19:20:32 -0800 Subject: [PATCH 4/4] refactor: replace deprecated `BaseModel.parse_obj` with `BaseModel.model_validate` --- asdf_pydantic/converter.py | 2 +- asdf_pydantic/model.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/asdf_pydantic/converter.py b/asdf_pydantic/converter.py index 4be2684..d81383f 100644 --- a/asdf_pydantic/converter.py +++ b/asdf_pydantic/converter.py @@ -40,4 +40,4 @@ def to_yaml_tree(self, obj: AsdfPydanticModel, tag, ctx): return obj.asdf_yaml_tree() def from_yaml_tree(self, node, tag, ctx): - return self._tag_to_class[tag].parse_obj(node) + return self._tag_to_class[tag].model_validate(node) diff --git a/asdf_pydantic/model.py b/asdf_pydantic/model.py index 70e4ccc..20e2918 100644 --- a/asdf_pydantic/model.py +++ b/asdf_pydantic/model.py @@ -15,7 +15,7 @@ class AsdfPydanticModel(BaseModel): ASDF Serialization and Deserialization: Serialize to ASDF yaml tree is done with the py:classmethod`AsdfPydanticModel.asdf_yaml_tree()` and deserialize to an - AsdfPydanticModel object with py:meth`AsdfPydanticModel.parse_obj()`. + AsdfPydanticModel object with py:meth`AsdfPydanticModel.model_validate()`. """ _tag: ClassVar[str | TagDefinition]