Skip to content

Add model_rebuild for models with typing imports when using deferred annotations#2725

Closed
koxudaxi wants to merge 2 commits intomainfrom
fix-model-rebuild-for-typing-imports
Closed

Add model_rebuild for models with typing imports when using deferred annotations#2725
koxudaxi wants to merge 2 commits intomainfrom
fix-model-rebuild-for-typing-imports

Conversation

@koxudaxi
Copy link
Copy Markdown
Owner

@koxudaxi koxudaxi commented Dec 21, 2025

Fixes: #2341

Summary by CodeRabbit

  • Improvements

    • Improved handling of typing imports so generated Pydantic v2 models correctly rebuild and resolve forward/deferred references.
  • New Output

    • Generated examples now include models with explicit post-definition rebuild calls to ensure proper runtime schemas.
  • Tests

    • Added tests validating typing-import-driven rebuild behavior with OpenAPI inputs.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 21, 2025

Walkthrough

Adds a typing-import rebuild heuristic: introduces a TYPING_IMPORTS_REQUIRING_REBUILD constant, extends exported typing IMPORT_* symbols, and updates resolve-reference logic to compute and merge typing-related import paths so modules with deferred annotations trigger model_rebuild when needed.

Changes

Cohort / File(s) Summary
Parser core
src/datamodel_code_generator/parser/base.py
Adds TYPING_IMPORTS_REQUIRING_REBUILD tuple of typing-related IMPORT_* constants; exposes additional IMPORT_* symbols publicly; updates resolve-reference logic to compute typing_import_paths, merge them into required_paths_in_module, and refine forward-needed + inheritance propagation so typing imports trigger module rebuilds.
OpenAPI input & expected output
tests/data/openapi/typing_imports_rebuild.yaml, tests/data/expected/main/openapi/typing_imports_rebuild.py
Adds an OpenAPI spec with Item and Container schemas and the expected generated Pydantic models file that includes Optional/list typing and explicit model_rebuild() calls for the models.
Tests
tests/main/openapi/test_main_openapi.py
Adds test_main_openapi_typing_imports_rebuild validating that generated code includes model_rebuild when deferred annotations and typing imports are present (pydantic_v2.BaseModel, --no-use-union-operator).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect typing_import_paths computation for completeness and false positives/negatives.
  • Verify merge order and final union (required_filtered ∪ typing_import_paths) preserves prior propagation behavior.
  • Check exported IMPORT_* surface changes for API compatibility.
  • Run and validate the new test and generated sample to ensure model_rebuild() is emitted correctly for relevant models.

Possibly related PRs

Suggested labels

safe-to-fix

Suggested reviewers

  • ilovelinux

Poem

"I’m a rabbit with a tiny quill,
I hop through types and mend the sill.
When Optional hides in forward light,
I nudge a rebuild—set things right.
Hooray! No more undefined at night." 🐇

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding model_rebuild() calls for models with typing imports when using deferred annotations.
Linked Issues check ✅ Passed The PR addresses issue #2341 by ensuring typing imports are included in generated models, preventing PydanticUndefinedAnnotation errors during annotation evaluation.
Out of Scope Changes check ✅ Passed All changes directly support the fix: new typing imports constant, resolve-reference logic modifications, test file additions, and expected output files are all in-scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-model-rebuild-for-typing-imports

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…-typing-imports

# Conflicts:
#	tests/main/openapi/test_main_openapi.py
@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 21, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.35%. Comparing base (8a10962) to head (12d4080).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2725   +/-   ##
=======================================
  Coverage   99.35%   99.35%           
=======================================
  Files          83       83           
  Lines       11868    11874    +6     
  Branches     1433     1434    +1     
=======================================
+ Hits        11792    11798    +6     
  Misses         45       45           
  Partials       31       31           
Flag Coverage Δ
unittests 99.35% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/datamodel_code_generator/parser/base.py (2)

43-57: Typing import constants and TYPING_IMPORTS_REQUIRING_REBUILD grouping look consistent

Grouping IMPORT_DICT, IMPORT_FROZEN_SET, IMPORT_LIST, IMPORT_MAPPING, IMPORT_OPTIONAL, IMPORT_SEQUENCE, IMPORT_SET, IMPORT_TUPLE, and IMPORT_UNION into TYPING_IMPORTS_REQUIRING_REBUILD matches how DataType.imports yields typing/container imports, so the later membership checks over field imports will behave as intended. If this list ever diverges from DataType.imports, a short comment referencing that coupling here could help future maintainers keep them in sync, but the current setup looks correct and self-contained.

Also applies to: 109-119


2398-2441: Deferred-annotation rebuild now correctly considers typing imports; consider seeding inheritance with them

The extended block in __get_resolve_reference_action_parts:

  • Restricts the new behavior to deferred-annotation scenarios using the v2 resolver.
  • Detects typing_import_paths by scanning each model’s field imports for TYPING_IMPORTS_REQUIRING_REBUILD.
  • Unions these paths into required_paths_in_module, then computes forward_needed based on intra-module references and propagates along base-class relationships.
  • Ensures all models with relevant typing imports end up in required_paths_in_module via the final required_filtered | typing_import_paths union.

This is a good fit for the PR’s goal: modules whose fields depend on typing-based imports under deferred annotations will now also emit model_rebuild() calls.

One optional hardening you might consider: if you want subclasses of typing-heavy models to always participate in the inheritance propagation, you could seed required_filtered with typing_import_paths as well, not just with forward_needed. That would look like:

Suggested tweak to seed inheritance propagation with typing imports
-            changed = True
-            required_filtered = set(forward_needed)
+            changed = True
+            # Seed with forward-needed models and those with typing imports,
+            # so subclasses of typing-heavy bases are also pulled in.
+            required_filtered = set(forward_needed) | typing_import_paths
@@
-                required_paths_in_module = required_filtered | typing_import_paths
+                required_paths_in_module = required_filtered

This change is conservative (it can only increase the set of models that get model_rebuild() calls) and may further reduce the risk of subtle inheritance edge cases, but the current logic is already a clear improvement and should address the reported regression.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 535775d and 12d4080.

📒 Files selected for processing (2)
  • src/datamodel_code_generator/parser/base.py (3 hunks)
  • tests/main/openapi/test_main_openapi.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/datamodel_code_generator/parser/base.py (4)
src/datamodel_code_generator/imports.py (1)
  • Import (20-38)
src/datamodel_code_generator/model/base.py (3)
  • path (716-718)
  • imports (281-300)
  • imports (635-640)
src/datamodel_code_generator/types.py (1)
  • imports (450-495)
src/datamodel_code_generator/model/typed_dict.py (1)
  • imports (150-155)
tests/main/openapi/test_main_openapi.py (2)
tests/main/conftest.py (2)
  • output_file (98-100)
  • run_main_and_assert (244-408)
tests/test_main_kr.py (1)
  • output_file (44-46)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: py312-isort5 on Ubuntu
  • GitHub Check: py312-black23 on Ubuntu
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.12 on Ubuntu
  • GitHub Check: py312-isort7 on Ubuntu
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.10 on Ubuntu
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.13 on Windows
  • GitHub Check: benchmarks
  • GitHub Check: Analyze (python)
🔇 Additional comments (1)
tests/main/openapi/test_main_openapi.py (1)

4400-4409: New OpenAPI regression test is well-wired and aligned with the fix

The test_main_openapi_typing_imports_rebuild test follows the existing run_main_and_assert pattern, uses the new typing_imports_rebuild.yaml/.py fixtures, and targets pydantic_v2.BaseModel with --no-use-union-operator, which is exactly the scenario this PR is addressing. No changes needed here.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Dec 21, 2025

CodSpeed Performance Report

Merging #2725 will not alter performance

Comparing fix-model-rebuild-for-typing-imports (12d4080) with main (8a10962)

Summary

✅ 70 untouched
⏩ 10 skipped1

Footnotes

  1. 10 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@koxudaxi koxudaxi closed this Dec 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pydantic Undefined Annotation: name 'Optional' is not defined

1 participant