Skip to content

Commit c71c2fd

Browse files
committed
Be stricter about converting rootmodel to dict
`model_to_dict` was recursively converting RootModels, which could have led to confusing behaviour. We only need to handle the case of empty input represented by a RootModel, and the code now raises an error if a RootModel is used otherwise.
1 parent 3625f28 commit c71c2fd

File tree

1 file changed

+17
-2
lines changed

1 file changed

+17
-2
lines changed

src/labthings_fastapi/utilities/__init__.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pydantic import BaseModel, ConfigDict, Field, RootModel, create_model
55
from pydantic.dataclasses import dataclass
66
from anyio.from_thread import BlockingPortal
7+
from labthings_fastapi.utilities.introspection import EmptyObject
78

89
if TYPE_CHECKING:
910
from ..thing import Thing
@@ -63,9 +64,23 @@ def wrap_plain_types_in_rootmodel(model: type) -> type[BaseModel]:
6364

6465

6566
def model_to_dict(model: Optional[BaseModel]) -> Dict[str, Any]:
66-
"""Convert a pydantic model to a dictionary"""
67+
"""Convert a pydantic model to a dictionary
68+
69+
We convert only the top level model, i.e. we do not recurse into submodels.
70+
This is important to avoid serialising Blob objects in action inputs.
71+
This function returns `dict(model)`, with exceptions for the case of `None`
72+
(converted to an empty dictionary) and `RootModel`s (checked to see if they
73+
correspond to empty input).
74+
75+
If RootModels with non-empty input are allowed, this function will need to
76+
be updated to handle them.
77+
"""
6778
if model is None:
6879
return {}
6980
if isinstance(model, RootModel):
70-
return model_to_dict(model.root)
81+
if model.root is None:
82+
return {}
83+
if isinstance(model.root, EmptyObject):
84+
return {}
85+
raise ValueError("RootModels with non-empty input are not supported")
7186
return dict(model)

0 commit comments

Comments
 (0)