Add --class-name-prefix, --class-name-suffix, and --class-name-affix-scope options#2885
Add --class-name-prefix, --class-name-suffix, and --class-name-affix-scope options#2885
Conversation
📝 WalkthroughWalkthroughExpose ClassNameAffixScope and add configurable class-name affixing: prefix, suffix, and affix-scope options; wire new config/CLI fields through GenerateConfig, Parser, ModelResolver, and generators; update typing, tests, and docs. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant CLI
participant Config
participant Parser
participant ModelResolver
participant Generator
Note over CLI,Config: User supplies --class-name-prefix/--class-name-suffix/--class-name-affix-scope
CLI->>Config: parse options -> GenerateConfig
Config->>Parser: init parser (with affix settings)
Parser->>ModelResolver: init ModelResolver(class_name_prefix/suffix/affix_scope,...)
Parser->>ModelResolver: add/create Reference (include model_type "model"|"enum")
ModelResolver-->>ModelResolver: determine is_root & scope -> decide skip_affix
alt affix applies
ModelResolver->>ModelResolver: apply affix to name
end
ModelResolver->>Generator: provide resolved (affixed & unique) class names
Generator->>Generator: emit generated files with affixed class names
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (1)
🧰 Additional context used🧠 Learnings (2)📚 Learning: 2025-12-25T09:22:22.481ZApplied to files:
📚 Learning: 2025-12-18T13:43:16.235ZApplied to files:
🧬 Code graph analysis (2)src/datamodel_code_generator/__main__.py (2)
src/datamodel_code_generator/reference.py (1)
🪛 Ruff (0.14.10)src/datamodel_code_generator/__main__.py517-517: Unused Remove unused (RUF100) 518-518: Unused Remove unused (RUF100) src/datamodel_code_generator/reference.py549-549: Unused Remove unused (RUF100) 1036-1036: Unused Remove unused (RUF100) 1082-1082: Unused Remove unused (RUF100) 1085-1085: Unused Remove unused (RUF100) 1087-1087: Unused Remove unused (RUF100) 1090-1090: Unused Remove unused (RUF100) 1091-1091: Unused Remove unused (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). (1)
🔇 Additional comments (13)
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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
tests/data/expected/main/jsonschema/class_name_affix_scope_models/output.py (1)
22-24: Consider detecting and avoiding redundant affixes.The generated class name
ModelSchemaSchemaresults from applying the suffix "Schema" to a base name that already contains "Schema". While this is technically correct behavior, it creates awkward naming that could confuse users.Consider enhancing the generator to detect when an affix is already present in the class name and skip application or emit a warning. This would improve the user experience and prevent naming like
ModelSchemaSchema.Would you like me to open an issue to track this enhancement, or would you prefer to implement smarter affix detection logic in the current PR?
tests/main/jsonschema/test_main_jsonschema.py (1)
707-768: Suffix and affix-scope=models tests are correctly wired; consider enums scope coverageThe
--class-name-suffixand--class-name-affix-scope modelstests mirror established patterns (cli_doc metadata, fixtures, and arguments) and should robustly cover model-only affix behavior.If enums-only behavior (
--class-name-affix-scope enums) isn’t exercised elsewhere, consider adding a small JSON Schema test for that mode to catch regressions in enum naming.src/datamodel_code_generator/reference.py (2)
1040-1096: Clean implementation of affix application logic.The scope-based affix application is well-structured:
_apply_class_name_affixcorrectly checks scope conditionsget_affixed_nameprovides a clean API for GraphQL with appropriate documentationThe
is_enum = model_type == "enum"check relies on string comparison. Consider using an enum or constant formodel_typevalues to prevent typos and improve type safety.🔎 Optional: Use constants or enum for model_type values
# At module level or in a constants file MODEL_TYPE_MODEL = "model" MODEL_TYPE_ENUM = "enum" # Then in _apply_class_name_affix: is_enum = model_type == MODEL_TYPE_ENUMThis would prevent potential typos like
"models"or"Model"from silently failing.
1098-1151: Well-structured transformation pipeline with clear ordering.The transformation order (generator → singularization → affix → uniqueness) is correct, and the
skip_affixparameter properly supports deferred affix application.The inline comments explaining
reserved_namebehavior in theskip_generator_and_affixbranch are helpful for maintainability.Regarding static analysis hints: multiple
noqadirectives are flagged as unused (lines 1098, 1101, 1103, 1106, 1107). Consider removing them if the corresponding rules aren't enabled.#!/bin/bash # Verify if PLR0913, PLR0917, FBT001, FBT002 rules are enabled fd -t f '(pyproject\.toml|ruff\.toml|\.ruff\.toml)$' --exec grep -l 'select\|extend-select' {} \; | xargs cat 2>/dev/null | grep -E '(PLR|FBT)'
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/data/jsonschema/class_name_affix.jsonis excluded by!tests/data/**/*.jsonand included by none
📒 Files selected for processing (22)
src/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/__main__.pysrc/datamodel_code_generator/_types/generate_config_dict.pysrc/datamodel_code_generator/_types/graphql_parser_config_dict.pysrc/datamodel_code_generator/_types/jsonschema_parser_config_dict.pysrc/datamodel_code_generator/_types/openapi_parser_config_dict.pysrc/datamodel_code_generator/_types/parser_config_dict.pysrc/datamodel_code_generator/arguments.pysrc/datamodel_code_generator/cli_options.pysrc/datamodel_code_generator/config.pysrc/datamodel_code_generator/enums.pysrc/datamodel_code_generator/parser/base.pysrc/datamodel_code_generator/parser/graphql.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/reference.pytests/data/expected/main/input_model/config_class.pytests/data/expected/main/jsonschema/class_name_affix_scope_models/output.pytests/data/expected/main/jsonschema/class_name_prefix/output.pytests/data/expected/main/jsonschema/class_name_suffix/output.pytests/data/expected/main/jsonschema/class_name_suffix_with_class_name/output.pytests/main/jsonschema/test_main_jsonschema.pytests/main/test_public_api_signature_baseline.py
🧰 Additional context used
🧬 Code graph analysis (17)
src/datamodel_code_generator/_types/jsonschema_parser_config_dict.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
src/datamodel_code_generator/_types/graphql_parser_config_dict.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
src/datamodel_code_generator/parser/graphql.py (1)
src/datamodel_code_generator/reference.py (2)
get_affixed_name(1074-1096)Reference(142-201)
src/datamodel_code_generator/_types/parser_config_dict.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
tests/main/test_public_api_signature_baseline.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
tests/data/expected/main/jsonschema/class_name_affix_scope_models/output.py (2)
tests/data/expected/main/jsonschema/class_name_suffix/output.py (1)
ItemSchema(17-19)tests/data/expected/main/jsonschema/class_name_suffix_with_class_name/output.py (1)
ItemSchema(17-19)
tests/data/expected/main/input_model/config_class.py (2)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)src/datamodel_code_generator/model/type_alias.py (1)
TypeAlias(37-42)
src/datamodel_code_generator/_types/openapi_parser_config_dict.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
tests/data/expected/main/jsonschema/class_name_prefix/output.py (2)
src/datamodel_code_generator/model/enum.py (1)
Enum(39-121)src/datamodel_code_generator/model/base.py (1)
name(827-829)
tests/main/jsonschema/test_main_jsonschema.py (3)
tests/conftest.py (1)
freeze_time(352-355)tests/test_main_kr.py (1)
output_file(44-46)tests/main/conftest.py (2)
output_file(98-100)run_main_and_assert(244-408)
src/datamodel_code_generator/parser/base.py (1)
src/datamodel_code_generator/model/base.py (2)
class_name(848-850)class_name(853-857)
src/datamodel_code_generator/__init__.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
src/datamodel_code_generator/arguments.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
src/datamodel_code_generator/reference.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
src/datamodel_code_generator/__main__.py (2)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)src/datamodel_code_generator/util.py (1)
field_validator(175-191)
src/datamodel_code_generator/config.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
src/datamodel_code_generator/_types/generate_config_dict.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/reference.py
549-549: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
550-550: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1044-1044: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1098-1098: Unused noqa directive (non-enabled: PLR0913, PLR0917)
Remove unused noqa directive
(RUF100)
1101-1101: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1103-1103: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1106-1106: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1107-1107: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/__main__.py
517-517: Unused noqa directive (non-enabled: UP045)
Remove unused noqa directive
(RUF100)
518-518: Unused noqa directive (non-enabled: UP045)
Remove unused noqa directive
(RUF100)
🔇 Additional comments (43)
src/datamodel_code_generator/enums.py (1)
131-142: LGTM! Clear and well-documented enum.The
ClassNameAffixScopeenum is well-structured with clear documentation explaining each scope option. The implementation follows existing patterns in the codebase.tests/data/expected/main/jsonschema/class_name_suffix_with_class_name/output.py (1)
1-24: LGTM! Test expectation correctly demonstrates suffix behavior with explicit root name.The generated output correctly shows that when an explicit root class name (
MyRootModel) is provided, the suffix is applied to non-root classes (StatusSchema,ItemSchema) but not to the explicitly named root model.tests/data/expected/main/jsonschema/class_name_suffix/output.py (1)
1-24: LGTM! Test expectation correctly demonstrates consistent suffix application.The generated output correctly shows the "Schema" suffix applied consistently to all classes (enum and models) when using the default affix scope.
tests/data/expected/main/jsonschema/class_name_affix_scope_models/output.py (1)
12-14: LGTM! Enum correctly excludes suffix with "models" scope.The
Statusenum correctly has no suffix applied, demonstrating that theclass_name_affix_scope="models"setting properly excludes enum classes.tests/data/expected/main/jsonschema/class_name_prefix/output.py (1)
1-24: LGTM! Test expectation correctly demonstrates prefix application.The generated output correctly shows the "Api" prefix applied consistently to all classes (enums and models), demonstrating the class name prefix feature working as intended.
src/datamodel_code_generator/parser/jsonschema.py (3)
3152-3152: LGTM!Correctly passes
model_type="enum"to enable scope-based affix application for enum references. This aligns with the newClassNameAffixScopefeature that distinguishes between model and enum classes.
3223-3223: LGTM!Consistent with the other enum reference creation sites in this method.
3237-3237: LGTM!Correctly handles the nullable enum wrapper case with the same
model_type="enum"parameter.src/datamodel_code_generator/_types/jsonschema_parser_config_dict.py (2)
18-18: LGTM!Import correctly placed within the
TYPE_CHECKINGblock alongside other enum imports fromdatamodel_code_generator.enums.
59-61: LGTM!New configuration fields are appropriately typed and placed logically after
class_name. The typing is consistent with other parser config TypedDicts mentioned in the AI summary.tests/main/test_public_api_signature_baseline.py (3)
19-19: LGTM!Import correctly placed at module level (not under
TYPE_CHECKING) sinceClassNameAffixScope.Allis used as a runtime default value.
88-90: LGTM!Baseline parameters correctly reflect the new public API surface. The default
ClassNameAffixScope.Allmatches the expected behavior documented in the enum definition.
217-219: LGTM!Parser baseline signature is consistent with the generate function baseline, ensuring API parity is tested correctly.
src/datamodel_code_generator/parser/graphql.py (1)
150-158: LGTM!The implementation correctly:
- Determines
model_typebased on GraphQLTypeKindfor scope-aware affixing- Uses
get_affixed_name()which is the dedicated GraphQL API per the relevant code snippet (no uniqueness checks, no singularization)- Preserves
original_name=type_.namefor internal lookups while exposing the affixed name viaReference.name- Keys
self.referencesbytype_.name(original name), which is correct since all lookups (e.g., line 370) use original GraphQL type namesThis cleanly separates internal naming from generated output naming.
src/datamodel_code_generator/_types/generate_config_dict.py (1)
69-71: LGTM!The new fields are properly typed with
NotRequiredfor backward compatibility. Note thatclass_name_affix_scopeis typed asNotRequired[ClassNameAffixScope](without| None), which correctly requires a valid enum value when the field is provided.src/datamodel_code_generator/__init__.py (1)
38-38: LGTM!The
ClassNameAffixScopeenum is correctly imported and added to the public API exports.src/datamodel_code_generator/cli_options.py (1)
81-83: LGTM!The new CLI options are properly documented with appropriate metadata. The
MODELcategory placement is correct, and the positioning after--class-nameis logical.src/datamodel_code_generator/_types/graphql_parser_config_dict.py (1)
59-61: LGTM!The new fields are consistent with the changes in
generate_config_dict.pyand properly extend the parser configuration for GraphQL.src/datamodel_code_generator/arguments.py (2)
22-22: LGTM!The import of
ClassNameAffixScopeis correctly placed and necessary for the new CLI option.
202-224: Well-documented CLI options with clear semantics.The new arguments are properly structured with comprehensive help text. The documented behavior—"Does not apply to root model when --class-name is specified"—is correctly implemented:
skip_affix_for_rootis set toTruewhen--class-nameis provided, and the_apply_class_name_affix()method correctly skips affixing when bothis_rootandskip_affix_for_rootare true.tests/data/expected/main/input_model/config_class.py (2)
28-30: LGTM!The
ClassNameAffixScopeTypeAlias correctly represents the enum values as a Literal type, consistent with the enum definition.
142-144: LGTM!The new fields are correctly typed with
NotRequiredfor backward compatibility and match the configuration schema.src/datamodel_code_generator/_types/openapi_parser_config_dict.py (2)
18-18: LGTM!The import is correctly placed within the
TYPE_CHECKINGblock for type annotation purposes.
60-62: LGTM!The new configuration fields are properly typed and consistent with other config definitions across the codebase.
src/datamodel_code_generator/_types/parser_config_dict.py (2)
18-18: LGTM!Import is correctly placed in the
TYPE_CHECKINGblock.
59-61: LGTM!The configuration fields are properly defined with consistent typing across all parser config dictionaries.
src/datamodel_code_generator/config.py (3)
18-18: LGTM!The import is correctly placed at runtime (not in
TYPE_CHECKING) since it's used for the default valueClassNameAffixScope.All.
107-109: LGTM!The fields are properly typed with sensible defaults. The default of
ClassNameAffixScope.Allaligns with the enum's documentation indicating "Apply to all classes including enums (default)."
242-244: LGTM!The fields are consistently defined with the same defaults as in
GenerateConfig, ensuring coherent configuration across the parser pipeline.src/datamodel_code_generator/__main__.py (5)
57-57: LGTM!The import of
ClassNameAffixScopeis correctly added and properly used throughout the file for configuration and validation.
399-407: LGTM!The validator correctly handles type coercion for the
class_name_affix_scopefield, converting None to the default value and strings to the enum type. The implementation follows established patterns in this codebase.
475-483: LGTM!The Pydantic v1 validator correctly mirrors the v2 implementation, ensuring consistent behavior across both Pydantic versions. The duplication is necessary for compatibility.
896-898: LGTM!The new configuration fields are correctly passed through to the
generate()function, completing the configuration pipeline. The placement next to the existingclass_nameparameter is logical and maintains good code organization.
517-519: Clean up unusednoqadirectives.The new configuration fields are correctly added with appropriate types and defaults. However, the
# noqa: UP045directives on lines 517-518 are unused according to static analysis.🔎 Suggested cleanup
- class_name_prefix: Optional[str] = None # noqa: UP045 - class_name_suffix: Optional[str] = None # noqa: UP045 + class_name_prefix: Optional[str] = None + class_name_suffix: Optional[str] = None class_name_affix_scope: ClassNameAffixScope = ClassNameAffixScope.All⛔ Skipped due to learnings
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: 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.tests/main/jsonschema/test_main_jsonschema.py (2)
677-705: Prefix option CLI doc and test wiring look consistentThe
--class-name-prefixcli_doc metadata, use offreeze_time, andrun_main_and_assertarguments (schema path, expected fixture path, and extra_args) all match existing JSON Schema tests in this module. No changes needed here.
771-785: Root-model override behavior with --class-name is clearly specified and testedThis test precisely captures the contract that an explicit
--class-nameprevents the suffix from being applied to the root model, using a dedicated expected fixture and frozen timestamp. The setup is consistent with surrounding tests.src/datamodel_code_generator/reference.py (7)
38-38: LGTM!Import of
ClassNameAffixScopeis correctly placed with other imports from the same package.
597-602: LGTM!Configuration storage is clean with sensible defaults. Empty strings for prefix/suffix and
ClassNameAffixScope.Allas the default scope are appropriate.
831-837: LGTM!The deferred affix logic is correct. Since
add_refdoesn't know themodel_typeyet, it appropriately skips affix application when scope-dependent, delegating it toadd()where the type is known.
939-958: Well-designed deferred affix reprocessing logic.The
is_rootdetection andneeds_reprocesslogic correctly handle the case whereadd_ref()created a reference without affix (due to unknownmodel_type), and nowadd()can apply the correct affix.One edge case to consider: a path like
"file.json#/"would havesplit("#")[-1]return"/", and"/" not in "/"isFalse, so it wouldn't be detected as root. This seems intentional (trailing slash indicates a path), but worth confirming this matches expected behavior.
974-992: LGTM!Both code paths consistently pass
model_typeandis_roottoget_class_name().
1147-1147: LGTM!Passing
model_typeto_get_unique_nameenables type-specific duplicate name suffixes, which integrates well with the overall affix feature.
546-551: Thenoqa: FBT001, FBT002directives on lines 549-550 are necessary and should be kept.The project's linter configuration enables
FBTrules for non-test files (vialint.select = ["ALL"]with no global FBT ignore, only per-file ignores fortests/**/*.py). The noqa directives suppress legitimate FBT001/FBT002 warnings for boolean parameters with default values—a consistent pattern throughout this file and the codebase.Likely an incorrect or invalid review comment.
9d1c3fb to
7038df8
Compare
🤖 Generated by GitHub Actions
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/datamodel_code_generator/reference.py (1)
1105-1158: Remove unusednoqadirectives; transformation order is correct.The logic correctly handles:
- Transformation order: generator → singularization → affix → reserved name check → uniqueness
- Proper separation of
skip_generator_and_affixpath (no reserved_name check) vs. normal path- Conditional affix application with
skip_affixparameterHowever, multiple unused
noqadirectives should be removed (lines 1105, 1108, 1110, 1113, 1114).🔎 Proposed fix
- def get_class_name( # noqa: PLR0913, PLR0917 + def get_class_name( self, name: str, - unique: bool = True, # noqa: FBT001, FBT002 + unique: bool = True, reserved_name: str | None = None, - singular_name: bool = False, # noqa: FBT001, FBT002 + singular_name: bool = False, singular_name_suffix: str | None = None, model_type: str = "model", - is_root: bool = False, # noqa: FBT001, FBT002 - skip_affix: bool = False, # noqa: FBT001, FBT002 + is_root: bool = False, + skip_affix: bool = False, ) -> ClassName:
🧹 Nitpick comments (3)
tests/main/jsonschema/test_main_jsonschema.py (2)
738-769: Consider adding explicit coverage for--class-name-affix-scope=enumsYou exercise the default
allscope (prefix/suffix tests) andmodelsscope here; the newenumsscope is only verified implicitly via implementation. A small additional golden-output test with--class-name-affix-scope enums(showing only enums renamed while models stay unchanged) would close the matrix and protect against regressions in scope handling.
771-812: Tighten invalid prefix/suffix tests by asserting error messages / using shared helperThe invalid prefix/suffix tests correctly assert
Exit.ERROR, but they don't validate the error message or reuserun_main_and_assertlike the nearby invalid model name tests. To make these failures more robust and consistent, consider either:
- switching to
run_main_and_assert(..., expected_exit=Exit.ERROR, expected_stderr_contains=...), or- capturing
capsysand asserting the specific validation message.
This keeps the tests resilient to unrelated exit paths that might also returnExit.ERROR.src/datamodel_code_generator/reference.py (1)
597-610: Consider validating before assignment for cleaner initialization.The validation logic is correct, but prefix/suffix are assigned to instance variables (lines 598-599) before validation (lines 601-606). If validation fails, the instance is briefly in an inconsistent state. While this doesn't cause issues in practice (since
__init__raises and the instance is discarded), validating before assignment is cleaner.🔎 Proposed refactor
- # Class name affix configuration with validation - self.class_name_prefix: str = class_name_prefix or "" - self.class_name_suffix: str = class_name_suffix or "" - # Validate prefix/suffix are valid identifier components - if self.class_name_prefix and not self.class_name_prefix.isidentifier(): - msg = f"--class-name-prefix '{self.class_name_prefix}' is not a valid Python identifier" + # Class name affix configuration with validation + prefix = class_name_prefix or "" + suffix = class_name_suffix or "" + # Validate prefix/suffix are valid identifier components + if prefix and not prefix.isidentifier(): + msg = f"--class-name-prefix '{prefix}' is not a valid Python identifier" raise ValueError(msg) - if self.class_name_suffix and not (f"A{self.class_name_suffix}").isidentifier(): - msg = f"--class-name-suffix '{self.class_name_suffix}' is not a valid Python identifier component" + if suffix and not (f"A{suffix}").isidentifier(): + msg = f"--class-name-suffix '{suffix}' is not a valid Python identifier component" raise ValueError(msg) + self.class_name_prefix: str = prefix + self.class_name_suffix: str = suffix self.class_name_affix_scope: ClassNameAffixScope = class_name_affix_scope or ClassNameAffixScope.All self.skip_affix_for_root: bool = skip_affix_for_root self.skip_generator_and_affix: bool = skip_generator_and_affix
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/datamodel_code_generator/reference.pytests/main/jsonschema/test_main_jsonschema.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/datamodel_code_generator/reference.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/reference.py
549-549: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
550-550: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1051-1051: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1105-1105: Unused noqa directive (non-enabled: PLR0913, PLR0917)
Remove unused noqa directive
(RUF100)
1108-1108: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1110-1110: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1113-1113: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1114-1114: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
🔇 Additional comments (7)
tests/main/jsonschema/test_main_jsonschema.py (1)
677-769: Well-integrated CLI doc + golden-output coverage for new class-name affix optionsThe three tests for
--class-name-prefix,--class-name-suffix, and--class-name-affix-scope=modelsfollow existing patterns (cli_doc metadata, golden outputs undermain/jsonschema/..., and@freeze_timefor deterministic headers) and give solid end-to-end coverage of the new options, including interaction with enums via the sharedclass_name_affix.jsonfixture. Nice job threading these through the same harness used by other CLI options.src/datamodel_code_generator/reference.py (6)
38-38: LGTM: Import is correctly placed.The import of
ClassNameAffixScopeis necessary for the new affix scope functionality and is appropriately positioned with other package imports.
838-844: LGTM: Deferred affix logic is correct.The logic to skip affix application in
add_refwhen scope is notAllis correct. Sinceadd_refis called before the model type is known, deferring affix application untiladd()is called (withmodel_type) ensures correct scope-based filtering.
942-999: LGTM: Root detection and reprocessing logic are correct.The changes to the
add()method properly handle:
- Root path detection (lines 946-948) with correct logic for identifying root schemas
- Smart early return that reprocesses references when scope-dependent affixes need to be applied with the now-known
model_type(lines 953-966)- Consistent propagation of
model_typeandis_roottoget_class_name()(lines 987-999)
1081-1103: LGTM: GraphQL-specific method is correctly implemented.The
get_affixed_name()method correctly provides a simplified path for GraphQL parser with appropriate handling:
- Skips uniqueness and singularization (GraphQL manages its own naming)
- Hardcodes
is_root=False(appropriate since GraphQL types aren't JSON Schema roots)- Clear documentation of GraphQL-specific behavior
1160-1160: LGTM: Model type parameter enables type-specific duplicate suffixes.Adding the
model_typeparameter to_get_unique_name()is necessary to support type-specific duplicate name suffixes viaduplicate_name_suffix_map.
546-550: Remove unusednoqadirectives.Static analysis correctly identifies that the
# noqa: FBT001, FBT002directives on lines 549-550 are unused and should be removed.🔎 Proposed fix
class_name_prefix: str | None = None, class_name_suffix: str | None = None, class_name_affix_scope: ClassNameAffixScope | None = None, - skip_affix_for_root: bool = False, # noqa: FBT001, FBT002 - skip_generator_and_affix: bool = False, # noqa: FBT001, FBT002 + skip_affix_for_root: bool = False, + skip_generator_and_affix: bool = False,⛔ Skipped due to learnings
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.
CodSpeed Performance ReportMerging #2885 will not alter performanceComparing
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2885 +/- ##
=======================================
Coverage 99.37% 99.37%
=======================================
Files 92 92
Lines 16046 16121 +75
Branches 1893 1898 +5
=======================================
+ Hits 15945 16020 +75
Misses 52 52
Partials 49 49
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:
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/datamodel_code_generator/reference.py (1)
1047-1079: Scope-based affix logic is correct, but remove unused noqa directive.The implementation correctly applies affixes based on scope and root status:
All: applies to both models and enumsModels: applies only to non-enum classesEnums: applies only to enum classes- Root models respect
skip_affix_for_rootflagHowever, the
# noqa: FBT001, FBT002directive on line 1051 is unused and should be removed.🔎 Proposed fix
def _apply_class_name_affix( self, name: str, model_type: str = "model", - is_root: bool = False, # noqa: FBT001, FBT002 + is_root: bool = False, ) -> str:
🧹 Nitpick comments (2)
src/datamodel_code_generator/parser/jsonschema.py (1)
3152-3152: LGTM! All enum reference creation paths correctly categorized.The addition of
model_type="enum"to all three enum reference creation paths ensures that enums can be properly identified for selective class name affixing based on the newClassNameAffixScopefeature. This covers:
- Empty nullable enums (line 3152)
- Standard enums (line 3223)
- Nullable enum wrappers (line 3237)
Consider extracting the string literal
"enum"to a named constant to avoid magic strings and improve maintainability:# At module or class level MODEL_TYPE_ENUM = "enum" # Then use: model_type=MODEL_TYPE_ENUMThis would make the code more maintainable if model type categorization is expanded in the future.
Also applies to: 3223-3223, 3237-3237
tests/main/jsonschema/test_main_jsonschema.py (1)
677-811: New class-name affix tests are well-targeted; consider a bit more coverage.The new tests cover prefix, suffix, scoped application to models, interaction with
--class-name, and basic validation of invalid affixes, which should guard the new CLI surface well. Two small follow-ups you might consider (non-blocking):
- Add a JSON Schema + test case for
--class-name-affix-scope enumsto exercise that branch explicitly, similar totest_main_class_name_affix_scope_models.- For the invalid prefix/suffix tests, optionally assert on stderr via
run_main_and_assert(..., expected_exit=Exit.ERROR, expected_stderr_contains=...)for stronger guarantees about the user-facing error message, and to stay stylistically consistent with the nearby invalid--class-nametests.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/data/jsonschema/class_name_affix.jsonis excluded by!tests/data/**/*.jsonand included by none
📒 Files selected for processing (23)
docs/cli-reference/index.mddocs/cli-reference/model-customization.mddocs/cli-reference/quick-reference.mdsrc/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/__main__.pysrc/datamodel_code_generator/_types/generate_config_dict.pysrc/datamodel_code_generator/_types/parser_config_dicts.pysrc/datamodel_code_generator/arguments.pysrc/datamodel_code_generator/cli_options.pysrc/datamodel_code_generator/config.pysrc/datamodel_code_generator/enums.pysrc/datamodel_code_generator/parser/base.pysrc/datamodel_code_generator/parser/graphql.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/prompt_data.pysrc/datamodel_code_generator/reference.pytests/data/expected/main/input_model/config_class.pytests/data/expected/main/jsonschema/class_name_affix_scope_models/output.pytests/data/expected/main/jsonschema/class_name_prefix/output.pytests/data/expected/main/jsonschema/class_name_suffix/output.pytests/data/expected/main/jsonschema/class_name_suffix_with_class_name/output.pytests/main/jsonschema/test_main_jsonschema.pytests/main/test_public_api_signature_baseline.py
🚧 Files skipped from review as they are similar to previous changes (6)
- src/datamodel_code_generator/cli_options.py
- src/datamodel_code_generator/arguments.py
- tests/data/expected/main/jsonschema/class_name_prefix/output.py
- src/datamodel_code_generator/enums.py
- tests/data/expected/main/jsonschema/class_name_affix_scope_models/output.py
- tests/data/expected/main/jsonschema/class_name_suffix_with_class_name/output.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-25T09:22:22.481Z
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.
Applied to files:
src/datamodel_code_generator/reference.py
📚 Learning: 2025-12-18T13:43:16.235Z
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.
Applied to files:
src/datamodel_code_generator/reference.py
🧬 Code graph analysis (11)
src/datamodel_code_generator/_types/parser_config_dicts.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
src/datamodel_code_generator/parser/base.py (1)
src/datamodel_code_generator/model/base.py (2)
class_name(848-850)class_name(853-857)
src/datamodel_code_generator/__main__.py (2)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)src/datamodel_code_generator/util.py (1)
field_validator(175-191)
src/datamodel_code_generator/__init__.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
tests/data/expected/main/input_model/config_class.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
src/datamodel_code_generator/config.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
tests/main/jsonschema/test_main_jsonschema.py (2)
tests/main/conftest.py (2)
output_file(98-100)run_main_and_assert(244-408)src/datamodel_code_generator/__main__.py (2)
Exit(126-132)main(1000-1310)
tests/main/test_public_api_signature_baseline.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
src/datamodel_code_generator/reference.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
src/datamodel_code_generator/_types/generate_config_dict.py (1)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)
tests/data/expected/main/jsonschema/class_name_suffix/output.py (3)
src/datamodel_code_generator/model/enum.py (1)
Enum(39-121)tests/data/expected/main/jsonschema/class_name_suffix_with_class_name/output.py (2)
StatusSchema(12-14)ItemSchema(17-19)tests/data/expected/main/jsonschema/class_name_affix_scope_models/output.py (1)
ItemSchema(17-19)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py
517-517: Unused noqa directive (non-enabled: UP045)
Remove unused noqa directive
(RUF100)
518-518: Unused noqa directive (non-enabled: UP045)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/reference.py
549-549: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
550-550: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1051-1051: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1105-1105: Unused noqa directive (non-enabled: PLR0913, PLR0917)
Remove unused noqa directive
(RUF100)
1108-1108: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1110-1110: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1113-1113: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1114-1114: Unused noqa directive (non-enabled: FBT001, FBT002)
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). (1)
- GitHub Check: combine coverage
🔇 Additional comments (27)
docs/cli-reference/quick-reference.md (2)
95-97: New options correctly integrated into Model Customization table.The three options are properly placed in alphabetical order after
--class-nameand follow the table formatting convention. Descriptions are appropriately concise for the quick reference format.
215-217: New options correctly added to alphabetical index (section C).The options are properly ordered and link to the correct anchors in model-customization.md.
docs/cli-reference/model-customization.md (4)
12-14: Three new options properly documented in options table.The options are correctly placed between
--class-nameand--collapse-reuse-modelsin alphabetical order, maintaining table consistency.
1252-1332: --class-name-affix-scope documentation is comprehensive and clear.The section includes proper markdown anchor, clear description of the three scopes (all/models/enums), related options cross-references, practical usage example, and example schema with output showing correct behavior (Status enum unchanged, ItemSchema model affected when scope='models').
1334-1412: --class-name-prefix documentation is comprehensive and well-structured.The section includes all standard elements: anchor, description, related options, usage example, and input/output examples demonstrating prefix application to both models and enums. Examples show correct transformation (Item → ApiItem, Status → ApiStatus, Model → ApiModel).
1414-1492: --class-name-suffix documentation is comprehensive and well-structured.The section follows the same pattern as prefix documentation, with clear examples showing suffix application to both models and enums (Item → ItemSchema, Status → StatusSchema, Model → ModelSchema).
docs/cli-reference/index.md (2)
14-14: Model Customization option count correctly updated.The count has been increased from 38 to 39 options, properly reflecting the addition of three new options. This aligns with the actual documented options in the Model Customization section.
47-49: New options correctly added to alphabetical index section C.The three options are properly ordered alphabetically and include correct links to the model-customization.md anchors.
src/datamodel_code_generator/parser/graphql.py (1)
148-158: Affixed GraphQL reference names wiring looks correctDeriving
"enum"vs"model"fromresolved_typeand usingget_affixed_namesolely forReference.namekeeps internal keys/original_name stable while honoring affix scope for generated class names. This is a clean integration with the new affix API.src/datamodel_code_generator/__init__.py (1)
30-52: Public exposure ofClassNameAffixScopeis consistentImporting
ClassNameAffixScopefromenumsand adding it to__all__matches how other strategy enums are surfaced at the top level; no issues.Also applies to: 921-954
tests/data/expected/main/input_model/config_class.py (1)
28-30: Typed config surface for affix options looks consistent
ClassNameAffixScope’s Literal values match the runtime enum, and the three newGenerateConfigfields mirror existing optional string/enum config keys. This keeps the generated config class in sync with the new feature.Also applies to: 142-145
src/datamodel_code_generator/_types/generate_config_dict.py (1)
15-35:GenerateConfigDictextended correctly for affix optionsImporting
ClassNameAffixScopeunderTYPE_CHECKINGand addingclass_name_prefix,class_name_suffix, andclass_name_affix_scopeas optional fields is in line with existing config dict patterns and keeps the typed config dict aligned with the public options.Also applies to: 69-71
src/datamodel_code_generator/parser/base.py (1)
879-901: ModelResolver wiring for affix configuration looks correctForwarding
class_name_prefix,class_name_suffix, andclass_name_affix_scopedirectly fromconfig, and derivingskip_affix_for_rootfromconfig.class_namegives ModelResolver all it needs to honor both affix and explicit root-name semantics without changing other behavior.tests/data/expected/main/jsonschema/class_name_suffix/output.py (1)
1-24: Expected output for--class-name-suffixscenario looks correctEnum and model names consistently include the
Schemasuffix, and field types line up with the described JSON Schema structure, giving good coverage for the suffix behavior.src/datamodel_code_generator/_types/parser_config_dicts.py (1)
15-28: ParserConfig wiring for class-name affix options looks consistent and correct.Importing
ClassNameAffixScopeunderTYPE_CHECKINGand addingclass_name_prefix,class_name_suffix, andclass_name_affix_scope: NotRequired[ClassNameAffixScope]toParserConfigaligns with how other enum-backed options are modeled here. I don’t see any typing or structural issues in this addition.Also applies to: 60-63
tests/main/test_public_api_signature_baseline.py (1)
19-19: LGTM! Baseline signatures correctly extended.The new class name affix parameters are properly added to both baseline function signatures with consistent defaults and type annotations.
Also applies to: 88-90, 217-219
src/datamodel_code_generator/config.py (1)
18-18: LGTM! Configuration fields properly added.The new class name affix fields are correctly added to both
GenerateConfigandParserConfigwith appropriate types and defaults.Also applies to: 107-109, 242-244
src/datamodel_code_generator/__main__.py (4)
57-57: LGTM! Enum validator correctly implemented.The validator properly converts string inputs to the
ClassNameAffixScopeenum and defaultsNonetoAll.Also applies to: 399-407
475-483: LGTM! Pydantic v1 compatibility maintained.The Pydantic v1 validator correctly mirrors the v2 implementation for backward compatibility.
517-519: LGTM! Config fields properly defined.The new configuration fields are correctly typed and follow the project's existing patterns for optional fields.
896-898: LGTM! Parameters correctly propagated.The new configuration options are properly passed through to the
generate()function call.src/datamodel_code_generator/reference.py (6)
546-609: LGTM! Initialization and validation are robust.The class name affix configuration is properly initialized with thorough validation:
- Prefix must be a valid Python identifier
- Suffix must form valid identifier when combined
- Clear error messages for invalid inputs
- Appropriate default handling
838-844: LGTM! Deferred affix application is correctly implemented.The logic properly defers affix application when scope is model-type dependent, applying it later when
model_typeis known. The comments clearly explain this design decision.
942-999: LGTM! Model type and root tracking correctly implemented.The
addmethod properly:
- Determines root status based on path structure
- Implements smart early-return logic to avoid redundant processing
- Allows reprocessing when scope-dependent affix needs model_type
- Consistently passes
model_typeandis_rootto name generation
1081-1103: LGTM! GraphQL-specific naming method is well-designed.The
get_affixed_namemethod correctly provides a streamlined path for GraphQL naming:
- Skips uniqueness checks (GraphQL maintains its own naming)
- Applies generator and affix without singularization
- Clear documentation of GraphQL-specific behavior
1105-1158: LGTM! Comprehensive class name generation with affix support.The enhanced
get_class_namemethod correctly implements:
- Conditional transformation skipping via
skip_generator_and_affix- Deferred affix application via
skip_affixparameter- Proper placement of reserved name check (after transformations)
- Consistent uniqueness handling for all code paths
- Clear comments explaining the logic flow
The transformation pipeline is sound: dotted names → generator → singularization → affix → reserved name check → uniqueness.
1160-1183: LGTM! Model-type-specific duplicate name suffixes implemented.The changes enable type-specific suffixes for duplicate name resolution while maintaining backward compatibility with the global
duplicate_name_suffix.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/datamodel_code_generator/reference.py (1)
1047-1079: Fix unusednoqadirective; logic is correct.The scope-based filtering logic correctly applies affixes according to the configuration. However, the
# noqa: FBT001, FBT002directive on line 1051 is unused and should be removed.🔎 Proposed fix
def _apply_class_name_affix( self, name: str, model_type: str = "model", - is_root: bool = False, # noqa: FBT001, FBT002 + is_root: bool = False, ) -> str:
🧹 Nitpick comments (2)
src/datamodel_code_generator/reference.py (2)
1101-1110: Remove unusednoqadirectives.Multiple
noqadirectives forFBT001,FBT002,PLR0913, andPLR0917are unused according to static analysis and should be removed for cleaner code.🔎 Proposed fix
- def get_class_name( # noqa: PLR0913, PLR0917 + def get_class_name( self, name: str, - unique: bool = True, # noqa: FBT001, FBT002 + unique: bool = True, reserved_name: str | None = None, - singular_name: bool = False, # noqa: FBT001, FBT002 + singular_name: bool = False, singular_name_suffix: str | None = None, model_type: str = "model", - is_root: bool = False, # noqa: FBT001, FBT002 - skip_affix: bool = False, # noqa: FBT001, FBT002 + is_root: bool = False, + skip_affix: bool = False, ) -> ClassName:
549-550: Remove unusednoqadirectives.The
# noqa: FBT001, FBT002directives on lines 549-550 are unused and should be removed.🔎 Proposed fix
- skip_affix_for_root: bool = False, # noqa: FBT001, FBT002 - skip_generator_and_affix: bool = False, # noqa: FBT001, FBT002 + skip_affix_for_root: bool = False, + skip_generator_and_affix: bool = False, ) -> None:
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/datamodel_code_generator/parser/base.pysrc/datamodel_code_generator/reference.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-25T09:22:22.481Z
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.
Applied to files:
src/datamodel_code_generator/reference.py
📚 Learning: 2025-12-18T13:43:16.235Z
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.
Applied to files:
src/datamodel_code_generator/reference.py
🧬 Code graph analysis (2)
src/datamodel_code_generator/parser/base.py (1)
src/datamodel_code_generator/model/base.py (2)
class_name(848-850)class_name(853-857)
src/datamodel_code_generator/reference.py (2)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)src/datamodel_code_generator/model/base.py (5)
name(827-829)path(908-910)class_name(848-850)class_name(853-857)duplicate_name(832-834)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/reference.py
549-549: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
550-550: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1051-1051: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1101-1101: Unused noqa directive (non-enabled: PLR0913, PLR0917)
Remove unused noqa directive
(RUF100)
1104-1104: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1106-1106: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1109-1109: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1110-1110: Unused noqa directive (non-enabled: FBT001, FBT002)
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). (10)
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.10 on macOS
- GitHub Check: Analyze (python)
- GitHub Check: benchmarks
- GitHub Check: 3.11 on macOS
- GitHub Check: 3.12 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
🔇 Additional comments (6)
src/datamodel_code_generator/reference.py (4)
597-609: LGTM! Validation logic is sound.The validation correctly ensures:
class_name_prefixis a complete valid Python identifierclass_name_suffixcan be concatenated (tested with "A" prefix) to form a valid identifier- Clear error messages guide users to fix configuration issues
838-844: LGTM! Correct deferral of scope-dependent affixes.The logic correctly defers affix application when the scope is not
All, sinceadd_ref()doesn't have access tomodel_typeyet. The affix will be applied later inadd()when the actual model type is known.
1081-1099: LGTM! Well-documented method for GraphQL parser.The method correctly provides affix application without uniqueness checks, as needed by the GraphQL parser. The inline comments clearly document the intentional differences in behavior (no singularization, no dot-path splitting).
1156-1180: LGTM! Correct addition of model_type parameter.The
model_typeparameter correctly enables type-specific suffix selection via_get_suffix_for_model_type(), supporting the configurable duplicate name suffix feature.src/datamodel_code_generator/parser/base.py (2)
896-900: LGTM! Correct integration of affix configuration.The affix-related parameters are correctly passed from config to
ModelResolver. The derivation ofskip_affix_for_rootfromconfig.class_name is not Noneis logical—when users specify an explicit root class name via--class-name, affixes should not be applied to that root model.
1131-1137: LGTM! Correct use of skip_generator_and_affix for duplicate resolution.Setting
skip_generator_and_affix=Trueis correct here. When resolving duplicate class names within a module (line 1142), only uniqueness transformations should be applied, not class name generation or affixes, to preserve the intended names.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/datamodel_code_generator/reference.py (1)
1045-1077: Scope-based filtering logic is correct.The implementation correctly applies affixes according to the configured scope (All, Models, or Enums) and respects the root handling flag. The logic is sound and the filtering conditions are accurate.
Note: The unused
noqadirective on line 1049 was already flagged in a previous review.
🧹 Nitpick comments (2)
src/datamodel_code_generator/reference.py (2)
546-607: Solid validation logic for affix configuration.The initialization correctly validates that prefix/suffix are valid Python identifier components. The validation approach is sound:
- Prefix must be a complete identifier
- Suffix validation using
f"A{self.class_name_suffix}"ensures it can be safely appendedOne minor note: Line 549 has an unused
noqadirective that could be removed.🔎 Optional: Remove unused noqa directive
- skip_affix_for_root: bool = False, # noqa: FBT001, FBT002 + skip_affix_for_root: bool = False,
1099-1144: Correct transformation order and control flow.The updates properly integrate affix application into the class name generation pipeline with the correct ordering:
- Class name generator
- Singularization (if requested)
- Affix application (unless skipped)
- Reserved name check
- Uniqueness resolution
The logic correctly propagates
model_typeandis_rootthrough the transformations.Minor note: Several unused
noqadirectives could be cleaned up (lines 1099, 1102, 1104, 1107, 1108).🔎 Optional: Remove unused noqa directives
- def get_class_name( # noqa: PLR0913, PLR0917 + def get_class_name( self, name: str, - unique: bool = True, # noqa: FBT001, FBT002 + unique: bool = True, reserved_name: str | None = None, - singular_name: bool = False, # noqa: FBT001, FBT002 + singular_name: bool = False, singular_name_suffix: str | None = None, model_type: str = "model", - is_root: bool = False, # noqa: FBT001, FBT002 - skip_affix: bool = False, # noqa: FBT001, FBT002 + is_root: bool = False, + skip_affix: bool = False, ) -> ClassName:
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/datamodel_code_generator/reference.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-25T09:22:22.481Z
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.
Applied to files:
src/datamodel_code_generator/reference.py
📚 Learning: 2025-12-18T13:43:16.235Z
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.
Applied to files:
src/datamodel_code_generator/reference.py
🧬 Code graph analysis (1)
src/datamodel_code_generator/reference.py (2)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)src/datamodel_code_generator/model/base.py (5)
name(827-829)path(908-910)class_name(848-850)class_name(853-857)duplicate_name(832-834)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/reference.py
549-549: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1049-1049: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1099-1099: Unused noqa directive (non-enabled: PLR0913, PLR0917)
Remove unused noqa directive
(RUF100)
1102-1102: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1104-1104: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1107-1107: Unused noqa directive (non-enabled: FBT001, FBT002)
Remove unused noqa directive
(RUF100)
1108-1108: Unused noqa directive (non-enabled: FBT001, FBT002)
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). (10)
- GitHub Check: 3.13 on macOS
- GitHub Check: 3.10 on macOS
- GitHub Check: 3.11 on macOS
- GitHub Check: 3.12 on Windows
- GitHub Check: 3.11 on Windows
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.14 on Windows
- GitHub Check: 3.13 on Windows
- GitHub Check: Analyze (python)
- GitHub Check: benchmarks
🔇 Additional comments (5)
src/datamodel_code_generator/reference.py (5)
38-38: LGTM: Import addition supports new affix scope feature.The import of
ClassNameAffixScopeis correctly placed and necessary for the class name affix functionality.
836-842: Correct deferral of affix application when scope-dependent.The logic correctly identifies when affix application must be deferred because the model_type is unknown at
add_reftime but needed for scope-based filtering (Models vs Enums). The affix will be applied later duringadd()when the model_type is known.
940-997: Well-designed reprocessing logic for deferred affix application.The changes correctly handle the two-phase affix application:
add_ref()defers affix when scope-dependent and model_type is unknownadd()applies the correct affix with the now-known model_typeThe root detection logic (lines 944-946) correctly identifies root schemas, and the early-return conditions (lines 961-963) appropriately skip reprocessing when unnecessary.
1079-1097: Appropriate specialized method for GraphQL parser.The
get_affixed_namemethod provides a focused interface for applying class name transformations without uniqueness resolution or singularization. This design correctly serves the GraphQL parser's needs while keeping the more complexget_class_namemethod unchanged.The docstring clearly documents the intentional limitations, which helps prevent misuse.
1146-1146: LGTM: Signature update supports type-specific suffixes.The
model_typeparameter addition enables type-specific duplicate name resolution, correctly integrating with the existing suffix map functionality.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/datamodel_code_generator/__main__.py (1)
517-519: Clean up unusednoqadirectives.The field declarations are correct, but the
# noqa: UP045directives on lines 517-518 are unnecessary and should be removed for cleaner code.🔎 Proposed fix
- class_name_prefix: Optional[str] = None # noqa: UP007, UP045 - class_name_suffix: Optional[str] = None # noqa: UP007, UP045 + class_name_prefix: Optional[str] = None # noqa: UP007 + class_name_suffix: Optional[str] = None # noqa: UP007 class_name_affix_scope: ClassNameAffixScope = ClassNameAffixScope.All
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/datamodel_code_generator/__main__.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/datamodel_code_generator/__main__.py (2)
src/datamodel_code_generator/enums.py (1)
ClassNameAffixScope(131-141)src/datamodel_code_generator/util.py (1)
field_validator(175-191)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py
517-517: Unused noqa directive (non-enabled: UP045)
Remove unused noqa directive
(RUF100)
518-518: 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). (15)
- GitHub Check: 3.13 on Windows
- GitHub Check: py312-black22 on Ubuntu
- GitHub Check: py312-isort6 on Ubuntu
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.14 on Windows
- GitHub Check: py312-isort5 on Ubuntu
- GitHub Check: py312-isort7 on Ubuntu
- GitHub Check: 3.10 on Ubuntu
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: 3.12 on Ubuntu
- GitHub Check: py312-black23 on Ubuntu
- GitHub Check: 3.12 on Windows
- GitHub Check: 3.11 on Windows
- GitHub Check: benchmarks
- GitHub Check: Analyze (python)
🔇 Additional comments (4)
src/datamodel_code_generator/__main__.py (4)
57-57: LGTM!The import of
ClassNameAffixScopeis correctly positioned and follows the existing import pattern.
399-407: LGTM!The field validator correctly handles coercion from
NoneandstrtoClassNameAffixScope, with appropriate use ofmode="before"for pre-validation transformation. The logic properly defaultsNonetoClassNameAffixScope.All, consistent with the field definition.
475-483: LGTM!The Pydantic v1 field validator mirrors the v2 implementation correctly, ensuring consistent behavior across both Pydantic versions.
896-898: LGTM!The new parameters are correctly passed to the
generate()function, following the established pattern and logically grouped with the relatedclass_nameparameter.
Breaking Change AnalysisResult: No breaking changes detected Reasoning: This PR adds three new CLI options (--class-name-prefix, --class-name-suffix, --class-name-affix-scope) and a new ClassNameAffixScope enum. All new features are opt-in with None/empty defaults. When the new options are not specified, the code behaves exactly as before: prefix and suffix default to empty strings, and _apply_class_name_affix() returns names unchanged. No Jinja2 templates were modified, no default behaviors were changed, and existing users will see identical generated output without any action required. This analysis was performed by Claude Code Action |
|
🎉 Released in 0.52.0 This PR is now available in the latest release. See the release notes for details. |
Summary by CodeRabbit
New Features
CLI
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.