Add --class-decorators option for custom model decorators#2760
Add --class-decorators option for custom model decorators#2760
Conversation
WalkthroughAdds a new Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant CLI as CLI (--class-decorators)
participant Config as Config (validated)
participant Generate as generate()
participant Parser as Parser (base / subclass)
participant DataModel as DataModelFactory/_create_data_model
CLI->>Config: parse args (comma-separated string)
Note right of Config `#DFF2E1`: validate_class_decorators\nsplit, trim, add '@'
Config->>Generate: run_generate_from_config(config)
Generate->>Parser: instantiate(..., class_decorators=list)
Parser->>DataModel: _create_data_model(..., **kwargs)
alt no explicit decorators in kwargs
Parser->>DataModel: inject decorators=self.class_decorators
end
DataModel-->>Generate: DataModel source with decorators applied
Generate-->>CLI: write generated files
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ 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 |
🤖 Generated by GitHub Actions
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2760 +/- ##
=======================================
Coverage 99.47% 99.47%
=======================================
Files 85 85
Lines 12573 12627 +54
Branches 1500 1506 +6
=======================================
+ Hits 12507 12561 +54
Misses 35 35
Partials 31 31
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:
|
CodSpeed Performance ReportMerging #2760 will not alter performanceComparing Summary
Footnotes
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (6)
src/datamodel_code_generator/arguments.py (1)
722-729: New--class-decoratorsCLI option definition looks consistentArgument wiring (name, help text, type, default) matches existing patterns like
--additional-importsand the docs description; no functional issues from this definition itself. If you ever reshuffle groups for CLI help readability, consider moving both--additional-importsand--class-decoratorsintotemplate_options, but that’s purely cosmetic.src/datamodel_code_generator/parser/base.py (1)
689-689: Parser now exposesclass_decoratorscleanlyAdding the
class_decoratorskw-only parameter and storing it onselfis straightforward and keeps the API symmetric withadditional_imports. One minor defensive improvement would be to copy the list to avoid accidental external mutation:self.class_decorators: list[str] = list(class_decorators) if class_decorators else []Not required, but reduces aliasing surprises if callers reuse the same list instance.
Also applies to: 808-809
docs/class-decorators.md (1)
1-116: Good end‑to‑end documentation of--class-decoratorsThe examples and usage notes (including
@prefix handling and--additional-importsinterplay) accurately reflect the feature and tests.You might optionally:
- Rename the bold labels like
**schema.json**/**Generated model.py**to proper sub‑headings to satisfy MD036.- Change “type checking decorators” to “type‑checking decorators” for the LanguageTool nit.
src/datamodel_code_generator/__main__.py (1)
241-255: Config normalization and wiring forclass_decoratorslooks good; consider small robustness and lint tweaks
- The
validate_class_decoratorsroot validator correctly:
- Accepts a comma‑separated string.
- Trims entries.
- Skips empty entries.
- Adds
@when missing, matching the CLI tests and docs.- The
Config.class_decoratorsfield and therun_generate_from_configcall intogenerate(class_decorators=...)complete the CLI→Config→generate wiring.Two minor follow‑ups you might consider:
Handle list input defensively
If someone setsclass_decoratorsas a list inpyproject.tomlor passes a dict intoConfig.parse_objdirectly,class_decorators.split(",")will raise. You could mirroradditional_importsbut make it tolerant of bothstrandSequence[str]:Suggested more robust validator
@model_validator(mode="before") -def validate_class_decorators(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805 - """Validate and split class decorators, adding @ prefix if missing.""" - class_decorators = values.get("class_decorators") - if class_decorators is not None: - decorators = [] - for raw_decorator in class_decorators.split(","): - stripped = raw_decorator.strip() - if stripped: - if not stripped.startswith("@"): - stripped = f"@{stripped}" - decorators.append(stripped) - values["class_decorators"] = decorators - return values +def validate_class_decorators(cls, values: dict[str, Any]) -> dict[str, Any]: + """Normalize class_decorators to a list of '@'-prefixed strings.""" + raw = values.get("class_decorators") + if raw is None: + return values + + if isinstance(raw, str): + raw_items = raw.split(",") + elif isinstance(raw, (list, tuple)): + raw_items = raw + else: + # Leave unexpected types untouched and let pydantic report type errors. + return values + + decorators: list[str] = [] + for item in raw_items: + stripped = str(item).strip() + if not stripped: + continue + if not stripped.startswith("@"): + stripped = f"@{stripped}" + decorators.append(stripped) + + values["class_decorators"] = decorators + return valuesClean up new unused
noqacomments flagged by Ruff
SinceN805/UP045aren’t enabled, the new# noqacomments on the validator signature andclass_decoratorsfield can be dropped to satisfy RUF100, or you can enable those checks globally if you want to keep them.If you want to double‑check how
class_decoratorsis expected to be represented inpyproject.toml(string vs list), please confirm against your existingadditional_imports/custom_formattersdocs and usage.Also applies to: 400-401, 710-712
tests/main/test_main_general.py (1)
191-285: Integration coverage for--class-decoratorsis solid
test_class_decoratorsverifies the programmaticgenerate(..., class_decorators=[...], additional_imports=[...])path against the golden file.- CLI tests exercise:
- Basic
--class-decoratorswiring,- Auto‑adding the
@prefix,- Ignoring empty entries in a comma‑separated list.
This gives good end‑to‑end confidence for both CLI and API usage. If you want even more assurance later, an additional CLI test with multiple decorators could validate ordering, but it’s not strictly necessary given current parser tests.
src/datamodel_code_generator/__init__.py (1)
417-417: Top‑levelgenerate()API now correctly exposesclass_decoratorsAdding
class_decoratorsas a kw‑only arg and threading it through to the parser constructor cleanly exposes the new feature to library users without breaking existing callers.You might optionally mirror the CLI behavior by normalizing entries here as well (ensure leading
@and trim whitespace) so that programmatic callers can safely pass plain decorator names, but the current contract is clear and consistent if you document thatgenerate()expects fully‑formed decorator strings.Also applies to: 672-673
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
docs/class-decorators.mddocs/cli-reference/index.mddocs/cli-reference/quick-reference.mddocs/cli-reference/template-customization.mdsrc/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/__main__.pysrc/datamodel_code_generator/arguments.pysrc/datamodel_code_generator/cli_options.pysrc/datamodel_code_generator/parser/base.pysrc/datamodel_code_generator/parser/graphql.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/parser/openapi.pytests/data/expected/main/class_decorators_dataclass.pytests/main/test_main_general.pytests/parser/test_graphql.pytests/parser/test_jsonschema.py
🧰 Additional context used
🧬 Code graph analysis (4)
tests/parser/test_graphql.py (2)
src/datamodel_code_generator/parser/graphql.py (1)
GraphQLParser(73-699)src/datamodel_code_generator/model/dataclass.py (1)
DataClass(46-117)
tests/parser/test_jsonschema.py (2)
tests/main/test_main_general.py (1)
test_class_decorators(192-203)src/datamodel_code_generator/parser/jsonschema.py (1)
JsonSchemaParser(518-3265)
tests/data/expected/main/class_decorators_dataclass.py (1)
src/datamodel_code_generator/model/base.py (1)
name(730-732)
src/datamodel_code_generator/__main__.py (1)
src/datamodel_code_generator/util.py (4)
model_validator(57-62)model_validator(66-72)model_validator(76-80)model_validator(83-114)
🪛 LanguageTool
docs/class-decorators.md
[grammar] ~106-~106: Use a hyphen to join words.
Context: ...havior - TypedDict: Add runtime type checking decorators - msgspec.Struct...
(QB_NEW_EN_HYPHEN)
🪛 markdownlint-cli2 (0.18.1)
docs/class-decorators.md
15-15: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
37-37: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
65-65: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py
242-242: Unused noqa directive (non-enabled: N805)
Remove unused noqa directive
(RUF100)
400-400: Unused noqa directive (non-enabled: UP045)
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). (9)
- GitHub Check: 3.11 on Ubuntu
- GitHub Check: py312-isort5 on Ubuntu
- GitHub Check: 3.10 on macOS
- GitHub Check: py312-black24 on Ubuntu
- GitHub Check: 3.12 on Windows
- GitHub Check: 3.11 on Windows
- GitHub Check: 3.10 on Windows
- GitHub Check: Analyze (python)
- GitHub Check: benchmarks
🔇 Additional comments (12)
docs/cli-reference/quick-reference.md (1)
112-116: Quick-reference wiring for--class-decoratorsis consistentThe new entries under “Template Customization” and in the alphabetical index are correctly linked and categorized; they match the detailed template-customization docs and the CLI metadata.
Also applies to: 188-189
src/datamodel_code_generator/cli_options.py (1)
175-183: CLI metadata for--class-decoratorsis correctly registered
CLI_OPTION_METAnow includes--class-decoratorsunder the TEMPLATE category, which aligns with how the docs present it and with the argparse definition; this should keep the sync tests and generated docs happy.docs/cli-reference/template-customization.md (1)
5-9:--class-decoratorsdocumentation is clear and aligned with behaviorThe new option row, cross-reference from
--additional-imports, and the dedicated--class-decoratorssection (usage plus dataclasses_json example) accurately describe how to apply custom decorators and how it interacts with imports and--output-model-type. This should be sufficient for users to implement the Issue #2358 workflow without extra post-processing.Also applies to: 36-37, 113-182
tests/parser/test_graphql.py (1)
95-106: GraphQL parser test appropriately coversclass_decoratorspropagationThe new test mirrors the existing
_create_data_modeldataclass-arguments test and cleanly verifies that parser-levelclass_decoratorsare applied to the resultingDataClass. This gives good, focused coverage of the new behavior with minimal coupling.docs/cli-reference/index.md (1)
15-15: CLI index wiring for--class-decoratorslooks consistentTemplate Customization count and the new
--class-decoratorsentry/anchor are correctly updated and aligned with existing patterns.Also applies to: 43-43
tests/data/expected/main/class_decorators_dataclass.py (1)
1-17: Golden dataclass fixture matches intended decorator outputImport and decorator ordering (
@dataclass_jsonabove@dataclass) look correct and are consistent with the new tests.tests/parser/test_jsonschema.py (1)
474-489: Parser tests correctly pinclass_decoratorsbehaviorThese tests clearly establish that:
class_decoratorsis stored as‑is when provided.- The default is an empty list rather than
Nonewhen omitted.That matches the intended parser API and will catch regressions in base‐parser initialization.
src/datamodel_code_generator/parser/openapi.py (1)
192-193: OpenAPI parser now correctly participates inclass_decoratorsflowAdding
class_decoratorsto the OpenAPIParser constructor and forwarding it tosuper().__init__is consistent with the other parsers and keeps the signature backward‑compatible.Also applies to: 298-299
src/datamodel_code_generator/parser/graphql.py (2)
112-112: LGTM! Clean parameter propagation.The
class_decoratorsparameter is correctly added to the__init__signature and properly forwarded to the base class. The type annotationlist[str] | None = Noneis appropriate, and this follows the standard pattern used by other parameters in this parser.Also applies to: 214-214
361-363: LGTM! Solid decorator injection logic.The implementation correctly:
- Checks if decorators aren't already provided in kwargs (respecting explicit parameters)
- Verifies
self.class_decoratorsis truthy (handles bothNoneand empty list cases)- Creates a defensive copy with
list(self.class_decorators)to prevent mutation issuesThis ensures that class-level decorators from the parser configuration are applied to generated models when no explicit decorators are specified.
src/datamodel_code_generator/parser/jsonschema.py (2)
534-534: LGTM! Consistent with GraphQL parser.The
class_decoratorsparameter follows the same pattern as the GraphQL parser implementation, maintaining consistency across the codebase. Parameter addition and forwarding to the base class are implemented correctly.Also applies to: 637-637
1755-1757: LGTM! Decorator injection mirrors GraphQL parser implementation.The
_create_data_modelmethod correctly injects decorators using the same defensive pattern as the GraphQL parser:
- Respects explicit
decoratorsin kwargs (explicit trumps implicit)- Guards against
Noneand empty list with the truthy check- Creates a protective copy to prevent shared mutable state
The consistency between parsers ensures uniform behavior when applying class decorators across different schema types.
548df39 to
bf60ea8
Compare
bf60ea8 to
e5bc109
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/datamodel_code_generator/__main__.py (2)
241-254: Consider removing unused noqa directive.The static analysis tool indicates that the
noqa: N805directive on line 242 is unused. This suggests the linting rule it's meant to suppress is not enabled in your configuration.🔎 Suggested cleanup
@model_validator(mode="before") - def validate_class_decorators(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805 + def validate_class_decorators(cls, values: dict[str, Any]) -> dict[str, Any]: """Validate and split class decorators, adding @ prefix if missing."""
401-401: Consider removing unused noqa directive.The static analysis tool indicates that the
noqa: UP045directive on line 401 is unused. This suggests the rule it's meant to suppress is not enabled in your configuration.🔎 Suggested cleanup
- class_decorators: Optional[list[str]] = None # noqa: UP045 + class_decorators: Optional[list[str]] = None
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
docs/cli-reference/index.mddocs/cli-reference/quick-reference.mdsrc/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/__main__.pysrc/datamodel_code_generator/arguments.pysrc/datamodel_code_generator/cli_options.pysrc/datamodel_code_generator/parser/base.pysrc/datamodel_code_generator/parser/graphql.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/parser/openapi.pytests/data/expected/main/class_decorators_dataclasses_dataclass.pytests/data/expected/main/class_decorators_msgspec_Struct.pytests/data/expected/main/class_decorators_pydantic_BaseModel.pytests/data/expected/main/class_decorators_pydantic_v2_BaseModel.pytests/data/expected/main/class_decorators_pydantic_v2_dataclass.pytests/main/test_main_general.pytests/parser/test_jsonschema.py
🚧 Files skipped from review as they are similar to previous changes (8)
- docs/cli-reference/quick-reference.md
- tests/parser/test_jsonschema.py
- tests/main/test_main_general.py
- src/datamodel_code_generator/parser/base.py
- src/datamodel_code_generator/parser/openapi.py
- src/datamodel_code_generator/cli_options.py
- src/datamodel_code_generator/arguments.py
- src/datamodel_code_generator/init.py
🧰 Additional context used
🧬 Code graph analysis (5)
tests/data/expected/main/class_decorators_pydantic_BaseModel.py (2)
tests/data/expected/main/class_decorators_dataclasses_dataclass.py (1)
User(14-17)tests/data/expected/main/class_decorators_pydantic_v2_BaseModel.py (1)
User(12-15)
tests/data/expected/main/class_decorators_pydantic_v2_dataclass.py (5)
tests/data/expected/main/class_decorators_dataclasses_dataclass.py (1)
User(14-17)tests/data/expected/main/class_decorators_msgspec_Struct.py (1)
User(12-15)tests/data/expected/main/class_decorators_pydantic_BaseModel.py (1)
User(12-15)tests/data/expected/main/class_decorators_pydantic_v2_BaseModel.py (1)
User(12-15)src/datamodel_code_generator/model/base.py (1)
name(730-732)
tests/data/expected/main/class_decorators_msgspec_Struct.py (4)
tests/data/expected/main/class_decorators_dataclasses_dataclass.py (1)
User(14-17)tests/data/expected/main/class_decorators_pydantic_BaseModel.py (1)
User(12-15)tests/data/expected/main/class_decorators_pydantic_v2_BaseModel.py (1)
User(12-15)tests/data/expected/main/class_decorators_pydantic_v2_dataclass.py (1)
User(14-17)
tests/data/expected/main/class_decorators_dataclasses_dataclass.py (4)
tests/data/expected/main/class_decorators_msgspec_Struct.py (1)
User(12-15)tests/data/expected/main/class_decorators_pydantic_BaseModel.py (1)
User(12-15)tests/data/expected/main/class_decorators_pydantic_v2_BaseModel.py (1)
User(12-15)tests/data/expected/main/class_decorators_pydantic_v2_dataclass.py (1)
User(14-17)
src/datamodel_code_generator/__main__.py (1)
src/datamodel_code_generator/util.py (4)
model_validator(57-62)model_validator(66-72)model_validator(76-80)model_validator(83-114)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py
242-242: Unused noqa directive (non-enabled: N805)
Remove unused noqa directive
(RUF100)
401-401: Unused noqa directive (non-enabled: UP045)
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-black24 on Ubuntu
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: 3.10 on Ubuntu
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.12 on Windows
- GitHub Check: 3.13 on Ubuntu
- GitHub Check: 3.14 on Windows
- GitHub Check: 3.13 on Windows
- GitHub Check: 3.11 on Windows
- GitHub Check: 3.14 on Ubuntu
- GitHub Check: benchmarks
- GitHub Check: Analyze (python)
🔇 Additional comments (12)
docs/cli-reference/index.md (2)
15-15: Template Customization option count correctly incremented.The count has been properly updated from 16 to 17 to reflect the addition of the new
--class-decoratorsoption.
44-44: New option correctly positioned in alphabetical index.The
--class-decoratorsoption is properly placed in alphabetical order between--checkand--class-name, and the link format is consistent with all other options in the index. The#class-decoratorsanchor section exists intemplate-customization.md.tests/data/expected/main/class_decorators_msgspec_Struct.py (1)
1-15: Test fixture looks correct for msgspec.Struct with decorator.The expected output properly demonstrates the class decorator feature applied to msgspec models. The decorator placement and imports are correct.
tests/data/expected/main/class_decorators_dataclasses_dataclass.py (1)
1-17: Test fixture looks correct for standard dataclass with decorator.The expected output properly demonstrates the class decorator feature applied to standard Python dataclasses. The decorator ordering (@my_decorator before @DataClass) is correct.
src/datamodel_code_generator/__main__.py (1)
695-817: Proper integration of class_decorators into generation pipeline.The class_decorators configuration is correctly validated, stored, and propagated through to the generate() function. The validator properly handles comma-separated input, strips whitespace, filters empty entries, and ensures the @ prefix is present.
src/datamodel_code_generator/parser/graphql.py (2)
100-218: Clean integration of class_decorators parameter.The class_decorators parameter is properly added to the GraphQLParser constructor and forwarded to the base class. The type hint and default value are appropriate.
365-389: Decorator injection logic is well-implemented.The _create_data_model method correctly injects class decorators when not explicitly provided in kwargs. Creating a copy of the decorators list avoids potential mutation issues.
src/datamodel_code_generator/parser/jsonschema.py (2)
525-642: Clean integration of class_decorators parameter.The class_decorators parameter is properly added to the JsonSchemaParser constructor and forwarded to the base class, consistent with the GraphQLParser implementation.
1763-1787: Decorator injection logic is consistent and correct.The _create_data_model method follows the same pattern as GraphQLParser, ensuring consistent behavior across different parser implementations.
tests/data/expected/main/class_decorators_pydantic_v2_dataclass.py (1)
1-17: Test fixture looks correct for Pydantic v2 dataclass with decorator.The expected output properly demonstrates the class decorator feature applied to Pydantic v2 dataclass models. The imports and decorator placement are correct.
tests/data/expected/main/class_decorators_pydantic_v2_BaseModel.py (1)
1-15: Test fixture looks correct for Pydantic v2 BaseModel with decorator.The expected output properly demonstrates the class decorator feature applied to Pydantic v2 BaseModel classes. The decorator is correctly placed before the class definition.
tests/data/expected/main/class_decorators_pydantic_BaseModel.py (1)
1-15: Test fixture looks correct for Pydantic BaseModel with decorator.The expected output properly demonstrates the class decorator feature applied to Pydantic BaseModel classes. This fixture complements the v2 variant to ensure compatibility across Pydantic versions.
Breaking Change AnalysisResult: No breaking changes detected Reasoning: This PR adds a new optional
The PR follows proper additive patterns: new optional CLI argument, new optional Python API parameter with default This analysis was performed by Claude Code Action |
* Add --class-decorators option for custom model decorators * docs: update CLI reference documentation 🤖 Generated by GitHub Actions * Add test for empty entries in class-decorators * Add parameterized e2e test for class-decorators across all output types --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* Add --generate-prompt option for LLM consultation * docs: update CLI reference documentation and prompt data 🤖 Generated by GitHub Actions * Add --class-decorators option for custom model decorators (#2760) * Add --class-decorators option for custom model decorators * docs: update CLI reference documentation 🤖 Generated by GitHub Actions * Add test for empty entries in class-decorators * Add parameterized e2e test for class-decorators across all output types --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * Merge main and update expected files * Replace expected file comparisons with structural assertions * docs: update CLI reference documentation and prompt data 🤖 Generated by GitHub Actions * Add usage examples for --generate-prompt (pipe to LLM tools, clipboard) * docs: update CLI reference documentation and prompt data 🤖 Generated by GitHub Actions * Fix escape sequence handling in truncation logic * Add --generate-prompt manual docs with CLI tools and clipboard examples * Add llm-integration.md with detailed docs, simplify cli-reference * Fix CLI tool flags and link paths in LLM integration docs * Add LLM Integration to navigation menu * Remove redundant paste instructions * Add LLM integration to Common Recipes section * Move LLM recipe to top, remove snake-case recipe * Update CI/CD recipe to show GitHub Action example * Add script to update GitHub Action version in docs during CI build * Fix grammar: type checking -> type-checking * Fix ruff errors: remove shebang, fix line length --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Fixes: #2358
Summary by CodeRabbit
New Features
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.