Skip to content

Fix bug in handling of graphql empty list defaults#2948

Merged
koxudaxi merged 6 commits intokoxudaxi:mainfrom
rpmcginty:bugfix/graphql-empty-list-default-bug
Jan 12, 2026
Merged

Fix bug in handling of graphql empty list defaults#2948
koxudaxi merged 6 commits intokoxudaxi:mainfrom
rpmcginty:bugfix/graphql-empty-list-default-bug

Conversation

@rpmcginty
Copy link
Copy Markdown
Contributor

@rpmcginty rpmcginty commented Jan 6, 2026

Summary by CodeRabbit

  • New Features

    • Generated models now include GraphQL __typename metadata and initialize list fields as empty lists by default.
    • Added Boolean and String type aliases in generated outputs.
  • Improvements

    • Improved handling of single-item list defaults and modernized type hints to Python 3.10+ union syntax.
  • Tests

    • Added parameterized tests for empty-list-default across Pydantic versions and tightened OpenAPI test assertions.

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 6, 2026

Warning

Rate limit exceeded

@koxudaxi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 28 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 8431860 and df9dd1e.

📒 Files selected for processing (1)
  • tests/main/graphql/test_main_graphql.py
📝 Walkthrough

Walkthrough

Adds generated Pydantic v1/v2 fixtures for GraphQL and OpenAPI empty-list defaults, extends pydantic default-handling to support single-item lists of BaseModel inner types, and introduces a parameterized GraphQL test plus a small OpenAPI test assertion change.

Changes

Cohort / File(s) Summary
Generated GraphQL models (Pydantic v1)
tests/data/expected/main/graphql/empty_list_default.py
New file: adds Boolean/String aliases and two Pydantic models (Container, PodSpec) with list fields using Field(default_factory=list) and __typename aliases.
Generated GraphQL models (Pydantic v2)
tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py
New file: adds Boolean/String aliases; Container and PodSpec with multiple list variants (list[Container], `list[Container
GraphQL tests
tests/main/graphql/test_main_graphql.py
Adds parameterized test test_main_graphql_empty_list_default for pydantic.BaseModel and pydantic_v2.BaseModel, with Black-version skip and expected-output assertions.
OpenAPI tests
tests/main/openapi/test_main_openapi.py
Minor change: test_main_openapi_empty_list_default now passes assert_func=assert_file_content to run_main_and_assert.
Pydantic model handling
src/datamodel_code_generator/model/pydantic/base_model.py
Extend _get_default_as_pydantic_model to handle single-item lists whose inner type is a BaseModel: return STANDARD_LIST for empty defaults or construct a lambda mapping inner-model parse for non-empty lists; add noqa and pragma no cover annotations; tighten required/default handling in __str__.

Sequence Diagram(s)

(Skipped — changes are localized to model generation and parsing logic; no multi-component sequential flow suitable for diagramming.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

safe-to-fix

Suggested reviewers

  • ilovelinux

Poem

🐇
I nibble schemas in moonlit code,
Empty lists snug in every node,
Factories sprout where defaults were thin,
Models hop in, new hopes begin,
Tests cheer softly — carrots for the win. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the main bug fix: handling of GraphQL empty list defaults, which is clearly reflected in the code changes across GraphQL test files and the base model implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

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: 3

🤖 Fix all issues with AI Agents
In @tests/main/graphql/test_main_graphql.py:
- Around line 124-125: Rename the test function
test_main_openapi_empty_list_default to test_main_graphql_empty_list_default and
update its docstring from "Test OpenAPI generation with empty list default
values." to "Test GraphQL generation with empty list default values." to reflect
that this is a GraphQL test (ensure any references to the old name in the test
module are updated accordingly).
- Line 129: The test passes a Path-prefixed value for expected_file which should
instead be the filename string; replace the argument
expected_file=GRAPHQL_DATA_PATH / expected_output with
expected_file=expected_output so expected_file uses the same filename string as
expected_output (refer to the expected_file and expected_output parameters in
the failing test function).
- Line 127: The test references GRAPHQL_DATA_PATH / "empty_list_default.graphql"
but the tests/data/graphql directory and the file empty_list_default.graphql are
missing; create the tests/data/graphql directory and add an
empty_list_default.graphql file matching the expected input schema (e.g., a
minimal GraphQL schema or the specific input used by the test) so the test can
load GRAPHQL_DATA_PATH / "empty_list_default.graphql" successfully.
📜 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 fd5e698 and 67079bb.

⛔ Files ignored due to path filters (1)
  • tests/data/graphql/empty_list_default.graphql is excluded by !tests/data/**/*.graphql and included by none
📒 Files selected for processing (3)
  • tests/data/expected/main/graphql/empty_list_default.py
  • tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py
  • tests/main/graphql/test_main_graphql.py
🧰 Additional context used
🧬 Code graph analysis (2)
tests/data/expected/main/graphql/empty_list_default.py (3)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py (2)
  • Container (12-13)
  • PodSpec (16-17)
src/datamodel_code_generator/model/base.py (1)
  • name (839-841)
tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py (1)
tests/data/expected/main/graphql/empty_list_default.py (2)
  • Container (12-13)
  • PodSpec (16-17)
⏰ 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). (12)
  • GitHub Check: 3.11 on Ubuntu
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.14 on Ubuntu
  • GitHub Check: py312-pydantic1 on Ubuntu
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: py312-black23 on Ubuntu
  • GitHub Check: 3.10 on Ubuntu
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.13 on Windows
  • GitHub Check: benchmarks
  • GitHub Check: Analyze (python)
🔇 Additional comments (2)
tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py (1)

1-17: LGTM! Expected output correctly uses Field(default_factory=list) for mutable defaults.

The generated Pydantic models correctly use Field(default_factory=list) for the optional list field, which is the proper pattern to avoid mutable default issues in Python.

tests/data/expected/main/graphql/empty_list_default.py (1)

1-17: LGTM! Expected output uses correct Pydantic pattern.

The expected output correctly demonstrates the Field(default_factory=list) pattern for optional container fields with empty list defaults.

Comment thread tests/main/graphql/test_main_graphql.py Outdated
Comment thread tests/main/graphql/test_main_graphql.py
Comment thread tests/main/graphql/test_main_graphql.py Outdated
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Jan 6, 2026

CodSpeed Performance Report

Merging this PR will not alter performance

Comparing rpmcginty:bugfix/graphql-empty-list-default-bug (df9dd1e) with main (838b2a0)

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

Summary

✅ 11 untouched benchmarks
⏩ 98 skipped benchmarks1

Footnotes

  1. 98 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.

@rpmcginty rpmcginty force-pushed the bugfix/graphql-empty-list-default-bug branch from 67079bb to efbc5d6 Compare January 7, 2026 01:59
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: 1

🤖 Fix all issues with AI agents
In @tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py:
- Around line 16-17: The generated Pydantic model makes the GraphQL non-nullable
list field nullable by using Optional[List[Container]]; in the PodSpec class
remove Optional so the containers annotation is List[Container] and keep
Field(default_factory=list) to preserve the empty-list default, updating the
containers type hint in the PodSpec model accordingly.
📜 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 67079bb and efbc5d6.

⛔ Files ignored due to path filters (1)
  • tests/data/graphql/empty_list_default.graphql is excluded by !tests/data/**/*.graphql and included by none
📒 Files selected for processing (3)
  • tests/data/expected/main/graphql/empty_list_default.py
  • tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py
  • tests/main/graphql/test_main_graphql.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/main/graphql/test_main_graphql.py
  • tests/data/expected/main/graphql/empty_list_default.py
🧰 Additional context used
🧬 Code graph analysis (1)
tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py (1)
tests/data/expected/main/graphql/empty_list_default.py (2)
  • Container (12-13)
  • PodSpec (16-17)
⏰ 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). (10)
  • GitHub Check: 3.12 on macOS
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.10 on macOS
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.13 on macOS
  • GitHub Check: 3.13 on Windows
  • GitHub Check: benchmarks
  • GitHub Check: Analyze (python)
🔇 Additional comments (2)
tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py (2)

1-10: LGTM! Header and imports are correct for Pydantic v2.

The generated file header, future annotations import, and Pydantic v2 imports are all appropriate for this test fixture.


12-13: LGTM! Container model is correctly defined.

The Container model with an optional name field is properly structured for Pydantic v2.

Comment thread tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py Outdated
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 (1)
src/datamodel_code_generator/model/pydantic/base_model.py (1)

132-145: New logic correctly handles GraphQL empty list defaults.

The implementation properly handles the case where the parent data type is a list with a single BaseModel child:

  • Returns STANDARD_LIST for empty list defaults (line 141)
  • Returns a parsing lambda for non-empty defaults (lines 142-145)

This complements the existing loop logic (lines 151-163) which handles lists within union types.

Optional: Consider extracting common logic.

Lines 132-145 and 151-163 contain nearly identical logic. You could extract this into a helper method to reduce duplication, though this is optional given the slightly different contexts.

📜 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 4fa0727 and 936e6a8.

⛔ Files ignored due to path filters (1)
  • tests/data/graphql/empty_list_default.graphql is excluded by !tests/data/**/*.graphql and included by none
📒 Files selected for processing (3)
  • src/datamodel_code_generator/model/pydantic/base_model.py
  • tests/data/expected/main/graphql/empty_list_default.py
  • tests/main/graphql/test_main_graphql.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/main/graphql/test_main_graphql.py
🧰 Additional context used
🪛 Ruff (0.14.10)
src/datamodel_code_generator/model/pydantic/base_model.py

129-129: Unused noqa directive (non-enabled: PLR0911, PLR0912)

Remove unused noqa directive

(RUF100)

⏰ 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). (12)
  • GitHub Check: py312-isort6 on Ubuntu
  • GitHub Check: py312-isort7 on Ubuntu
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: py312-black24 on Ubuntu
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.13 on Ubuntu
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: benchmarks
  • GitHub Check: Analyze (python)
🔇 Additional comments (2)
tests/data/expected/main/graphql/empty_list_default.py (1)

1-32: LGTM! Correct implementation of empty list defaults.

The generated fixture properly uses default_factory=list on line 31 to handle empty list defaults, which is the correct Pydantic pattern for mutable default values. The model structure, type aliases, and GraphQL __typename handling all follow expected conventions.

src/datamodel_code_generator/model/pydantic/base_model.py (1)

129-129: Remove unnecessary noqa directive.

The noqa: PLR0911, PLR0912 directive is flagged as unnecessary because these rules are not enabled in your project configuration.

Based on static analysis hints.

🧹 Proposed fix
-    def _get_default_as_pydantic_model(self) -> str | None:  # noqa: PLR0911, PLR0912
+    def _get_default_as_pydantic_model(self) -> str | None:
⛔ Skipped due to learnings
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2681
File: tests/cli_doc/test_cli_doc_coverage.py:82-82
Timestamp: 2025-12-18T13:43:16.235Z
Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.
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.
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/util.py:49-66
Timestamp: 2025-12-25T09:23:08.506Z
Learning: In datamodel-code-generator, the is_pydantic_v2() and is_pydantic_v2_11() functions in src/datamodel_code_generator/util.py intentionally use global variable caching (_is_v2, _is_v2_11) on top of lru_cache for performance optimization. This dual-layer caching eliminates function call overhead and cache lookup overhead for frequently-called version checks. The PLW0603 linter warnings should be suppressed with # noqa: PLW0603 as this is a deliberate design choice.

@koxudaxi koxudaxi force-pushed the bugfix/graphql-empty-list-default-bug branch from 936e6a8 to dff1941 Compare January 10, 2026 07:18
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 (1)
src/datamodel_code_generator/model/pydantic/base_model.py (1)

132-145: Consider extracting the duplicated list-handling logic.

This code block is nearly identical to lines 151-163 within the loop below. The duplication suggests that sometimes self.data_type.is_list is True directly (GraphQL case), and sometimes we need to iterate through data_type.data_types to find a list type.

Consider extracting the shared logic into a helper method to reduce maintenance burden and improve clarity:

♻️ Suggested refactor to eliminate duplication
def _handle_list_of_models(self, data_type_list, data_type_child, default):
    """Handle default value for a list of BaseModel instances."""
    if (
        data_type_child.reference
        and isinstance(data_type_child.reference.source, BaseModelBase)
        and isinstance(default, list)
    ):
        if not default:
            return STANDARD_LIST
        return (
            f"lambda :[{data_type_child.alias or data_type_child.reference.source.class_name}."
            f"{self._PARSE_METHOD}(v) for v in {default!r}]"
        )
    return None

Then use it in both locations:

# At lines 132-145
if self.data_type.is_list and len(self.data_type.data_types) == 1:
    result = self._handle_list_of_models(
        self.data_type, self.data_type.data_types[0], self.default
    )
    if result:
        return result

# And at lines 151-163
if data_type.is_list and len(data_type.data_types) == 1:
    result = self._handle_list_of_models(
        data_type, data_type.data_types[0], self.default
    )
    if result:
        return result
📜 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 936e6a8 and dff1941.

📒 Files selected for processing (2)
  • src/datamodel_code_generator/model/pydantic/base_model.py
  • tests/main/graphql/test_main_graphql.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/main/graphql/test_main_graphql.py
🧰 Additional context used
🪛 Ruff (0.14.10)
src/datamodel_code_generator/model/pydantic/base_model.py

129-129: Unused noqa directive (non-enabled: PLR0911, PLR0912)

Remove unused noqa directive

(RUF100)

⏰ 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: 3.10 on Ubuntu
  • GitHub Check: py312-isort7 on Ubuntu
  • GitHub Check: 3.14 on Windows
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: py312-pydantic1 on Ubuntu
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.12 on Ubuntu
  • GitHub Check: 3.13 on Ubuntu
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: benchmarks
  • GitHub Check: Analyze (python)
🔇 Additional comments (2)
src/datamodel_code_generator/model/pydantic/base_model.py (2)

129-129: Remove unused noqa directive.

The PLR0911 and PLR0912 rules are not enabled in your Ruff configuration, making this directive unnecessary.

As per static analysis hints, the unused directive should be removed to reduce code noise.

🧹 Proposed fix
-    def _get_default_as_pydantic_model(self) -> str | None:  # noqa: PLR0911, PLR0912
+    def _get_default_as_pydantic_model(self) -> str | None:
⛔ Skipped due to learnings
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2681
File: tests/cli_doc/test_cli_doc_coverage.py:82-82
Timestamp: 2025-12-18T13:43:16.235Z
Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

142-145: The pragma: no cover correctly indicates untested code for non-empty GraphQL list defaults.

The bug fix commit dff1941 only adds test coverage for empty GraphQL list defaults (default_factory=list). The non-empty code path (lines 142–145) that generates lambda :[...parse_obj(v) for v in {...}] is not exercised by the test suite. If non-empty GraphQL list defaults are valid and should be supported, add a test case using a GraphQL schema with a non-empty list default value.

@koxudaxi koxudaxi changed the title [WIP] Fix bug in handling of graphql empty list defaults Fix bug in handling of graphql empty list defaults Jan 12, 2026
@koxudaxi koxudaxi force-pushed the bugfix/graphql-empty-list-default-bug branch from dff1941 to 8431860 Compare January 12, 2026 17:42
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: 1

🤖 Fix all issues with AI agents
In @tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py:
- Around line 11-20: Replace the PEP 695 "type X = Y" aliases with a Python
3.10-compatible TypeAlias annotation: import TypeAlias from typing and change
the two aliases so Boolean and String are declared as "Boolean: TypeAlias =
bool" and "String: TypeAlias = str" respectively, preserving their docstrings
exactly; update any relevant top-level imports to include "from typing import
TypeAlias".
🧹 Nitpick comments (1)
src/datamodel_code_generator/model/pydantic/base_model.py (1)

129-129: Consider removing the unused noqa directive.

Ruff indicates this noqa: PLR0911, PLR0912 directive is unused (the rules may not be enabled). If these rules aren't active in the project's linting configuration, the comment can be removed to reduce noise.

Proposed fix
-    def _get_default_as_pydantic_model(self) -> str | None:  # noqa: PLR0911, PLR0912
+    def _get_default_as_pydantic_model(self) -> str | None:
📜 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 dff1941 and 8431860.

⛔ Files ignored due to path filters (1)
  • tests/data/graphql/empty_list_default.graphql is excluded by !tests/data/**/*.graphql and included by none
📒 Files selected for processing (7)
  • src/datamodel_code_generator/model/pydantic/base_model.py
  • tests/data/expected/main/graphql/empty_list_default.py
  • tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py
  • tests/data/expected/main/openapi/empty_list_default.py
  • tests/data/expected/main/openapi/pydantic_v2_empty_list_default.py
  • tests/main/graphql/test_main_graphql.py
  • tests/main/openapi/test_main_openapi.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/main/graphql/test_main_graphql.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-02T08:25:22.111Z
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.

Applied to files:

  • src/datamodel_code_generator/model/pydantic/base_model.py
🧬 Code graph analysis (3)
src/datamodel_code_generator/model/pydantic/base_model.py (4)
src/datamodel_code_generator/model/_types.py (1)
  • WrappedDefault (10-18)
src/datamodel_code_generator/reference.py (1)
  • reference (77-79)
src/datamodel_code_generator/model/base.py (2)
  • class_name (860-862)
  • class_name (865-869)
src/datamodel_code_generator/parser/jsonschema.py (1)
  • has_default (430-432)
tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py (1)
tests/data/expected/main/graphql/empty_list_default.py (2)
  • Container (23-25)
  • PodSpec (28-34)
tests/data/expected/main/openapi/pydantic_v2_empty_list_default.py (1)
tests/data/expected/main/openapi/empty_list_default.py (2)
  • PodSpec (14-15)
  • Container (10-11)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/model/pydantic/base_model.py

129-129: Unused noqa directive (non-enabled: PLR0911, PLR0912)

Remove unused noqa directive

(RUF100)

tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py

11-11: Cannot use type alias statement on Python 3.10 (syntax was added in Python 3.12)

(invalid-syntax)


17-17: Cannot use type alias statement on Python 3.10 (syntax was added in Python 3.12)

(invalid-syntax)

tests/data/expected/main/graphql/empty_list_default.py

11-11: Cannot use type alias statement on Python 3.10 (syntax was added in Python 3.12)

(invalid-syntax)


17-17: Cannot use type alias statement on Python 3.10 (syntax was added in Python 3.12)

(invalid-syntax)

⏰ 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). (12)
  • GitHub Check: 3.13 on macOS
  • GitHub Check: 3.14 on macOS
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.10 on macOS
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.11 on macOS
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.12 on macOS
  • GitHub Check: benchmarks
  • GitHub Check: Analyze (python)
🔇 Additional comments (11)
tests/data/expected/main/graphql/empty_list_default.py (2)

28-34: LGTM on the PodSpec model definition.

The PodSpec model correctly uses Field(default_factory=list) for the list fields with empty list defaults, which is the proper pattern to avoid mutable default argument issues in Pydantic.


11-20: This is correct for the test configuration.

The type statement syntax (lines 11, 17) is intentional. The test explicitly passes --target-python-version 3.12 to the code generator, making PEP 695 type statement syntax the correct output for this fixture. No changes needed.

src/datamodel_code_generator/model/pydantic/base_model.py (3)

129-144: Good addition for handling single-item list of BaseModel.

The new logic correctly handles the case where a field is a list containing a single BaseModel type:

  • Returns STANDARD_LIST for empty list defaults, ensuring default_factory=list generation.
  • For non-empty lists, constructs a lambda that parses each element.

The early return at line 140 for empty lists is particularly important for the bugfix this PR addresses.


236-236: Correct fix for required fields with defaults.

The condition change from if self.required to if self.required and not self.has_default ensures that fields marked as required but having a default value (like empty list defaults) still get their default_factory computed rather than being skipped.


265-265: Correct guard against adding ellipsis when default_factory exists.

The condition change ensures that when a default_factory is present for a required field, the ellipsis (...) isn't incorrectly prepended to the field arguments.

tests/main/openapi/test_main_openapi.py (1)

2805-2821: LGTM - Improved test assertion.

Adding assert_func=assert_file_content ensures the test properly validates the generated output against the expected fixture file, providing stronger verification of the empty list default handling.

tests/data/expected/main/openapi/empty_list_default.py (1)

10-15: LGTM - Correct expected output for empty list defaults.

The updated type hints use modern Python union syntax (enabled by from __future__ import annotations), and Field(default_factory=list) correctly handles the empty list default for the containers field.

tests/data/expected/main/openapi/pydantic_v2_empty_list_default.py (1)

10-15: LGTM - Pydantic v2 expected output matches v1.

The expected output correctly uses modern type hints and Field(default_factory=list) for empty list defaults.

Note: This file appears identical to empty_list_default.py. If the generated output is truly the same for both Pydantic v1 and v2 for this schema, consider whether maintaining separate files is necessary or if a single fixture could serve both test cases.

tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py (3)

1-9: LGTM!

The header and imports are appropriate for a Pydantic v2 generated model file. The from __future__ import annotations import is a good practice for forward references.


23-25: LGTM!

The Container class definition is correct for Pydantic v2. The typename__ field properly uses Field with a default value and alias for the GraphQL __typename introspection field.


28-34: LGTM!

The PodSpec class correctly demonstrates the empty list default handling fix. Using Field(default_factory=list) is the proper pattern to avoid mutable default argument issues in Pydantic. The three field variations appropriately test different nullability combinations:

  • container_list: non-nullable list of non-nullable items
  • container_list_or_none: non-nullable list with nullable items
  • container_or_none_list_or_none: nullable list with nullable items

Comment thread tests/data/expected/main/graphql/pydantic_v2_empty_list_default.py
@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (838b2a0) to head (df9dd1e).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #2948   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           94        94           
  Lines        17778     17787    +9     
  Branches      2043      2045    +2     
=========================================
+ Hits         17778     17787    +9     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

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.

@koxudaxi koxudaxi merged commit a6a7b04 into koxudaxi:main Jan 12, 2026
38 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

Breaking Change Analysis

Result: No breaking changes detected

Reasoning: This PR is a bug fix for GraphQL list fields with empty list defaults (e.g., [Container!]! = []). The key changes are: (1) Added logic to handle empty list defaults for list fields with model references, returning default_factory=list. (2) Changed the condition if self.required: to if self.required and not self.has_default: so that required fields with explicit defaults no longer incorrectly ignore those defaults. (3) Changed elif self.required: to elif self.required and not default_factory: to prevent adding ... when a default_factory exists. These are correctness fixes - previously, required fields with explicit empty list defaults were incorrectly generated without those defaults. The new behavior correctly respects the schema-defined defaults. Users who were working around this bug may see different output, but the new output is the correct behavior per the schema definition. The OpenAPI test file changes are just cosmetic (modernizing Optional[List[X]] to list[X] | None) and fixing a test that wasn't actually comparing files. No CLI/API changes, no Python version changes, no template changes required.


This analysis was performed by Claude Code Action

@github-actions
Copy link
Copy Markdown
Contributor

🎉 Released in 0.53.0

This PR is now available in the latest release. See the release notes for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants