diff --git a/src/ome_types/model/_shape_union.py b/src/ome_types/model/_shape_union.py index 289ff442..550c345a 100644 --- a/src/ome_types/model/_shape_union.py +++ b/src/ome_types/model/_shape_union.py @@ -55,8 +55,9 @@ class ShapeUnion(OMEType, RootModel, UserSequence[ShapeType]): # type: ignore[m default_factory=list, json_schema_extra={ "type": "Elements", - "choices": tuple( # type: ignore[dict-item] - {"name": kind.title(), "type": cls} for kind, cls in _KINDS.items() + "choices": tuple( + (("name", kind.title()), ("type", cls)) + for kind, cls in _KINDS.items() ), }, ) @@ -120,7 +121,8 @@ class ShapeUnion(OMEType, UserSequence[ShapeType]): # type: ignore metadata={ # type: ignore[call-arg] "type": "Elements", "choices": tuple( - {"name": kind.title(), "type": cls} for kind, cls in _KINDS.items() + (("name", kind.title()), ("type", cls)) + for kind, cls in _KINDS.items() ), }, ) diff --git a/src/ome_types/model/_structured_annotations.py b/src/ome_types/model/_structured_annotations.py index ca6f056a..92ecacdc 100644 --- a/src/ome_types/model/_structured_annotations.py +++ b/src/ome_types/model/_structured_annotations.py @@ -64,8 +64,8 @@ class StructuredAnnotationList(OMEType, RootModel, UserSequence[Annotation]): # default_factory=list, json_schema_extra={ "type": "Elements", - "choices": tuple( # type: ignore[dict-item] - {"name": cls.__name__, "type": cls} for cls in AnnotationTypes + "choices": tuple( + (("name", cls.__name__), ("type", cls)) for cls in AnnotationTypes ), }, ) @@ -127,7 +127,7 @@ class StructuredAnnotationList(OMEType, UserSequence[Annotation]): # type: igno metadata={ # type: ignore[call-arg] "type": "Elements", "choices": tuple( - {"name": cls.__name__, "type": cls} for cls in AnnotationTypes + (("name", cls.__name__), ("type", cls)) for cls in AnnotationTypes ), }, ) diff --git a/src/xsdata_pydantic_basemodel/pydantic_compat.py b/src/xsdata_pydantic_basemodel/pydantic_compat.py index 7bf4df3e..94e242d0 100644 --- a/src/xsdata_pydantic_basemodel/pydantic_compat.py +++ b/src/xsdata_pydantic_basemodel/pydantic_compat.py @@ -54,6 +54,19 @@ def _pydantic_field_to_dataclass_field(name: str, pydantic_field: FieldInfo) -> metadata = _get_metadata(pydantic_field) + # HACK + # see https://github.com/tlambert03/ome-types/pull/235 for description of problem + # This is a hack to get around the fact that xsdata requires Element choices + # to be added to the Field metadata as `choices: List[dict]` ... but pydantic + # requires that everything in a Field be hashable (if you want to cast the model + # to a JSON schema), and `dict` is not hashable. So here, when we're converting + # a pydantic Field to a dataclass Field for xsdata to consume, we cast all items + # in the `choices` list to `dict` (which is hashable). + # Then, in our source code, we declare choices as tuple[tuple[str, str], ...] + # which IS hashable. + if "choices" in metadata: + metadata["choices"] = [dict(choice) for choice in metadata["choices"]] + dataclass_field = field( # type: ignore default=default, default_factory=default_factory, metadata=metadata )