Skip to content

Guard mutable generation inputs#3394

Merged
koxudaxi merged 3 commits into
mainfrom
guard-input-mutations
Jun 13, 2026
Merged

Guard mutable generation inputs#3394
koxudaxi merged 3 commits into
mainfrom
guard-input-mutations

Conversation

@koxudaxi

@koxudaxi koxudaxi commented Jun 13, 2026

Copy link
Copy Markdown
Owner

No description provided.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR prevents input mutation in the JSON Schema parser by excluding parser-owned "self" metadata from validation inputs without mutating originals, adds a test context manager to detect input mutations, updates integration test helpers to use the guard, and adds regression and unit tests to verify no mutations occur.

Changes

Input Mutation Prevention

Layer / File(s) Summary
Parser non-mutation fix
src/datamodel_code_generator/parser/jsonschema.py
Treats "self" as reserved, adds non-mutating sanitization for validation inputs, removes in-place raw.pop("self", None), and updates JsonSchemaObject.__init__ to use __pydantic_self__ naming.
Test mutation detection framework
tests/conftest.py
Adds _tracks_mutation and assert_inputs_not_mutated context manager that deep-copies tracked dict/list inputs and fails tests when changes are detected.
Integration test helper updates
tests/main/conftest.py
Extends run_generate_file_and_assert and run_generate_and_assert with unchanged_inputs / assert_input_unchanged and runs generation and parity assertions inside assert_inputs_not_mutated.
Regression tests for cached YAML preservation
tests/main/jsonschema/test_main_jsonschema.py
Adds tests that preload cached YAML entries and assert generation preserves cached root and referenced YAML files unchanged.
Existing tests adopt mutation guards
tests/main/protobuf/test_main_protobuf.py, tests/main/test_main_general.py
Refactors tests to call shared helpers with assert_input_unchanged=True so dict inputs are checked for non-mutation.
Mutation guard unit tests
tests/test_conftest_helpers.py
Adds tests verifying assert_inputs_not_mutated allows unchanged nested structures and reports nested mutations with the expected failure message.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

breaking-change-analyzed

Poem

🐰 I hop through YAML, gentle and light,
I copy the self and keep inputs right.
Cached refs untouched, the tests clap along,
No sneaky mutations — the schemas stay strong.
thump-thump 🎉

🚥 Pre-merge checks | ✅ 5
✅ 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 'Guard mutable generation inputs' clearly summarizes the main change: adding mutation guards to prevent side effects on input objects during code generation.
Docstring Coverage ✅ Passed Docstring coverage is 95.24% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch guard-input-mutations

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.

@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

📚 Docs Preview: https://pr-3394.datamodel-code-generator.pages.dev

@codspeed-hq

codspeed-hq Bot commented Jun 13, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ 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.

✅ 11 untouched benchmarks
⏩ 98 skipped benchmarks1


Comparing guard-input-mutations (f6e376c) with main (3e10480)

Open in CodSpeed

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/conftest.py (1)

707-711: ⚡ Quick win

Consider isinstance for clarity.

While the match statement is correct, a simpler alternative would be more readable for this type check.

♻️ Simpler alternative
 def _tracks_mutation(value: object) -> bool:
-    match value:
-        case dict() | list():
-            return True
-    return False
+    return isinstance(value, (dict, list))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/conftest.py` around lines 707 - 711, The helper _tracks_mutation
currently uses a match statement for a simple type check; replace the match with
a direct isinstance check (e.g., return isinstance(value, (dict, list))) inside
the _tracks_mutation function to make the intent clearer and more concise while
preserving behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/conftest.py`:
- Around line 707-711: The helper _tracks_mutation currently uses a match
statement for a simple type check; replace the match with a direct isinstance
check (e.g., return isinstance(value, (dict, list))) inside the _tracks_mutation
function to make the intent clearer and more concise while preserving behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eb8dd47e-cb39-4af7-abd5-2d5192ffd440

📥 Commits

Reviewing files that changed from the base of the PR and between bcd0b3c and c22cf41.

📒 Files selected for processing (7)
  • src/datamodel_code_generator/parser/jsonschema.py
  • tests/conftest.py
  • tests/main/conftest.py
  • tests/main/jsonschema/test_main_jsonschema.py
  • tests/main/protobuf/test_main_protobuf.py
  • tests/main/test_main_general.py
  • tests/test_conftest_helpers.py

@koxudaxi koxudaxi force-pushed the guard-input-mutations branch from c22cf41 to c2745d0 Compare June 13, 2026 11:31
@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (3e10480) to head (f6e376c).

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #3394   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          138       138           
  Lines        29372     29426   +54     
  Branches      3512      3514    +2     
=========================================
+ Hits         29372     29426   +54     
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 Harness.
📢 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 force-pushed the guard-input-mutations branch 2 times, most recently from 84bebc1 to bed3861 Compare June 13, 2026 11:53
@koxudaxi koxudaxi force-pushed the guard-input-mutations branch from bed3861 to a4ea7ff Compare June 13, 2026 12:04
Comment thread src/datamodel_code_generator/parser/jsonschema.py Fixed
@koxudaxi koxudaxi merged commit 3225c02 into main Jun 13, 2026
52 checks passed
@koxudaxi koxudaxi deleted the guard-input-mutations branch June 13, 2026 14:26
@github-actions

Copy link
Copy Markdown
Contributor

Breaking Change Analysis

Result: No breaking changes detected

Reasoning: PR #3394 ("Guard mutable generation inputs") is a defensive internal refactor of the JsonSchemaObject parser class to avoid mutating caller-provided input dicts/cached parsed data. The only non-test source change is src/datamodel_code_generator/parser/jsonschema.py: it adds defensive copies (values = dict(values)), moves extra-field collection from init into a mode="before" validator that returns a new dict, replaces the custom init with model_post_init, adjusts validate_items to use None if values == {} else values (which preserves the same items: false / items: [] handling the old init workaround restored), and adds "self" to EXCLUDE_FIELD_KEYS while removing the now-redundant raw.pop("self"). No breaking changes were found: (1) No generated-code output changes — no existing expected fixtures were modified; all test diffs are new tests or additions of assert_input_unchanged=True asserting identical output. (2) No CLI option or public API signature changes — generate() is unchanged and JsonSchemaObject is an internal parser class. (3) The only behavior change is that caller inputs are no longer mutated in place, which is a bug fix that preserves generated output. (4) No Jinja2 template changes. (5) No Python version support dropped — the new match/case syntax is valid because requires-python is already >=3.10 and the diff does not modify it. (6) No error-handling changes.


This analysis was performed by Claude Code Action

@github-actions

Copy link
Copy Markdown
Contributor

🎉 Released in 0.64.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