Skip to content

Commit 57e2cc6

Browse files
committed
fix(types): derive generated account union arms
1 parent 65dccc0 commit 57e2cc6

2 files changed

Lines changed: 58 additions & 5 deletions

File tree

scripts/post_generate_fixes.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,14 +1545,58 @@ def expose_account_reference_union_fields() -> None:
15451545
request, nested-input, response, and canonical-clone paths all expose the
15461546
same concrete arm types without import-time Pydantic patching.
15471547
"""
1548-
pattern = re.compile(r"\b(account_ref(?:_\d+)?)\.AccountReference\b(?![12])")
1548+
account_ref_source = OUTPUT_DIR / "core" / "account_ref.py"
1549+
if not account_ref_source.exists():
1550+
print(" account reference model not found (skipping union-field fix)")
1551+
return
1552+
1553+
tree = ast.parse(account_ref_source.read_text())
1554+
wrapper = next(
1555+
(
1556+
node
1557+
for node in tree.body
1558+
if isinstance(node, ast.ClassDef) and node.name == "AccountReference"
1559+
),
1560+
None,
1561+
)
1562+
if wrapper is None:
1563+
raise RuntimeError("generated account_ref.py has no AccountReference wrapper")
1564+
1565+
root_base = next(
1566+
(
1567+
base
1568+
for base in wrapper.bases
1569+
if isinstance(base, ast.Subscript)
1570+
and isinstance(base.value, ast.Name)
1571+
and base.value.id == "RootModel"
1572+
),
1573+
None,
1574+
)
1575+
if root_base is None:
1576+
raise RuntimeError("generated AccountReference has no RootModel union base")
1577+
1578+
def union_arm_names(node: ast.expr) -> list[str]:
1579+
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
1580+
return [*union_arm_names(node.left), *union_arm_names(node.right)]
1581+
if isinstance(node, ast.Name):
1582+
return [node.id]
1583+
raise RuntimeError(
1584+
"generated AccountReference has an unsupported union expression: "
1585+
f"{ast.unparse(node)}"
1586+
)
1587+
1588+
arm_names = union_arm_names(root_base.slice)
1589+
if len(arm_names) < 2 or len(set(arm_names)) != len(arm_names):
1590+
raise RuntimeError(f"generated AccountReference has invalid union arms: {arm_names!r}")
1591+
1592+
pattern = re.compile(r"\b(account_ref(?:_\d+)?)\.AccountReference\b(?!\d)")
15491593
total_files = 0
15501594
total_fields = 0
15511595

15521596
for py_file in sorted(OUTPUT_DIR.rglob("*.py")):
15531597
source = py_file.read_text()
15541598
fixed, replacements = pattern.subn(
1555-
r"\1.AccountReference1 | \1.AccountReference2",
1599+
lambda match: " | ".join(f"{match.group(1)}.{arm_name}" for arm_name in arm_names),
15561600
source,
15571601
)
15581602
if not replacements:

tests/test_code_generation.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,15 @@ def test_post_generate_exposes_account_reference_union_fields_idempotently(tmp_p
378378
from scripts import post_generate_fixes
379379

380380
generated_dir = tmp_path / "generated_poc"
381+
account_ref = generated_dir / "core" / "account_ref.py"
382+
account_ref.parent.mkdir(parents=True)
383+
account_ref.write_text(
384+
"from pydantic import RootModel\n\n"
385+
"class AccountReference(RootModel["
386+
"AccountReference1 | AccountReference2 | AccountReference3]):\n"
387+
" pass\n"
388+
)
381389
target = generated_dir / "sample_request.py"
382-
target.parent.mkdir(parents=True)
383390
target.write_text(
384391
"account: account_ref.AccountReference | None\n"
385392
"accounts: list[account_ref_1.AccountReference]\n"
@@ -390,8 +397,10 @@ def test_post_generate_exposes_account_reference_union_fields_idempotently(tmp_p
390397
post_generate_fixes.expose_account_reference_union_fields()
391398

392399
assert target.read_text() == (
393-
"account: account_ref.AccountReference1 | account_ref.AccountReference2 | None\n"
394-
"accounts: list[account_ref_1.AccountReference1 | account_ref_1.AccountReference2]\n"
400+
"account: account_ref.AccountReference1 | account_ref.AccountReference2 | "
401+
"account_ref.AccountReference3 | None\n"
402+
"accounts: list[account_ref_1.AccountReference1 | account_ref_1.AccountReference2 | "
403+
"account_ref_1.AccountReference3]\n"
395404
)
396405

397406

0 commit comments

Comments
 (0)