diff --git a/src/pydantic2linkml/gen_linkml.py b/src/pydantic2linkml/gen_linkml.py index 63557aae..8cde23f6 100644 --- a/src/pydantic2linkml/gen_linkml.py +++ b/src/pydantic2linkml/gen_linkml.py @@ -505,8 +505,6 @@ def _get_ase(self, subschema: core_schema.CoreSchema) -> AnonymousSlotExpression } return AnonymousSlotExpression(**ase_kwargs) - - if pydantic_version >= version.parse("2.10"): def _invalid_schema(self, schema: core_schema.InvalidSchema) -> None: diff --git a/tests/test_gen_linkml.py b/tests/test_gen_linkml.py index 308f1200..1f8dc79d 100644 --- a/tests/test_gen_linkml.py +++ b/tests/test_gen_linkml.py @@ -895,7 +895,12 @@ class Foo0(BaseModel): ] slot = SlotGenerator(field_schema).generate() - assert slot.range is None + # `notes=slot.notes` is used instead of `notes=ANY` because + # `SlotDefinition.__eq__` serializes fields before comparing, which + # causes `unittest.mock.ANY` to be treated as the literal string + # `''` rather than a wildcard. The note content is checked + # separately below. + assert slot == SlotDefinition(name="x", required=True, notes=slot.notes) assert in_exactly_one_string( "The union core schema contains a tuple as a choice. " "Tuples as choices are yet to be supported.", @@ -906,40 +911,62 @@ class Foo0(BaseModel): class Foo1(BaseModel): x: Union[int, Bar1, str] - slot = translate_field_to_slot(Foo1, "x") - - assert slot.range == ANY_CLASS_DEF.name - assert slot.any_of == [ - AnonymousSlotExpression(range="integer"), - AnonymousSlotExpression(range="Bar1"), - AnonymousSlotExpression(range="string"), - ] + assert translate_field_to_slot(Foo1, "x") == SlotDefinition( + name="x", + range=ANY_CLASS_DEF.name, + required=True, + any_of=[ + AnonymousSlotExpression(range="integer"), + AnonymousSlotExpression(range="Bar1"), + AnonymousSlotExpression(range="string"), + ], + ) # === Unions of two models === class Foo2(BaseModel): x: Union[Bar1, Bar2] - slot = translate_field_to_slot(Foo2, "x") - - assert slot.range == ANY_CLASS_DEF.name - assert slot.any_of == [ - AnonymousSlotExpression(range="Bar1"), - AnonymousSlotExpression(range="Bar2"), - ] + assert translate_field_to_slot(Foo2, "x") == SlotDefinition( + name="x", + range=ANY_CLASS_DEF.name, + required=True, + any_of=[ + AnonymousSlotExpression(range="Bar1"), + AnonymousSlotExpression(range="Bar2"), + ], + ) # === Union of base types, lists, and models === class Foo3(BaseModel): x: Union[int, list[Bar1], list[str], Bar2] - slot = translate_field_to_slot(Foo3, "x") + assert translate_field_to_slot(Foo3, "x") == SlotDefinition( + name="x", + range=ANY_CLASS_DEF.name, + required=True, + any_of=[ + AnonymousSlotExpression(range="integer"), + AnonymousSlotExpression(range="Bar1", multivalued=True), + AnonymousSlotExpression(range="string", multivalued=True), + AnonymousSlotExpression(range="Bar2"), + ], + ) - assert slot.range == ANY_CLASS_DEF.name - assert slot.any_of == [ - AnonymousSlotExpression(range="integer"), - AnonymousSlotExpression(range="Bar1", multivalued=True), - AnonymousSlotExpression(range="string", multivalued=True), - AnonymousSlotExpression(range="Bar2"), - ] + # === Nested unions === + class Foo4(BaseModel): + x: Union[int, Union[str, Bar1]] + + slot = translate_field_to_slot(Foo4, "x") + assert slot == SlotDefinition( + name="x", + range=ANY_CLASS_DEF.name, + required=True, + any_of=[ + AnonymousSlotExpression(range="integer"), + AnonymousSlotExpression(range="string"), + AnonymousSlotExpression(range="Bar1"), + ], + ) def test_tagged_union_schema(self): class Cat(BaseModel):