Fix indefinite hang on OpenAPI schemas with cyclic model dependencies#3078
Fix indefinite hang on OpenAPI schemas with cyclic model dependencies#3078kevin-paulson-mindbridge-ai wants to merge 20 commits intokoxudaxi:mainfrom
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughPrevented infinite/oscillating ordering in model sorting for circular base-class dependencies and added a guard to skip discriminator base-class slots that lack a Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Merging this PR will not alter performance
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/parser/test_base.py (1)
270-303: Consider asserting a postcondition, not just “no exception.”This catches the crash, but it would be a bit stronger if it also pinned one observable outcome after
__apply_discriminator_type()runs, so a future silent no-op doesn’t still pass.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/parser/test_base.py` around lines 270 - 303, The test currently only ensures no exception is raised; add a concrete postcondition after calling parser._Parser__apply_discriminator_type to ensure the union was not silently modified by the discriminator logic: e.g., assert that union_inner still contains exactly the two members referencing ref_pet and ref_other (check identities of union_inner.data_types entries or their .reference attributes) and that pet_model.base_classes remains unchanged — locate this in test_apply_discriminator_type_skips_base_class_without_reference and add the assertion(s) after the call to parser._Parser__apply_discriminator_type([root], Imports()).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/parser/test_base.py`:
- Around line 270-303: The test currently only ensures no exception is raised;
add a concrete postcondition after calling
parser._Parser__apply_discriminator_type to ensure the union was not silently
modified by the discriminator logic: e.g., assert that union_inner still
contains exactly the two members referencing ref_pet and ref_other (check
identities of union_inner.data_types entries or their .reference attributes) and
that pet_model.base_classes remains unchanged — locate this in
test_apply_discriminator_type_skips_base_class_without_reference and add the
assertion(s) after the call to parser._Parser__apply_discriminator_type([root],
Imports()).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: af656bb8-01d5-4471-9440-e33695a9c92c
📒 Files selected for processing (2)
src/datamodel_code_generator/parser/base.pytests/parser/test_base.py
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3078 +/- ##
===========================================
- Coverage 100.00% 99.98% -0.02%
===========================================
Files 87 87
Lines 18306 18319 +13
Branches 2091 2094 +3
===========================================
+ Hits 18306 18317 +11
- Misses 0 1 +1
- Partials 0 1 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
ilovelinux
left a comment
There was a problem hiding this comment.
This PR implements cycle detection as it is usually done in graph algorithms (BFS, DFS, etc...).
LGTM! Thank you @kevin-paulson-mindbridge-ai 🙂
ilovelinux
left a comment
There was a problem hiding this comment.
Hi @kevin-paulson-mindbridge-ai, I realized it would be great to add some generation test for schemas with cyclic references in order to track the generated output of this corner-case.
See tests/main/{openapi,jsonschema,graphql}/test_main_*.py for examples. 🙂
…to track the generated output
There was a problem hiding this comment.
🧹 Nitpick comments (2)
tests/main/openapi/test_main_openapi.py (1)
4756-4761: Add an explicit timeout to this hang-regression test.The test for cyclic component reference handling (which previously caused indefinite hangs) has no timeout protection. If this regression returns, CI will stall until a global job timeout. Since
run_main_and_assert()does not enforce a per-test timeout, add one directly to this test using@pytest.mark.timeout()to fast-fail if the code hangs again.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/main/openapi/test_main_openapi.py` around lines 4756 - 4761, This test calling run_main_and_assert(...) for "cyclic_component_refs.yaml" lacks a per-test timeout and can hang CI; add a pytest timeout decorator (use `@pytest.mark.timeout`(seconds)) to the test function that invokes run_main_and_assert for cyclic_component_refs.yaml so the test fails fast if it hangs—place the decorator immediately above the test definition and choose a reasonable timeout (e.g., 10–30s) to prevent indefinite hangs while allowing normal runs.tests/main/graphql/test_main_graphql.py (1)
924-929: Make the expected snapshot explicit in this regression test.On Line 924, this relies on implicit
run_main_and_assertbehavior for expected output resolution. Please passexpected_file="cyclic_mutual_types.py"explicitly to keep the regression assertion unambiguous and resilient to helper changes.Proposed patch
def test_main_cyclic_mutual_types(output_file: Path) -> None: """Mutual object-type cycle (A <-> B) in GraphQL.""" run_main_and_assert( input_path=GRAPHQL_DATA_PATH / "cyclic-mutual-types.graphql", output_path=output_file, input_file_type="graphql", assert_func=assert_file_content, + expected_file="cyclic_mutual_types.py", )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/main/graphql/test_main_graphql.py` around lines 924 - 929, Update the test call to run_main_and_assert to pass the explicit expected snapshot filename so the regression is unambiguous: modify the invocation of run_main_and_assert (the one using input_path=GRAPHQL_DATA_PATH / "cyclic-mutual-types.graphql", output_path=output_file, input_file_type="graphql", assert_func=assert_file_content) to include expected_file="cyclic_mutual_types.py" as an additional argument.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/main/graphql/test_main_graphql.py`:
- Around line 924-929: Update the test call to run_main_and_assert to pass the
explicit expected snapshot filename so the regression is unambiguous: modify the
invocation of run_main_and_assert (the one using input_path=GRAPHQL_DATA_PATH /
"cyclic-mutual-types.graphql", output_path=output_file,
input_file_type="graphql", assert_func=assert_file_content) to include
expected_file="cyclic_mutual_types.py" as an additional argument.
In `@tests/main/openapi/test_main_openapi.py`:
- Around line 4756-4761: This test calling run_main_and_assert(...) for
"cyclic_component_refs.yaml" lacks a per-test timeout and can hang CI; add a
pytest timeout decorator (use `@pytest.mark.timeout`(seconds)) to the test
function that invokes run_main_and_assert for cyclic_component_refs.yaml so the
test fails fast if it hangs—place the decorator immediately above the test
definition and choose a reasonable timeout (e.g., 10–30s) to prevent indefinite
hangs while allowing normal runs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6a41a0a5-bdda-41a2-953a-aae47badee83
⛔ Files ignored due to path filters (3)
tests/data/graphql/cyclic-mutual-types.graphqlis excluded by!tests/data/**/*.graphqland included by nonetests/data/jsonschema/cyclic_mutual_defs.jsonis excluded by!tests/data/**/*.jsonand included by nonetests/data/openapi/cyclic_component_refs.yamlis excluded by!tests/data/**/*.yamland included by none
📒 Files selected for processing (6)
tests/data/expected/main/graphql/cyclic_mutual_types.pytests/data/expected/main/jsonschema/cyclic_mutual_defs.pytests/data/expected/main/openapi/cyclic_component_refs.pytests/main/graphql/test_main_graphql.pytests/main/jsonschema/test_main_jsonschema.pytests/main/openapi/test_main_openapi.py
✅ Files skipped from review due to trivial changes (3)
- tests/data/expected/main/openapi/cyclic_component_refs.py
- tests/data/expected/main/jsonschema/cyclic_mutual_defs.py
- tests/data/expected/main/graphql/cyclic_mutual_types.py
ilovelinux
left a comment
There was a problem hiding this comment.
I locally cherry-picked the last two commits (the ones that introduce the generation tests) to main. I expect them to fail (or hang) but they succeed instead.
Given remote:
$ git remote add kevin-paulson-mindbridge-ai https://github.com/kevin-paulson-mindbridge-ai/datamodel-code-generator.git
$ git fetch kevin-paulson-mindbridge-aiTo replicate. exec from main branch:
$ git cherry-pick 7786cf90867db72f716f1796ab071fefcffe4dc5
$ git cherry-pick 8d3ee3958ce8d9c595fdaa3d46a73bba2b54fa23
$ tox -e py314-parallel -- -k cyclicThis means that the cyclic model dependency added as test was already handled by the tool.
In fact, also the other tests (which implements the same cyclic model dependency corner-case) works on main.
$ git switch --detach kevin-paulson-mindbridge-ai/slow_schema_generation_fix
$ git restore src/datamodel_code_generator/parser/base.py --source main
$ tox -e py314-parallel -- -k cyclic@kevin-paulson-mindbridge-ai could you double-check the corner-case you have in your OpenAPI schema? As now, it looks like the one you proposed was already supported, therefore we cannot reproduce the issue, validate the fix & add regression tests 🙁
…ctually reproduce the issue (don't complete in a reasonable time)
My apologies, I neglected to validate that the new tests reproduced the issue before the change. I have updated the 3 tests such that they reproduce the issue before the change (they each ran at least 30 seconds before I killed them) and after the change they pass quickly (less than a second each). They look OK to me, but I am most familiar with OpenAPI and I am very unfamiliar with graphql. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/data/expected/main/jsonschema/cyclic_mutual_defs.py`:
- Around line 14-19: The generated classes Person and Team create an impossible
mutual inheritance cycle (Person(Team, Model) and Team(Person, Model)) causing a
runtime NameError; replace this mutual inheritance with a valid hierarchy by
removing the forward base reference—e.g., have both classes inherit from Model
only or make one inherit from the other (but not both) so that Person and Team
do not form a direct cycle; update the class headers for Person and Team (the
class definitions named Person and Team) accordingly and ensure any
tests/fixtures referencing Model still import/define Model as before.
In `@tests/data/expected/main/openapi/cyclic_component_refs.py`:
- Around line 14-19: The expected fixture currently creates an unimportable
cyclic base-class relationship between Person and Team (Person(Team, Model) and
Team(Person, Model)); fix it by removing the cyclic inheritance and making both
classes inherit only from Model (e.g., class Person(Model): and class
Team(Model):) and, if a cross-reference is needed, add attributes that reference
the other type as a forward annotation (e.g., person has team: "Team" | None =
None and team has member: "Person" | None = None) so the file can be imported
successfully while preserving the relationship between Person and Team.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c31b332b-2832-4806-b2b4-639415011af1
⛔ Files ignored due to path filters (3)
tests/data/graphql/cyclic-mutual-types.graphqlis excluded by!tests/data/**/*.graphqland included by nonetests/data/jsonschema/cyclic_mutual_defs.jsonis excluded by!tests/data/**/*.jsonand included by nonetests/data/openapi/cyclic_component_refs.yamlis excluded by!tests/data/**/*.yamland included by none
📒 Files selected for processing (3)
tests/data/expected/main/graphql/cyclic_mutual_types.pytests/data/expected/main/jsonschema/cyclic_mutual_defs.pytests/data/expected/main/openapi/cyclic_component_refs.py
| class Person(Team, Model): | ||
| name: str | None = None | ||
|
|
||
|
|
||
| class Team(Person, Model): | ||
| name: str | None = None |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify current pattern fails at class creation time (read-only check).
python - <<'PY'
code = """
from __future__ import annotations
from pydantic import BaseModel
class Model(BaseModel):
pass
class Person(Team, Model):
name: str | None = None
class Team(Person, Model):
name: str | None = None
"""
try:
exec(code, {})
print("UNEXPECTED: class graph executed successfully")
except Exception as e:
print(f"Expected failure: {type(e).__name__}: {e}")
PYRepository: koxudaxi/datamodel-code-generator
Length of output: 132
🏁 Script executed:
# Check the full content of the fixture file
cat -n tests/data/expected/main/jsonschema/cyclic_mutual_defs.pyRepository: koxudaxi/datamodel-code-generator
Length of output: 540
🏁 Script executed:
# Look for related test files that might explain the purpose
fd -i cyclic tests/Repository: koxudaxi/datamodel-code-generator
Length of output: 495
🏁 Script executed:
# Check for input schema files
fd cyclic_mutual tests/ -type fRepository: koxudaxi/datamodel-code-generator
Length of output: 247
🏁 Script executed:
# Read the input schema file
cat -n tests/data/jsonschema/cyclic_mutual_defs.jsonRepository: koxudaxi/datamodel-code-generator
Length of output: 994
🏁 Script executed:
# Search for test code that validates this fixture
rg -l cyclic_mutual_defs tests/ -t pyRepository: koxudaxi/datamodel-code-generator
Length of output: 180
🏁 Script executed:
# Find and read the test file that uses this fixture
rg -B 5 -A 10 cyclic_mutual_defs tests/main/jsonschema/test_main_jsonschema.pyRepository: koxudaxi/datamodel-code-generator
Length of output: 714
🏁 Script executed:
# Check the other cyclic test case (OpenAPI)
cat -n tests/data/expected/main/openapi/cyclic_component_refs.pyRepository: koxudaxi/datamodel-code-generator
Length of output: 543
🏁 Script executed:
# Check the GraphQL cyclic test case
cat -n tests/data/expected/main/graphql/cyclic_mutual_types.pyRepository: koxudaxi/datamodel-code-generator
Length of output: 1337
🏁 Script executed:
# Search for any discussion or comments about cyclic references in the codebase
rg -i "cyclic|forward.*ref|mutual.*inherit" tests/ -A 2 -B 2 --max-count=20Repository: koxudaxi/datamodel-code-generator
Length of output: 18508
🏁 Script executed:
# Search for any TODOs or comments about cyclic inheritance in the codebase
rg -i "cyclic|todo|fixme.*inherit|forward.*ref.*inherit" --max-count=20 datamodel_code_generator/Repository: koxudaxi/datamodel-code-generator
Length of output: 142
🏁 Script executed:
# Check if there are any comments in the test file about cyclic_mutual_defs
rg -B 10 -A 5 "test_main_cyclic_mutual_defs" tests/main/jsonschema/test_main_jsonschema.pyRepository: koxudaxi/datamodel-code-generator
Length of output: 701
🏁 Script executed:
# Find the source directory
fd -t d "^datamodel" . | head -5Repository: koxudaxi/datamodel-code-generator
Length of output: 108
🏁 Script executed:
# Search for any issue numbers or comments about cyclic handling
rg -i "cyclic|mutual.*def" . --max-count=30 -t py | grep -E "(TODO|FIXME|issue|#[0-9]+|NOTE)" | head -20Repository: koxudaxi/datamodel-code-generator
Length of output: 59
🏁 Script executed:
# Search for code related to inheritance and base class generation
rg -i "base.*class|inherit" src/datamodel_code_generator/ -t py --max-count=20 | head -30Repository: koxudaxi/datamodel-code-generator
Length of output: 3872
🏁 Script executed:
# Check if there's special handling for ordering classes based on dependencies
rg -l "sort|order|depend" src/datamodel_code_generator/ -t py | head -10Repository: koxudaxi/datamodel-code-generator
Length of output: 526
🏁 Script executed:
# Look for any parser logic that handles allOf or schema references
rg "allOf|definitions" src/datamodel_code_generator/ -t py --max-count=15Repository: koxudaxi/datamodel-code-generator
Length of output: 8419
🏁 Script executed:
# Examine the SCC (Strongly Connected Components) module for cycle handling
cat src/datamodel_code_generator/parser/_scc.pyRepository: koxudaxi/datamodel-code-generator
Length of output: 5865
🏁 Script executed:
# Search for where allOf is processed and how inheritance is generated
rg "allOf" src/datamodel_code_generator/parser/jsonschema.py -B 3 -A 10 | head -80Repository: koxudaxi/datamodel-code-generator
Length of output: 3802
🏁 Script executed:
# Look for how base classes are generated from allOf
rg -i "base.*class|inherit" src/datamodel_code_generator/parser/jsonschema.py -B 2 -A 5 | head -100Repository: koxudaxi/datamodel-code-generator
Length of output: 4456
🏁 Script executed:
# Check the inheritance_forward_ref fixture to see how it handles forward references
cat -n tests/data/expected/main/jsonschema/inheritance_forward_ref.pyRepository: koxudaxi/datamodel-code-generator
Length of output: 1616
🏁 Script executed:
# Also check the input schema for inheritance_forward_ref
cat tests/data/jsonschema/inheritance_forward_ref.jsonRepository: koxudaxi/datamodel-code-generator
Length of output: 2386
🏁 Script executed:
# Check if cyclic_mutual_defs.py has model_rebuild calls
tail -20 tests/data/expected/main/jsonschema/cyclic_mutual_defs.pyRepository: koxudaxi/datamodel-code-generator
Length of output: 407
🏁 Script executed:
# Look for any comments or documentation about this limitation in the parser code
rg -i "mutual|cyclic.*class|inherit.*cycle" src/datamodel_code_generator/ -B 2 -A 2Repository: koxudaxi/datamodel-code-generator
Length of output: 1180
🏁 Script executed:
# Check if there's a --keep-model-order option being used and how it affects output
rg "keep.*model.*order|--keep-model-order" tests/data/expected/main/jsonschema/ -lRepository: koxudaxi/datamodel-code-generator
Length of output: 149
🏁 Script executed:
# Check if there are any GitHub issues or discussions about this in code comments
rg "1466|1467|1468|cyclic.*inherit" src/ tests/ --max-count=10Repository: koxudaxi/datamodel-code-generator
Length of output: 197
🏁 Script executed:
# Final confirmation: does the current output actually fail to import?
python3 << 'EOF'
import sys
code_path = "tests/data/expected/main/jsonschema/cyclic_mutual_defs.py"
try:
with open(code_path) as f:
code = f.read()
exec(compile(code, code_path, 'exec'), {})
print("SUCCESS: Code executed without error")
except Exception as e:
print(f"FAILURE: {type(e).__name__}: {e}")
sys.exit(1)
EOFRepository: koxudaxi/datamodel-code-generator
Length of output: 123
Invalid Python output: forward base reference and mutual inheritance cycle
Person(Team, Model) at Line 14 references Team before it exists, and Person/Team form an impossible direct inheritance cycle. The output fails at runtime with NameError: name 'Team' is not defined. Even with from __future__ import annotations, base class names are resolved eagerly at class definition time, so forward references do not help. The model_rebuild() pattern used in other forward-reference fixtures does not apply here because mutual class inheritance cannot be resolved post-hoc.
Consider generating valid output by removing the mutual inheritance:
Proposed fixture correction
-class Person(Team, Model):
+class Person(Model):
name: str | None = None
-class Team(Person, Model):
+class Team(Model):
name: str | None = None🧰 Tools
🪛 Ruff (0.15.9)
[error] 14-14: Undefined name Team
(F821)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/data/expected/main/jsonschema/cyclic_mutual_defs.py` around lines 14 -
19, The generated classes Person and Team create an impossible mutual
inheritance cycle (Person(Team, Model) and Team(Person, Model)) causing a
runtime NameError; replace this mutual inheritance with a valid hierarchy by
removing the forward base reference—e.g., have both classes inherit from Model
only or make one inherit from the other (but not both) so that Person and Team
do not form a direct cycle; update the class headers for Person and Team (the
class definitions named Person and Team) accordingly and ensure any
tests/fixtures referencing Model still import/define Model as before.
| class Person(Team, Model): | ||
| name: str | None = None | ||
|
|
||
|
|
||
| class Team(Person, Model): | ||
| name: str | None = None |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
python - <<'PY'
src = """
from __future__ import annotations
class BaseModel: pass
class Model(BaseModel): pass
class Person(Team, Model): pass
class Team(Person, Model): pass
"""
try:
exec(src, {})
except Exception as exc:
print(f"{type(exc).__name__}: {exc}")
PYRepository: koxudaxi/datamodel-code-generator
Length of output: 114
🏁 Script executed:
cat -n tests/data/expected/main/openapi/cyclic_component_refs.pyRepository: koxudaxi/datamodel-code-generator
Length of output: 543
Fix the cyclic inheritance in the expected fixture—this code cannot be imported.
The generated Person and Team classes form an unimportable cycle. Line 14 references Team before it is defined, which raises NameError at class definition time. The from __future__ import annotations pragma only defers annotations, not base-class lookup. Either reject this schema in the generator or adjust the expected output to produce valid Python.
🧰 Tools
🪛 Ruff (0.15.9)
[error] 14-14: Undefined name Team
(F821)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/data/expected/main/openapi/cyclic_component_refs.py` around lines 14 -
19, The expected fixture currently creates an unimportable cyclic base-class
relationship between Person and Team (Person(Team, Model) and Team(Person,
Model)); fix it by removing the cyclic inheritance and making both classes
inherit only from Model (e.g., class Person(Model): and class Team(Model):) and,
if a cross-reference is needed, add attributes that reference the other type as
a forward annotation (e.g., person has team: "Team" | None = None and team has
member: "Person" | None = None) so the file can be imported successfully while
preserving the relationship between Person and Team.
There was a problem hiding this comment.
I'll see if I can create better test data.
There was a problem hiding this comment.
@kevin-paulson-mindbridge-ai, sounds good! Here are a few concrete directions you could take:
Option A — Cyclic field references instead of cyclic inheritance
If your input schema uses allOf to pull in referenced types as base classes, consider restructuring it so each type holds the other as an optional field (forward-referenced). This produces importable Python:
# generated by datamodel-codegen:
# filename: cyclic_component_refs.yaml
# timestamp: 2019-07-26T00:00:00+00:00
from __future__ import annotations
from typing import Optional
from pydantic import BaseModel
class Person(BaseModel):
name: str | None = None
team: Optional[Team] = None # forward ref resolved by `from __future__ import annotations`
class Team(BaseModel):
name: str | None = None
member: Optional[Person] = NoneThis is valid Python (the from __future__ import annotations does help for field annotations).
Option B — Non-cyclic inheritance with a shared base
If the test is specifically meant to exercise how the generator handles allOf with a common parent, restructure so the cycle is not in the base-class chain:
class Model(BaseModel):
pass
class Person(Model):
name: str | None = None
team: Team | None = None # field ref, not inheritance
class Team(Model):
name: str | None = NoneOption C — Adjust the input schema
The root cause is the input YAML/JSON/GraphQL schema itself encoding a pattern (mutual allOf inheritance) that has no valid Python mapping. You could adjust the input schema to use composition via properties instead of allOf-based inheritance, which would make the expected output naturally importable.
Which approach fits your schema best? Happy to help refine the expected fixture once you decide on a direction.
🧠 Learnings used
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2890
File: tests/data/expected/main/jsonschema/ref_nullable_with_constraint.py:14-15
Timestamp: 2026-01-02T08:25:22.111Z
Learning: The datamodel-code-generator currently generates RootModel subclasses with an explicit `root` field annotation (e.g., `class StringType(RootModel[str]): root: str`). This is existing behavior of the code generator and should not be flagged as an issue introduced by new changes.
There was a problem hiding this comment.
@kevin-paulson-mindbridge-ai since you said those examples won't generate any output before1, and now they generate this output, I think this may been introduced by your bugfix.
That's an interesting corner-case. I think we should look into that before merging.
@kevin-paulson-mindbridge-ai IMHO if you can't fix that, consider adding an error to communicate to the user this is an (yet) unsupported corner-case, in order to avoid generating broken code. We may track that in another issue then and fix later. Finding the edges of the problem is the first step to solve it, after all.
CC: @koxudaxi WDYT? 🙂
Footnotes
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
I no longer think that this test data is fully reproducing the original issue and I don't think the fix is quite it either.
What caused me to look at this is we have a somewhat large schema available at test.mindbridge.ai/api/docs/mindbridge-openapi.json which produces with this change an about 20,000 line (valid) python file. Although with this change it will now generate the python file instead of never terminating:
- The generated python file produces duplicate union members, this was not noticed as they were hidden by
ruff check --fix - I'm having a lot of difficulty determining a test that reproduces the issue and still generates the valid Python expected.
I will plan to continue reviewing this PR later this week to see what I can discover.
That's not a problem, I appreciate your effort. Reviews & double-checks are useful because of that 🙂
I'll review it as soon as I'll get some free time 🙂
Could you add a timeout as suggested by the first nitpick comment here: #3078 (review)? |
1) Avoid infinite loop due to cyclic inheritance with discriminators 2) De-duplicate union members (but keep same order)
|
I think I've got things much closer now. I'll plan to come back to this to:
|
We've been generating a model with the tool based on our OpenAPI schema, however since version 0.37.0 we've been unsuccessful in doing so. In the latest release (0.56.0) the generation was revealed to be stuck in
sort_data_models. I waited up to an hour, I don't think it was ever going to complete. With this change it now works as expected.I tried to follow https://datamodel-code-generator.koxudaxi.dev/development-contributing/ as best as I can, but let me know if you would like me to make any changes or if I should create an issue or anything else. Thanks!
Summary by CodeRabbit
New Features
Bug Fixes
Tests