Skip to content

Add --validators option for Pydantic v2 field validators#2906

Merged
koxudaxi merged 19 commits intomainfrom
feature/validators-option
Jan 2, 2026
Merged

Add --validators option for Pydantic v2 field validators#2906
koxudaxi merged 19 commits intomainfrom
feature/validators-option

Conversation

@koxudaxi
Copy link
Copy Markdown
Owner

@koxudaxi koxudaxi commented Jan 2, 2026

Fixes: #464

Summary by CodeRabbit

  • New Features

    • Added --validators CLI option to attach custom field validators for Pydantic v2 generated models
    • Supports validator modes: before, after, wrap, plain; validators may target single or multiple fields
  • Documentation

    • Added comprehensive docs and quick-reference entries with examples and configuration format
  • Tests

    • Added test coverage for validators (including multi-field, wrap mode, invalid inputs, and version gating)

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 2, 2026

Warning

Rate limit exceeded

@koxudaxi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 55 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 9d8793d and 233159c.

📒 Files selected for processing (4)
  • docs/validators.md
  • src/datamodel_code_generator/parser/base.py
  • tests/data/expected/main/jsonschema/field_validators_plain_mode.py
  • tests/main/jsonschema/test_main_jsonschema.py
📝 Walkthrough

Walkthrough

Adds a new --validators CLI option and runtime support for a validators JSON config that maps models to Pydantic v2 field validators; introduces validator types, config wiring through CLI→Config→Parser→Model, template-time processing to render @field_validator methods, and tests/docs.

Changes

Cohort / File(s) Summary
Documentation
docs/cli-reference/index.md, docs/cli-reference/quick-reference.md, docs/cli-reference/template-customization.md, docs/validators.md
Added --validators docs, option index entry, usage examples, JSON schema for validators, and mode descriptions (before/after/wrap/plain).
Core validators module
src/datamodel_code_generator/validators.py
New module: ValidatorMode, ValidatorDefinition, ModelValidators, and conditional ValidatorsConfig (RootModel) for Pydantic v2.
Type definitions
src/datamodel_code_generator/_types/generate_config_dict.py, src/datamodel_code_generator/_types/parser_config_dicts.py
Added validators key to Generate/Parser config TypedDicts and new TypedDicts for validator definitions/models.
CLI & config wiring
src/datamodel_code_generator/__main__.py, src/datamodel_code_generator/config.py, src/datamodel_code_generator/arguments.py, src/datamodel_code_generator/cli_options.py, src/datamodel_code_generator/prompt_data.py
New --validators arg, CLI metadata, Config/ParserConfig fields, _load_validators_config() to parse/validate JSON, and updated run/generate call signatures to pass validators mapping.
Parser integration
src/datamodel_code_generator/parser/base.py
Injects per-model validators into extra_template_data (JSON-serialized) for templates.
Pydantic v2 model generation
src/datamodel_code_generator/model/pydantic_v2/base_model.py, src/datamodel_code_generator/model/pydantic_v2/imports.py
New _process_validators() to prepare validator metadata, unique method names, and imports; added import constants for field_validator, ValidationInfo, and ValidatorFunctionWrapHandler.
Tests & expected outputs
tests/data/expected/.../field_validators*.py, tests/data/expected/main/input_model/config_class.py, tests/main/jsonschema/test_main_jsonschema.py, tests/main/test_public_api_signature_baseline.py, tests/test_infer_input_type.py
Added expected generated examples for single/multi-field/wrap validators, new tests (validators success/failure/version-guarded), updated API baseline and inference test exclusions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as __main__.py
    participant Config as Config/ParserConfig
    participant Parser as parser.base
    participant Model as pydantic_v2.BaseModel
    participant Template as Template Engine

    User->>CLI: run with --validators <file.json>
    CLI->>CLI: _load_validators_config(file)
    Note right of CLI: parse JSON -> ValidatorsConfig (Pydantic v2)
    CLI->>Config: pass validators mapping into run_generate_from_config
    Config->>Parser: init with validators in config
    Parser->>Parser: for each model, attach validators -> extra_template_data[model]
    Parser->>Model: hand off extra_template_data
    Model->>Model: _process_validators() -> prepare imports, method names, metadata
    Model->>Template: provide prepared_validators in template context
    Template-->>User: rendered model with `@field_validator` methods
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #2832: Modifies public API baseline to include validators parameter — closely related to baseline changes here.
  • PR #2844: Introduces/extends config TypedDicts (generate_config_dict / parser_config_dicts) that this PR further extends for validators.
  • PR #2795: Ensures extra_template_data propagation into model generation — relevant to how validators are injected.

Suggested labels

breaking-change-analyzed

Poem

🐰 I hopped a patch across the land,

JSON validators held in hand,
Pydantic v2 hears my cheer,
Field guards bound and drawing near,
Hop, validate — code blossoms grand.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.07% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add --validators option for Pydantic v2 field validators' clearly and concisely summarizes the main change: introducing a new CLI option for adding field validators to Pydantic v2 models.
Linked Issues check ✅ Passed The PR implements the core requirements from issue #464: enabling validator specification via configuration, supporting external validator function references, generating field_validator decorators, and providing a separate configuration pathway for validator definitions.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the --validators feature: CLI argument addition, configuration wiring, Pydantic v2 validator processing, documentation, and comprehensive test coverage for the feature.

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
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 2, 2026

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

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Jan 2, 2026

CodSpeed Performance Report

Merging #2906 will not alter performance

Comparing feature/validators-option (233159c) with main (8ee5860)

⚠️ 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
⏩ 98 skipped1

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.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.38%. Comparing base (8ee5860) to head (233159c).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #2906    +/-   ##
========================================
  Coverage   99.38%   99.38%            
========================================
  Files          92       93     +1     
  Lines       16400    16536   +136     
  Branches     1947     1959    +12     
========================================
+ Hits        16299    16435   +136     
  Misses         52       52            
  Partials       49       49            
Flag Coverage Δ
unittests 99.38% <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: 2

🧹 Nitpick comments (3)
src/datamodel_code_generator/validators.py (1)

28-34: Consider adding validation for field specification.

ValidatorDefinition allows both field and fields to be None. While the downstream _process_validators method handles this gracefully by skipping validators without fields, you could add a Pydantic model validator to catch this at configuration load time with a clearer error message.

🔎 Optional: Add model validator for early detection
 class ValidatorDefinition(BaseModel):
     """Definition of a single validator."""

     field: str | None = None
     fields: list[str] | None = None
     function: str
     mode: ValidatorMode = ValidatorMode.AFTER
+
+    @model_validator(mode='after')
+    def check_fields_specified(self) -> 'ValidatorDefinition':
+        if not self.field and not self.fields:
+            raise ValueError("At least one of 'field' or 'fields' must be specified")
+        return self
tests/main/jsonschema/test_main_jsonschema.py (2)

7655-7716: Validators happy‑path tests and CLI doc metadata are well‑structured

The new test_field_validators and test_field_validators_multi_fields cover the primary behaviors of --validators for Pydantic v2 (single and multi‑field validators), and using skip_code_validation=True is a pragmatic choice since generated validator callables may not exist in the test environment. Decorating with PYDANTIC_V2_SKIP avoids accidental failures when v2 isn’t installed.

One minor doc consistency nit: in the cli_doc metadata you use a fully qualified path (tests/data/jsonschema/field_validators_config.json), whereas other CLI docs typically use paths rooted at the test‑data subtree (e.g., aliases/..., default_values/...). If the docs tooling assumes a particular root, you may want to align this path with the existing convention; otherwise it’s fine to leave as‑is.


7719-7760: Error‑path coverage for invalid validators config is solid; consider encoding consistency

Both test_validators_invalid_json and test_validators_invalid_structure exercise the key failure modes for --validators (JSON parse error vs structurally invalid config) and assert on clear stderr messages with Exit.ERROR, which is exactly what’s needed.

For consistency with the rest of this file (and to avoid any locale‑dependent surprises), you might want to add encoding="utf-8" to the two Path.write_text(...) calls that create the temporary invalid JSON files, matching earlier tests that write temp inputs.

📜 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 3db6667 and 26a13fb.

⛔ Files ignored due to path filters (4)
  • src/datamodel_code_generator/model/template/pydantic_v2/BaseModel.jinja2 is excluded by none and included by none
  • tests/data/jsonschema/field_validators.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/field_validators_config.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/field_validators_multi_fields_config.json is excluded by !tests/data/**/*.json and included by none
📒 Files selected for processing (21)
  • docs/cli-reference/index.md
  • docs/cli-reference/quick-reference.md
  • docs/cli-reference/template-customization.md
  • docs/validators.md
  • src/datamodel_code_generator/__main__.py
  • src/datamodel_code_generator/_types/generate_config_dict.py
  • src/datamodel_code_generator/_types/parser_config_dicts.py
  • src/datamodel_code_generator/arguments.py
  • src/datamodel_code_generator/cli_options.py
  • src/datamodel_code_generator/config.py
  • src/datamodel_code_generator/model/pydantic_v2/base_model.py
  • src/datamodel_code_generator/model/pydantic_v2/imports.py
  • src/datamodel_code_generator/parser/base.py
  • src/datamodel_code_generator/prompt_data.py
  • src/datamodel_code_generator/validators.py
  • tests/data/expected/main/input_model/config_class.py
  • tests/data/expected/main/jsonschema/field_validators.py
  • tests/data/expected/main/jsonschema/field_validators_multi_fields.py
  • tests/main/jsonschema/test_main_jsonschema.py
  • tests/main/test_public_api_signature_baseline.py
  • tests/test_infer_input_type.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-25T09:23:08.506Z
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.

Applied to files:

  • src/datamodel_code_generator/__main__.py
🧬 Code graph analysis (11)
src/datamodel_code_generator/model/pydantic_v2/imports.py (1)
src/datamodel_code_generator/imports.py (2)
  • Import (20-38)
  • from_full_path (35-38)
tests/main/test_public_api_signature_baseline.py (1)
src/datamodel_code_generator/validators.py (1)
  • ModelValidators (37-40)
tests/data/expected/main/input_model/config_class.py (2)
src/datamodel_code_generator/validators.py (3)
  • ModelValidators (37-40)
  • ValidatorMode (19-25)
  • ValidatorDefinition (28-34)
src/datamodel_code_generator/_types/generate_config_dict.py (2)
  • ValidatorDefinition (173-177)
  • ModelValidatorsModel (180-181)
tests/main/jsonschema/test_main_jsonschema.py (3)
src/datamodel_code_generator/util.py (1)
  • is_pydantic_v2 (52-57)
tests/main/conftest.py (2)
  • output_file (99-101)
  • run_main_and_assert (245-409)
src/datamodel_code_generator/__main__.py (1)
  • Exit (129-135)
tests/data/expected/main/jsonschema/field_validators.py (4)
src/datamodel_code_generator/reference.py (1)
  • validate_name (1158-1160)
src/datamodel_code_generator/model/pydantic_v2/base_model.py (1)
  • BaseModel (203-441)
src/datamodel_code_generator/util.py (1)
  • field_validator (175-191)
tests/data/expected/main/jsonschema/field_validators_multi_fields.py (1)
  • User (12-20)
src/datamodel_code_generator/model/pydantic_v2/base_model.py (2)
src/datamodel_code_generator/imports.py (3)
  • Import (20-38)
  • append (74-89)
  • from_full_path (35-38)
src/datamodel_code_generator/reference.py (4)
  • reference (77-79)
  • ModelResolver (511-1217)
  • get (1019-1021)
  • add (931-1017)
src/datamodel_code_generator/_types/generate_config_dict.py (2)
src/datamodel_code_generator/validators.py (3)
  • ModelValidators (37-40)
  • ValidatorMode (19-25)
  • ValidatorDefinition (28-34)
tests/data/expected/main/input_model/config_class.py (2)
  • ValidatorDefinition (251-255)
  • ModelValidatorsModel (258-259)
src/datamodel_code_generator/config.py (1)
src/datamodel_code_generator/validators.py (1)
  • ModelValidators (37-40)
src/datamodel_code_generator/parser/base.py (1)
src/datamodel_code_generator/util.py (1)
  • model_dump (254-258)
src/datamodel_code_generator/__main__.py (1)
src/datamodel_code_generator/validators.py (2)
  • ValidatorsConfig (46-47)
  • ModelValidators (37-40)
tests/data/expected/main/jsonschema/field_validators_multi_fields.py (4)
src/datamodel_code_generator/model/pydantic_v2/base_model.py (1)
  • BaseModel (203-441)
src/datamodel_code_generator/util.py (1)
  • field_validator (175-191)
tests/data/expected/main/jsonschema/field_validators.py (1)
  • User (12-25)
src/datamodel_code_generator/model/base.py (1)
  • name (828-830)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/config.py

49-49: Unused noqa directive (non-enabled: TC001)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/__main__.py

42-42: Unused noqa directive (non-enabled: TC003)

Remove unused noqa directive

(RUF100)


507-507: Unused noqa directive (non-enabled: UP045)

Remove unused noqa directive

(RUF100)


1293-1293: Unused noqa directive (non-enabled: T201)

Remove unused noqa directive

(RUF100)

🔇 Additional comments (42)
src/datamodel_code_generator/prompt_data.py (1)

145-145: LGTM!

The new --validators option description is clear, concise, and correctly placed in alphabetical order.

tests/data/expected/main/jsonschema/field_validators_multi_fields.py (1)

1-20: LGTM!

The generated test fixture correctly demonstrates multi-field validator syntax for Pydantic v2. The validator properly delegates to an external function and follows the expected decorator ordering (@field_validator@classmethod).

src/datamodel_code_generator/validators.py (1)

43-51: LGTM!

The runtime version check for ValidatorsConfig is appropriate. The pattern correctly handles Pydantic v1/v2 compatibility by setting the config to None when RootModel is unavailable, which aligns with the v2-only validators feature scope.

tests/data/expected/main/jsonschema/field_validators.py (1)

1-25: LGTM!

The generated test fixture correctly demonstrates both before and after validator modes for Pydantic v2. The naming convention (validate_name_validator, validate_email_validator) and external function delegation are consistent with the generator's implementation in _process_validators.

src/datamodel_code_generator/cli_options.py (1)

214-214: LGTM!

The --validators option metadata is correctly categorized under OptionCategory.TEMPLATE, which aligns with other template customization options like --extra-template-data and --class-decorators.

docs/cli-reference/index.md (2)

15-15: LGTM!

Template Customization option count correctly updated to 19 to reflect the new --validators option.


222-222: LGTM!

The --validators documentation link is correctly placed in alphabetical order and follows the established pattern for CLI reference entries.

tests/test_infer_input_type.py (1)

48-49: LGTM!

The validator configuration files are correctly excluded from JSON Schema inference tests since they contain validator configuration data rather than JSON Schema definitions.

src/datamodel_code_generator/_types/parser_config_dicts.py (3)

33-40: LGTM! Type definitions align with validators module.

The new ValidatorDefinitionDict and imports correctly mirror the structure from the validators module, enabling type-safe configuration of field validators across the codebase.


54-54: LGTM! ParserConfigDict correctly extended with validators support.

The validators field addition enables propagation of validator configuration through the parser initialization flow.


183-184: LGTM! ModelValidatorsDict provides clean structure for config files.

This TypedDict enables structured validators configuration in JSON/YAML files with proper type hints.

docs/cli-reference/quick-reference.md (2)

153-153: LGTM! CLI option documented in Template Customization category.

The --validators option is correctly categorized and documented with clear description indicating Pydantic v2 specificity.


351-351: LGTM! Alphabetical index entry added.

The alphabetical index correctly references the --validators option for easy lookup.

tests/main/test_public_api_signature_baseline.py (3)

54-54: LGTM! Import added for type checking.

The ModelValidators import correctly supports type annotations in the baseline signatures.


73-73: LGTM! Baseline signature updated for backward compatibility testing.

The validators parameter addition with None default ensures the public API surface is tested for compatibility.


209-209: LGTM! Parser baseline signature updated.

The validators parameter in the Parser baseline ensures backward compatibility testing for parser initialization.

tests/data/expected/main/input_model/config_class.py (4)

12-12: LGTM! Import reflects expected generated code.

The ModelValidators import in the test expectation file correctly represents how the feature should appear in generated configuration code.


114-114: LGTM! ValidatorMode type alias defined.

The Literal type alias for ValidatorMode matches the expected generated output structure.


130-130: LGTM! validators field added to GenerateConfig.

The validators field in the test expectation correctly demonstrates the config structure with proper typing.


251-259: LGTM! Validator TypedDict definitions match expected structure.

The ValidatorDefinition and ModelValidatorsModel TypedDicts correctly represent the expected generated output for validators configuration.

src/datamodel_code_generator/config.py (3)

91-91: LGTM! GenerateConfig extended with validators field.

The validators field addition enables per-model validator configuration with proper typing and backward-compatible None default.


233-233: LGTM! ParserConfig extended with validators field.

The validators field addition in ParserConfig mirrors the GenerateConfig structure, enabling validator configuration propagation through parser initialization.


49-49: No action needed—the TC001 suppression is correct.

ModelValidators is used in type annotations (lines 91, 233) that Pydantic evaluates at runtime. The noqa: TC001 directive properly suppresses the type-checking-only-import warning because the import is required at runtime. This pattern is consistently applied throughout the codebase (lines 40, 47, and other files) and is the correct approach for Pydantic usage.

src/datamodel_code_generator/arguments.py (1)

879-884: --validators option wiring looks consistent

The new --validators Path argument is placed in the right group, has a clear help string, and matches the style of other JSON-based template options. No issues from the CLI side.

src/datamodel_code_generator/model/pydantic_v2/imports.py (1)

23-25: New Pydantic v2 import constants align with existing pattern

The added IMPORT_FIELD_VALIDATOR, IMPORT_VALIDATION_INFO, and IMPORT_VALIDATOR_FUNCTION_WRAP_HANDLER follow the same Import.from_full_path pattern as the other Pydantic v2 symbols and are appropriate for the validator feature.

Please confirm that all three names are available from the top-level pydantic package in the minimum Pydantic v2 version you support, so these imports don’t break older 2.x environments.

docs/cli-reference/template-customization.md (2)

24-24: Option index entry for --validators is clear and correctly linked

The new row integrates well with the existing options table and points to the right #validators anchor, with a concise description tailored to Pydantic v2 models.


2696-2772: --validators section is consistent with behavior and other docs

The description, usage example, and generated output for --validators align with the JSON config format and Pydantic v2 validator usage documented elsewhere. The cross‑link to “Field Validators” keeps duplication minimal and discoverability good.

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

15-34: LGTM! Imports are well-organized.

The imports for validator support are appropriately added and correctly structured with the conditional imports for different validator modes.


300-301: LGTM! Appropriate placement of validator processing.

The _process_validators() call is correctly positioned at the end of __init__, after all other initialization and config processing is complete.


351-399: Well-implemented validator processing logic.

The implementation correctly handles:

  • Multiple fields per validator
  • Unique method name generation via ModelResolver
  • Conditional imports based on validator modes per Pydantic v2 semantics
  • Both simple function names (e.g., "my_validator") and fully-qualified paths (e.g., "module.my_validator")

The ValidationInfo import exclusion for plain mode (line 396-397) aligns with Pydantic v2 behavior: plain validators do not receive ValidationInfo, only wrap mode does.

src/datamodel_code_generator/_types/generate_config_dict.py (3)

38-38: LGTM! Correct import placement.

The imports are correctly placed within the TYPE_CHECKING block for runtime efficiency.


54-54: LGTM! The validators field is appropriately typed.

The Mapping[str, ModelValidators] type correctly represents the model-name-to-validators mapping structure.


173-181: LGTM! TypedDict definitions correctly mirror the Pydantic models.

The ValidatorDefinition and ModelValidatorsModel TypedDicts provide static type checking for dict-based configurations, complementing the Pydantic models used for runtime validation. The NotRequired wrappers correctly reflect the optional nature of field, fields, and mode.

tests/main/jsonschema/test_main_jsonschema.py (1)

27-45: Pydantic v2 skip marker wiring looks good

Importing is_pydantic_v2 and centralizing the skip condition in PYDANTIC_V2_SKIP mirrors the existing skip-marker pattern and cleanly scopes these tests to environments that actually have Pydantic v2. No issues from a correctness or maintainability perspective.

src/datamodel_code_generator/__main__.py (8)

42-42: LGTM! Import organization is correct.

The imports are properly organized:

  • Mapping is correctly imported at runtime (not under TYPE_CHECKING) for Pydantic's needs
  • ValidationError is used in the _load_validators_config function
  • ModelValidators is appropriately placed under TYPE_CHECKING as it's only used in type annotations

The static analysis hint about unused noqa: TC003 on line 42 can be safely ignored—the comment indicates this is intentional for Pydantic compatibility.

Also applies to: 49-49, 96-96, 103-105


183-185: LGTM! Config field addition follows existing patterns.

The validators field is correctly integrated:

  • Added to the field_validator decorator alongside other file-based configuration fields
  • Type annotation matches the pattern used for similar fields (Optional[TextIOBase])
  • The validate_file validator will handle path resolution and file opening consistently

Also applies to: 507-507


898-923: LGTM! Validators config loading is well-implemented.

The _load_validators_config helper function correctly:

  • Checks for Pydantic v2 availability before using ValidatorsConfig
  • Provides a clear, actionable error message when Pydantic v2 is required
  • Handles JSON parsing and validation errors with informative messages
  • Accesses .root on the ValidatorsConfig RootModel to extract the underlying dict

The error handling is comprehensive and follows the patterns established in the codebase.


934-934: LGTM! Parameter addition is well-typed.

The validators parameter is correctly added with:

  • Appropriate immutable type (Mapping[str, ModelValidators] | None)
  • Sensible position in the function signature
  • Optional nature matching the feature's optional usage

1064-1064: LGTM! Parameter propagation is complete.

The validators parameter is correctly passed through to the generate() function, completing the configuration pathway from CLI/config file to code generation.


1291-1294: LGTM! Validators config loading follows established patterns.

The validators configuration loading is properly integrated into the main flow:

  • Uses the new _load_validators_config helper function
  • Error handling is consistent with other configuration loading (aliases, default_values, etc.)
  • Errors are reported to stderr with appropriate exit code

1340-1340: LGTM! Validators config integration is complete.

The loaded validators_config is correctly passed to run_generate_from_config, completing the integration pathway from configuration file to code generation logic.


42-42: Remove this comment. The noqa directives are intentional and correct.

Line 42's noqa: TC003 has an explicit comment explaining it: "pydantic needs it"—Pydantic requires runtime access to these types despite them being imported from collections.abc.

Line 507's noqa: UP045 and the ~100+ similar suppressions throughout the codebase represent a deliberate choice to use Optional[X] syntax rather than X | None, consistent with the project's established pattern of preserving such directives despite RUF100 warnings.

Comment thread docs/validators.md
Comment thread src/datamodel_code_generator/parser/base.py
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: 2

🧹 Nitpick comments (2)
src/datamodel_code_generator/model/pydantic_v2/base_model.py (1)

370-373: Consider clarifying the fields extraction logic.

The current logic handles both "fields" (list) and "field" (single value) by using or with list filtering. While functional, it could be more explicit about the expected structure.

🔎 Alternative approach for clarity
-        fields = validator.get("fields") or [validator.get("field")]
-        fields = [f for f in fields if f]
+        # Support both "fields" (list) and "field" (single string)
+        fields_value = validator.get("fields") or validator.get("field")
+        if not fields_value:
+            continue
+        fields = fields_value if isinstance(fields_value, list) else [fields_value]
-        if not fields:
-            continue
tests/main/jsonschema/test_main_jsonschema.py (1)

7700-7778: Good coverage of validator config variants; minor helper-usage nit

These three tests nicely cover multi-field validators, wrap mode, and skipping entries without a field key, and the string checks on the generated file verify the intended behavior without over-coupling to full golden files.

If you want tighter consistency with the rest of the suite, you could optionally rewrite test_field_validators_with_no_field_skipped to use run_main_and_assert(..., skip_code_validation=True) plus explicit output inspection, but the current run_main_with_args approach is perfectly serviceable.

📜 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 26a13fb and 9d8793d.

📒 Files selected for processing (4)
  • src/datamodel_code_generator/model/pydantic_v2/base_model.py
  • src/datamodel_code_generator/parser/base.py
  • tests/data/expected/main/jsonschema/field_validators_wrap_mode.py
  • tests/main/jsonschema/test_main_jsonschema.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/datamodel_code_generator/parser/base.py
🧰 Additional context used
🧬 Code graph analysis (3)
tests/data/expected/main/jsonschema/field_validators_wrap_mode.py (2)
src/datamodel_code_generator/model/pydantic_v2/base_model.py (1)
  • BaseModel (213-447)
src/datamodel_code_generator/util.py (1)
  • field_validator (175-191)
src/datamodel_code_generator/model/pydantic_v2/base_model.py (3)
src/datamodel_code_generator/model/pydantic/base_model.py (2)
  • imports (268-276)
  • validator (86-89)
src/datamodel_code_generator/imports.py (3)
  • Import (20-38)
  • append (74-89)
  • from_full_path (35-38)
src/datamodel_code_generator/reference.py (3)
  • reference (77-79)
  • ModelResolver (511-1217)
  • add (931-1017)
tests/main/jsonschema/test_main_jsonschema.py (3)
src/datamodel_code_generator/util.py (1)
  • is_pydantic_v2 (52-57)
tests/main/conftest.py (2)
  • output_file (99-101)
  • run_main_and_assert (245-409)
src/datamodel_code_generator/__main__.py (1)
  • Exit (129-135)
⏰ 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: benchmarks
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.12 on macOS
  • GitHub Check: 3.13 on macOS
  • GitHub Check: py312-isort5 on Ubuntu
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.10 on macOS
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.14 on macOS
  • GitHub Check: Analyze (python)
🔇 Additional comments (10)
src/datamodel_code_generator/model/pydantic_v2/base_model.py (4)

15-15: LGTM! Import additions support the validator feature.

The new imports (IMPORT_ANY, IMPORT_FIELD_VALIDATOR, IMPORT_VALIDATION_INFO, IMPORT_VALIDATOR_FUNCTION_WRAP_HANDLER, ModelResolver) are all used appropriately in the _process_validators method to support Pydantic v2 field validators.

Also applies to: 27-34


310-311: LGTM! Appropriate placement of validator processing.

Calling _process_validators() at the end of __init__ ensures validators are processed after configuration setup and before template rendering.


361-365: LGTM! Method structure is sound.

The method signature and early-return pattern are appropriate for processing optional validator configurations.


396-406: LGTM! Conditional import logic correctly matches Pydantic v2 validator signatures.

The code appropriately adds imports based on detected validator modes:

  • IMPORT_VALIDATION_INFO is added for non-plain modes (which receive ValidationInfo)
  • IMPORT_VALIDATOR_FUNCTION_WRAP_HANDLER is added only for wrap mode

This aligns with Pydantic v2's validator function signatures.

tests/data/expected/main/jsonschema/field_validators_wrap_mode.py (3)

19-22: LGTM! Model fields are correctly defined.

The field definitions follow Pydantic v2 conventions with appropriate type constraints and the modern union syntax.


1-3: Standard generated file header.

The header correctly identifies the source schema file, which is helpful for tracing the origin of generated code.


24-29: Wrap mode field validator is correctly implemented.

The validator follows Pydantic v2 conventions:

  • Correct decorator order (@field_validator before @classmethod)
  • Proper signature with handler: ValidatorFunctionWrapHandler and info: ValidationInfo
  • Appropriate delegation to external validation logic

All required imports are present and types are correct for wrap mode operation.

tests/main/jsonschema/test_main_jsonschema.py (3)

27-27: Pydantic version gating helpers are correctly wired

Using is_pydantic_v2() in PYDANTIC_V2_SKIP / PYDANTIC_V1_ONLY gives the right behavior for enabling v2-only tests and v1-only tests, and matches the existing *_SKIP marker pattern in this module.

Also applies to: 45-46


7654-7697: Solid happy-path coverage for --validators including CLI docs

The test_field_validators setup cleanly exercises the new --validators option end-to-end (including @cli_doc metadata), and skip_code_validation=True is an appropriate way to avoid depending on real validator implementations while still asserting the golden output.


7786-7847: Error handling around --validators is well exercised

The invalid-JSON and invalid-structure tests correctly assert both non-OK exits and clear diagnostics, and test_validators_requires_pydantic_v2 (gated by PYDANTIC_V1_ONLY) nicely locks in the requirement that --validators only be used with Pydantic v2.

Comment thread src/datamodel_code_generator/model/pydantic_v2/base_model.py
Comment thread src/datamodel_code_generator/model/pydantic_v2/base_model.py
@koxudaxi koxudaxi merged commit a2b2756 into main Jan 2, 2026
39 checks passed
@koxudaxi koxudaxi deleted the feature/validators-option branch January 2, 2026 20:34
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 2, 2026

Breaking Change Analysis

Result: No breaking changes detected

Reasoning: This PR adds a new opt-in feature (--validators CLI option) for Pydantic v2 field validators without changing any existing behavior. Key observations: (1) The new --validators option is purely additive - existing commands continue to work identically. (2) Default behavior is unchanged - validators are only generated when the option is explicitly used. (3) Template changes use conditional rendering ({% if prepared_validators %}) so existing custom templates continue to work. (4) The Python API gains a new optional validators parameter with a default of None, maintaining backward compatibility. (5) No changes to default values, error handling for existing features, or Python version requirements. All changes are opt-in enhancements that don't affect users who don't use the new feature.


This analysis was performed by Claude Code Action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 3, 2026

🎉 Released in 0.52.1

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.

Support for adding validator decorator

1 participant