Skip to content

Commit 531621b

Browse files
committed
fix(codegen): expand union member trees before collecting types
extract_model() on union members produced ModelSpecs with model=None on MODEL-kind fields. _collect_from_fields then hit the RuntimeError guard when it encountered those unexpanded references. Call expand_model_tree() on each member before walking its fields. No current union members have sub-model fields, so this was latent.
1 parent 3f4bb72 commit 531621b

3 files changed

Lines changed: 38 additions & 1 deletion

File tree

packages/overture-schema-codegen/src/overture/schema/codegen/type_collection.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Annotated, get_args, get_origin
99

1010
from .enum_extraction import extract_enum
11-
from .model_extraction import extract_model
11+
from .model_extraction import expand_model_tree, extract_model
1212
from .newtype_extraction import extract_newtype
1313
from .pydantic_extraction import extract_pydantic_type
1414
from .specs import (
@@ -93,6 +93,7 @@ def _visit(node: TypeInfo) -> None:
9393
# by the feature_objs guard in _collect_from_model.
9494
for member_cls in node.union_members:
9595
member_spec = extract_model(member_cls)
96+
expand_model_tree(member_spec)
9697
_collect_from_model(member_spec)
9798

9899
if node.kind == TypeKind.ENUM and node.source_type is not None:

packages/overture-schema-codegen/tests/codegen_test_support.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,26 @@ class WaterSegment(SegmentBase):
237237
]
238238

239239

240+
class ContactInfo(BaseModel):
241+
"""Contact information for a venue."""
242+
243+
email: str = Field(description="Email address")
244+
phone: str | None = Field(None, description="Phone number")
245+
246+
247+
class VenueWithContact(SegmentBase):
248+
"""A segment variant with a nested sub-model field."""
249+
250+
subtype: Literal["venue"]
251+
contact: ContactInfo
252+
253+
254+
TestSegmentWithSubModel = Annotated[
255+
RoadSegment | VenueWithContact,
256+
Field(description="Test segment union with sub-model member"),
257+
]
258+
259+
240260
def make_union_spec(
241261
name: str = "TestUnion",
242262
*,

packages/overture-schema-codegen/tests/test_type_collection.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
FeatureWithSources,
66
FeatureWithUrl,
77
Instrument,
8+
TestSegmentWithSubModel,
89
has_name,
910
lookup_by_name,
1011
)
@@ -85,6 +86,21 @@ def test_same_name_different_types_both_collected(self) -> None:
8586
assert len(address_entries) == 2
8687

8788

89+
class TestCollectUnionMemberSubModels:
90+
"""Tests for union members with nested sub-model fields."""
91+
92+
def test_union_member_with_sub_model_collects_sub_model(self) -> None:
93+
"""Sub-models inside union members are collected without RuntimeError."""
94+
95+
class FeatureWithUnionSubModel(BaseModel):
96+
segment: TestSegmentWithSubModel
97+
98+
result = _expanded_supplementary(FeatureWithUnionSubModel)
99+
100+
assert has_name(result, "ContactInfo")
101+
assert isinstance(lookup_by_name(result, "ContactInfo"), ModelSpec)
102+
103+
88104
class TestCollectPydanticTypes:
89105
"""Tests for Pydantic built-in type collection."""
90106

0 commit comments

Comments
 (0)