Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/pydantic2linkml/gen_linkml.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
75 changes: 51 additions & 24 deletions tests/test_gen_linkml.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
# `'<ANY>'` 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.",
Expand All @@ -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):
Expand Down
Loading