Skip to content

Add dynamic model generation support#2898

Closed
koxudaxi wants to merge 9 commits intomainfrom
feature/dynamic-model-generation
Closed

Add dynamic model generation support#2898
koxudaxi wants to merge 9 commits intomainfrom
feature/dynamic-model-generation

Conversation

@koxudaxi
Copy link
Copy Markdown
Owner

@koxudaxi koxudaxi commented Jan 2, 2026

Fixes: #1160

Summary by CodeRabbit

Release Notes

  • New Features

    • Dynamic model generation from JSON Schema and OpenAPI specifications at runtime, enabling creation of Python model classes directly from schema definitions.
    • New public API for runtime model generation with enhanced error handling.
  • Tests

    • Added comprehensive test coverage for dynamic model generation functionality.

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 2, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This PR introduces a new dynamic model generation feature that enables runtime creation of Python model classes from schemas using Pydantic v2. It includes a public API function, type resolver, constraint handler, and comprehensive model creator infrastructure.

Changes

Cohort / File(s) Summary
Public API Layer
src/datamodel_code_generator/__init__.py
Adds generate_dynamic_models() function for user-facing dynamic model generation with auto-detection of input type (path, text, dict, or ParseResult) and returns dict mapping class names to model classes. Supports JsonSchema and OpenAPI inputs; notably excludes GraphQL.
Dynamic Module Initialization
src/datamodel_code_generator/dynamic/__init__.py
Establishes dynamic package as public interface, re-exporting DynamicModelCreator and 5 exception types (DynamicModelError, TypeResolutionError, CircularReferenceError, ConstraintConversionError, UnsupportedModelTypeError).
Exception Definitions
src/datamodel_code_generator/dynamic/exceptions.py
Defines custom exception hierarchy with base DynamicModelError and 4 specialized exceptions. Each captures specific failure scenarios (type resolution, circular refs, constraint conversion, unsupported model types) with contextual error messages.
Type Resolution
src/datamodel_code_generator/dynamic/type_resolver.py
Introduces TypeResolver class with three type mapping constants (FORMAT_TYPE_MAP, PRIMITIVE_TYPE_MAP, CONSTRAINED_TYPE_MAP). Resolves complex types (Literal, Union, List, Set, Dict, Tuple) with constraint extraction and forward reference tracking. Handles regex pattern normalization and fallback string-based resolution.
Constraint Conversion
src/datamodel_code_generator/dynamic/constraints.py
Provides constraints_to_field_kwargs() utility translating DataModel constraints to Pydantic FieldInfo arguments, mapping length/numeric/item bounds, patterns, and exclusive bounds. Aggregates unmapped constraints into json_schema_extra.
Model Creation Core
src/datamodel_code_generator/dynamic/creator.py
Implements DynamicModelCreator class orchestrating runtime model building. Handles enum/datamodel distinction, field resolution with type and constraints, base class resolution (including custom bases via import strings), model config building, and post-creation model rebuilding for forward ref resolution. Provides 9 public/internal methods.
Parser Integration
src/datamodel_code_generator/parser/base.py
Adds create_dynamic_models() method to Parser class, wrapping DynamicModelCreator initialization and delegation. Locally imports creator to avoid top-level coupling.
Test Suite
tests/dynamic/__init__.py, tests/dynamic/test_creator.py
Comprehensive test module covering simple models, nested definitions, constraints (string/numeric/array), circular refs, enums, inheritance (AllOf), OpenAPI integration, date/UUID formats, nullable fields, and path-based input handling. Tests skip conditionally for non-Pydantic v2 environments.

Sequence Diagram

sequenceDiagram
    participant User
    participant generate_dynamic_models as generate_dynamic_models()
    participant Parser
    participant DynamicModelCreator
    participant TypeResolver
    participant constraints_to_field_kwargs
    participant Pydantic as Pydantic.create_model()
    
    User->>generate_dynamic_models: input, input_file_type
    generate_dynamic_models->>Parser: instantiate & parse
    Parser-->>generate_dynamic_models: parsed results
    generate_dynamic_models->>DynamicModelCreator: init(parser)
    generate_dynamic_models->>DynamicModelCreator: create_models()
    
    rect rgb(200, 220, 255)
        Note over DynamicModelCreator: Model Creation Phase
        loop for each DataModel
            DynamicModelCreator->>DynamicModelCreator: sort & categorize
            alt is enum
                DynamicModelCreator->>Pydantic: Enum.__init__
            else is datamodel
                DynamicModelCreator->>TypeResolver: resolve_with_constraints(field.type)
                TypeResolver-->>DynamicModelCreator: (resolved_type, constraints)
                DynamicModelCreator->>constraints_to_field_kwargs: constraints_to_field_kwargs(constraints)
                constraints_to_field_kwargs-->>DynamicModelCreator: field_kwargs
                DynamicModelCreator->>Pydantic: create_model(class_name, **fields)
                Pydantic-->>DynamicModelCreator: model_class
            end
        end
    end
    
    rect rgb(200, 240, 200)
        Note over DynamicModelCreator: Rebuild Phase (Forward References)
        DynamicModelCreator->>DynamicModelCreator: _rebuild_models()
        loop for each model
            DynamicModelCreator->>Pydantic: model_rebuild()
        end
    end
    
    DynamicModelCreator-->>generate_dynamic_models: {class_name: model_type}
    generate_dynamic_models-->>User: dynamic_models dict
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

  • #2726: Modifies Parser class with use_generic_base_class support; shares the same parser implementation touchpoint as this PR's create_dynamic_models method addition.

Suggested Labels

breaking-change-analyzed

Poem

🐰 Hop hop, the schemas dance,
Dynamic models take their chance,
Pydantic fields resolve with care,
Type resolution in the air!
Forward refs? Rebuilt and bright, 🌟
Runtime models—pure delight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add dynamic model generation support' directly describes the main feature introduced in this PR - a new function generate_dynamic_models and supporting infrastructure for runtime Python model class generation from schemas.

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-2898.datamodel-code-generator.pages.dev

Comment thread src/datamodel_code_generator/dynamic/constraints.py Fixed
Comment thread src/datamodel_code_generator/dynamic/creator.py Dismissed
Comment thread src/datamodel_code_generator/dynamic/creator.py Fixed
Comment thread src/datamodel_code_generator/dynamic/creator.py Fixed
Comment thread src/datamodel_code_generator/dynamic/creator.py Dismissed
Comment thread src/datamodel_code_generator/dynamic/creator.py Fixed
Comment thread src/datamodel_code_generator/dynamic/creator.py Fixed
Comment thread src/datamodel_code_generator/dynamic/exceptions.py Dismissed
Comment thread src/datamodel_code_generator/dynamic/type_resolver.py Fixed
Comment thread src/datamodel_code_generator/parser/base.py Dismissed
@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 2, 2026

Codecov Report

❌ Patch coverage is 90.93079% with 38 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.16%. Comparing base (4f26e46) to head (e794006).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
src/datamodel_code_generator/dynamic/creator.py 85.18% 6 Missing and 10 partials ⚠️
src/datamodel_code_generator/__init__.py 62.96% 6 Missing and 4 partials ⚠️
.../datamodel_code_generator/dynamic/type_resolver.py 86.56% 4 Missing and 5 partials ⚠️
src/datamodel_code_generator/parser/base.py 66.66% 1 Missing and 1 partial ⚠️
...rc/datamodel_code_generator/dynamic/constraints.py 95.65% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2898      +/-   ##
==========================================
- Coverage   99.37%   99.16%   -0.22%     
==========================================
  Files          92       98       +6     
  Lines       16192    16667     +475     
  Branches     1915     1978      +63     
==========================================
+ Hits        16091    16528     +437     
- Misses         52       69      +17     
- Partials       49       70      +21     
Flag Coverage Δ
unittests 99.16% <90.93%> (-0.22%) ⬇️

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.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Jan 2, 2026

CodSpeed Performance Report

Merging #2898 will not alter performance

Comparing feature/dynamic-model-generation (e794006) with main (87445c8)

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (22)
src/datamodel_code_generator/dynamic/constraints.py (2)

83-89: Potential constraint overwrite when both gt and exclusive_minimum are present.

If a constraint object has both gt and exclusive_minimum set (or lt and exclusive_maximum), the exclusive bounds will silently overwrite the already-set gt/lt values. While JSON Schema semantics make this unlikely in practice, consider adding a check or using the stricter bound.

🔎 Proposed defensive handling
     exclusive_minimum = getattr(constraints, "exclusive_minimum", None)
     if exclusive_minimum is not None:
-        kwargs["gt"] = exclusive_minimum
+        if "gt" not in kwargs:
+            kwargs["gt"] = exclusive_minimum
+        else:
+            # Use stricter bound
+            kwargs["gt"] = max(kwargs["gt"], exclusive_minimum)

     exclusive_maximum = getattr(constraints, "exclusive_maximum", None)
     if exclusive_maximum is not None:
-        kwargs["lt"] = exclusive_maximum
+        if "lt" not in kwargs:
+            kwargs["lt"] = exclusive_maximum
+        else:
+            # Use stricter bound
+            kwargs["lt"] = min(kwargs["lt"], exclusive_maximum)

11-11: Consider removing the unused noqa directive.

Static analysis indicates the noqa: PLR0912, PLR0914, PLR0915 directive is unused since these rules are not enabled. If the project's linter configuration doesn't enable these rules, the directive can be safely removed.

🔎 Proposed fix
-def constraints_to_field_kwargs(  # noqa: PLR0912, PLR0914, PLR0915
+def constraints_to_field_kwargs(
     constraints: ConstraintsBase | None,
 ) -> dict[str, Any]:
src/datamodel_code_generator/parser/base.py (2)

3136-3170: Consider adding a runtime check for Pydantic v2.

The docstring explicitly states "Only Pydantic v2 models are supported," but there's no runtime enforcement. Users on Pydantic v1 will get unclear errors when attempting to use this feature.

🔎 Proposed Pydantic v2 check
     def create_dynamic_models(self) -> dict[str, type]:
         """Create actual Python model classes from parsed schema.
         ...
         """
         from datamodel_code_generator.dynamic.creator import DynamicModelCreator  # noqa: PLC0415
+        from datamodel_code_generator.util import is_pydantic_v2  # noqa: PLC0415
+
+        if not is_pydantic_v2():
+            msg = "Dynamic model generation requires Pydantic v2"
+            raise NotImplementedError(msg)

         if not self.results:
             return {}

         creator = DynamicModelCreator(self)
         return creator.create_models()

3164-3164: Consider removing the unused noqa directive.

Static analysis indicates the noqa: PLC0415 directive is unused. If PLC0415 (import not at top of file) is not enabled in your linter configuration, the directive can be removed. However, if you plan to enable this rule in the future, keeping it is fine.

tests/dynamic/test_creator.py (1)

1-1: Consider removing the unused noqa directive if D101/D102 rules are not enabled.

Static analysis indicates this directive may be unused. If docstring rules for classes (D101) and methods (D102) are not enabled in your linter configuration, this can be removed.

src/datamodel_code_generator/__init__.py (3)

959-959: Consider specifying encoding when reading file content.

The source.read_text() call doesn't specify an encoding, which will use the system default. For consistency with the rest of the codebase (which uses config.encoding), consider specifying encoding="utf-8" or making it configurable.

🔎 Proposed fix
         elif isinstance(source, Path):
-            input_file_type = infer_input_type(source.read_text())
+            input_file_type = infer_input_type(source.read_text(encoding="utf-8"))

899-975: Consider adding a runtime check for Pydantic v2.

The docstring states "Only Pydantic v2 models are supported," but this isn't enforced at runtime. Users on Pydantic v1 will encounter unclear errors.

🔎 Proposed check at function entry
 def generate_dynamic_models(
     input_: Path | str | ParseResult | Mapping[str, Any],
     *,
     input_file_type: InputFileType = InputFileType.Auto,
 ) -> dict[str, type]:
     """Generate actual Python model classes from schema at runtime.
     ...
     """
+    if not is_pydantic_v2():
+        msg = "Dynamic model generation requires Pydantic v2"
+        raise NotImplementedError(msg)
+
     from datamodel_code_generator.parser.jsonschema import JsonSchemaParser  # noqa: PLC0415

938-939: Consider removing unused noqa directives.

Static analysis indicates the noqa: PLC0415 directives on lines 938-939 are unused. If the PLC0415 rule is not enabled, these can be removed.

🔎 Proposed fix
-    from datamodel_code_generator.parser.jsonschema import JsonSchemaParser  # noqa: PLC0415
-    from datamodel_code_generator.parser.openapi import OpenAPIParser  # noqa: PLC0415
+    from datamodel_code_generator.parser.jsonschema import JsonSchemaParser
+    from datamodel_code_generator.parser.openapi import OpenAPIParser
src/datamodel_code_generator/dynamic/type_resolver.py (5)

77-77: Remove unused noqa directive.

Static analysis indicates PLR0911 is not enabled in the project's linting configuration.

🔎 Proposed fix
-    def resolve_with_constraints(  # noqa: PLR0911
+    def resolve_with_constraints(
         self, data_type: DataType
     ) -> tuple[Any, dict[str, Any]]:

118-118: Remove unused noqa directive.

Static analysis indicates UP007 is not enabled.

🔎 Proposed fix
-        return Union[inner_types], constraints  # type: ignore[valid-type] # noqa: UP007
+        return Union[inner_types], constraints  # type: ignore[valid-type]

149-149: Remove unused noqa directive.

Static analysis indicates PLR6301 is not enabled.

🔎 Proposed fix
-    def _extract_constraints(self, data_type: DataType, constraints: dict[str, Any]) -> None:  # noqa: PLR6301
+    def _extract_constraints(self, data_type: DataType, constraints: dict[str, Any]) -> None:

154-158: Consider handling double-quoted raw string patterns.

The normalization only handles single-quoted raw strings (r'...'). If DataType.kwargs can contain double-quoted raw strings (r"..."), those would not be normalized.

🔎 Proposed fix to handle both quote styles
             if key == "regex":
                 pattern_value = kwarg_value
                 if isinstance(pattern_value, str) and pattern_value.startswith("r'") and pattern_value.endswith("'"):
                     pattern_value = pattern_value[2:-1]
+                elif isinstance(pattern_value, str) and pattern_value.startswith('r"') and pattern_value.endswith('"'):
+                    pattern_value = pattern_value[2:-1]
                 constraints["pattern"] = pattern_value

169-169: Remove unused noqa directive.

Static analysis indicates UP007 is not enabled.

🔎 Proposed fix
-        return Union[inner_types]  # type: ignore[valid-type] # noqa: UP007
+        return Union[inner_types]  # type: ignore[valid-type]
src/datamodel_code_generator/dynamic/creator.py (9)

52-53: Remove unused noqa directives.

Static analysis indicates PLC0415 is not enabled. The local imports are appropriate for avoiding circular dependencies.

🔎 Proposed fix
-        from datamodel_code_generator.model.enum import Enum as EnumModel  # noqa: PLC0415
-        from datamodel_code_generator.parser.base import sort_data_models  # noqa: PLC0415
+        from datamodel_code_generator.model.enum import Enum as EnumModel
+        from datamodel_code_generator.parser.base import sort_data_models

90-90: Remove unused noqa directive.

🔎 Proposed fix
-        from pydantic import BaseModel as BaseModelCls  # noqa: PLC0415
+        from pydantic import BaseModel as BaseModelCls

132-132: Remove unused noqa directive.

🔎 Proposed fix
-    def _create_field_info(  # noqa: PLR6301
+    def _create_field_info(
         self, field: DataModelFieldBase, type_constraints: dict[str, Any] | None = None
     ) -> FieldInfo:

161-161: Remove unused noqa directive.

🔎 Proposed fix
-        from pydantic import BaseModel  # noqa: PLC0415
+        from pydantic import BaseModel

187-190: Consider logging when falling back to BaseModel for unresolved bases.

When a base class cannot be resolved from _short_name_lookup, the code silently falls back to BaseModel. Consider adding debug logging here to aid troubleshooting inheritance issues.

🔎 Proposed fix
                 if type_name in self._short_name_lookup:
                     bases.append(self._short_name_lookup[type_name])
                 else:
+                    logger.debug("Base class '%s' not found, falling back to BaseModel", type_name)
                     bases.append(BaseModel)
             else:
+                logger.debug("Base class has no reference or type, falling back to BaseModel")
                 bases.append(BaseModel)

194-194: Remove unused noqa directive.

🔎 Proposed fix
-    def _build_model_config(self, data_model: DataModel) -> ConfigDict | None:  # noqa: PLR6301
+    def _build_model_config(self, data_model: DataModel) -> ConfigDict | None:

219-219: Remove unused noqa directive.

🔎 Proposed fix
-    def _get_module_name(self, data_model: DataModel) -> str:  # noqa: PLR6301
+    def _get_module_name(self, data_model: DataModel) -> str:

249-249: Remove unused noqa directive.

🔎 Proposed fix
-        from pydantic import BaseModel  # noqa: PLC0415
+        from pydantic import BaseModel

257-258: Consider adding debug logging for unresolved annotations.

Silently passing on PydanticUndefinedAnnotation could hide issues with forward references that genuinely cannot be resolved. Consider adding debug logging to aid troubleshooting.

🔎 Proposed fix
                 try:
                     model.model_rebuild(_types_namespace=namespace)
-                except PydanticUndefinedAnnotation:
-                    pass
+                except PydanticUndefinedAnnotation as e:
+                    logger.debug("Model %s has unresolved annotation: %s", model_key, e)
📜 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 f4b240a and fe25d68.

📒 Files selected for processing (9)
  • src/datamodel_code_generator/__init__.py
  • src/datamodel_code_generator/dynamic/__init__.py
  • src/datamodel_code_generator/dynamic/constraints.py
  • src/datamodel_code_generator/dynamic/creator.py
  • src/datamodel_code_generator/dynamic/exceptions.py
  • src/datamodel_code_generator/dynamic/type_resolver.py
  • src/datamodel_code_generator/parser/base.py
  • tests/dynamic/__init__.py
  • tests/dynamic/test_creator.py
🧰 Additional context used
🧬 Code graph analysis (7)
src/datamodel_code_generator/parser/base.py (1)
src/datamodel_code_generator/dynamic/creator.py (2)
  • DynamicModelCreator (32-263)
  • create_models (42-68)
src/datamodel_code_generator/dynamic/exceptions.py (1)
src/datamodel_code_generator/types.py (1)
  • DataType (400-941)
src/datamodel_code_generator/__init__.py (2)
src/datamodel_code_generator/enums.py (1)
  • InputFileType (35-45)
src/datamodel_code_generator/parser/jsonschema.py (2)
  • JsonSchemaParser (552-3765)
  • parse_raw (3637-3668)
src/datamodel_code_generator/dynamic/__init__.py (1)
src/datamodel_code_generator/dynamic/exceptions.py (5)
  • CircularReferenceError (26-32)
  • ConstraintConversionError (35-43)
  • DynamicModelError (11-12)
  • TypeResolutionError (15-23)
  • UnsupportedModelTypeError (46-52)
tests/dynamic/test_creator.py (3)
src/datamodel_code_generator/enums.py (1)
  • InputFileType (35-45)
src/datamodel_code_generator/__init__.py (1)
  • generate_dynamic_models (899-975)
src/datamodel_code_generator/model/base.py (1)
  • name (827-829)
src/datamodel_code_generator/dynamic/constraints.py (1)
src/datamodel_code_generator/model/base.py (1)
  • ConstraintsBase (101-148)
src/datamodel_code_generator/dynamic/type_resolver.py (2)
src/datamodel_code_generator/types.py (1)
  • DataType (400-941)
src/datamodel_code_generator/reference.py (2)
  • reference (77-79)
  • short_name (187-189)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/parser/base.py

3164-3164: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/__init__.py

938-938: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


939-939: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

tests/dynamic/test_creator.py

1-1: Unused noqa directive (non-enabled: D101, D102)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/dynamic/constraints.py

11-11: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/dynamic/type_resolver.py

77-77: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


118-118: Unused noqa directive (non-enabled: UP007)

Remove unused noqa directive

(RUF100)


149-149: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


169-169: Unused noqa directive (non-enabled: UP007)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/dynamic/creator.py

52-52: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


53-53: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


90-90: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


132-132: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


161-161: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


194-194: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


219-219: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


249-249: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: 3.10 on Ubuntu
  • GitHub Check: 3.10 on Windows
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: py312-isort5 on Ubuntu
  • GitHub Check: py312-isort6 on Ubuntu
  • GitHub Check: benchmarks
  • GitHub Check: py312-black24 on Ubuntu
  • GitHub Check: 3.12 on Ubuntu
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: Analyze (python)
🔇 Additional comments (14)
tests/dynamic/__init__.py (1)

1-1: LGTM!

Standard test package initializer with an appropriate docstring.

src/datamodel_code_generator/dynamic/exceptions.py (1)

1-52: LGTM!

Well-structured exception hierarchy with informative error messages and proper storage of contextual information for debugging. The use of TYPE_CHECKING for the DataType forward reference correctly avoids circular imports.

tests/dynamic/test_creator.py (1)

1-16: LGTM! Comprehensive test coverage.

The test suite provides excellent coverage of dynamic model generation features including constraints, nested models, circular references, various data types, and different input formats. The Pydantic v2 skip condition correctly gates the tests.

src/datamodel_code_generator/__init__.py (1)

1037-1037: LGTM!

The __all__ export list is correctly updated to include the new generate_dynamic_models function.

src/datamodel_code_generator/dynamic/__init__.py (1)

1-25: LGTM!

Clean package initialization with appropriate re-exports. The __all__ list is well-organized and includes all the necessary public components for dynamic model generation.

src/datamodel_code_generator/dynamic/type_resolver.py (5)

1-57: Well-structured type mapping constants.

The three type mapping dictionaries provide clear separation of concerns for format-based types, primitive types, and constrained types. The coverage of standard formats (date-time, uuid variants, etc.) and primitives is comprehensive.


61-68: Clean class initialization with forward reference tracking.

The TypeResolver is properly initialized with a models lookup dictionary and forward reference tracking, which integrates well with the dynamic model creation flow.


116-122: Verify ordering of union vs optional type resolution.

The check for len(data_type.data_types) > 1 on line 116 runs before the is_optional check on line 120. If a DataType is marked is_optional=True and also has multiple inner types (e.g., Optional[Union[A, B]]), the union branch handles it first without wrapping in | None. Confirm this is the intended behavior, or whether is_optional should be checked first.


126-147: Appropriate fallback chain for type resolution.

The resolution priority (constrained types → format-based types → primitive types → model lookup → Any fallback) is well-ordered. Using getattr for the format attribute safely handles cases where it may not be present.


171-174: LGTM!

Clean property implementation exposing forward references for model rebuilding.

src/datamodel_code_generator/dynamic/creator.py (4)

12-15: Good compatibility handling for Pydantic versions.

The fallback to NameError when PydanticUndefinedAnnotation is unavailable provides graceful degradation for different Pydantic versions.


141-148: Default handling logic is correct.

The condition on line 141 properly handles the case where field.default is explicitly None by first checking has_default. The fallback chain (has_default → default_factory → required → None) covers all cases appropriately.


226-245: LGTM!

Clean enum creation using the functional API with proper quote stripping and consistent model registration.


104-105: Verify __config__ usage with Pydantic v2's create_model.

In Pydantic v2, create_model may expect model_config parameter or handle __config__ differently. Verify this is the correct approach for the target Pydantic version.

Pydantic v2 create_model __config__ parameter

@koxudaxi koxudaxi marked this pull request as draft January 2, 2026 11:28
Comment thread src/datamodel_code_generator/dynamic/constraints.py Dismissed
Comment thread src/datamodel_code_generator/dynamic/creator.py Dismissed
Comment thread src/datamodel_code_generator/dynamic/type_resolver.py Fixed
Comment thread src/datamodel_code_generator/dynamic/creator.py Dismissed
Comment thread src/datamodel_code_generator/dynamic/creator.py Dismissed
Comment thread src/datamodel_code_generator/dynamic/creator.py Dismissed
Comment thread src/datamodel_code_generator/dynamic/creator.py Dismissed
- Remove line comments from generate_dynamic_models function
- Remove unused exclusive_minimum/exclusive_maximum from CONSTRAINT_FIELD_MAP
- Fix description access: use field.extras.get() instead of getattr()
- Move all inline schemas to tests/data/dynamic/
- Rewrite tests as functions instead of classes
- Use inline-snapshot for assertions
- Consolidate similar tests into grouped test functions
- Replace PRIMITIVE_TYPE_MAP with builtins lookup
- Use TYPE_ALIASES for schema type names (string->str, etc.)
- Rename CONSTRAINED_TYPE_MAP to CONSTRAINED_TYPE_BASE
from typing import TYPE_CHECKING, Any, ForwardRef, Literal

if TYPE_CHECKING:
from datamodel_code_generator.types import DataType

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
datamodel_code_generator.types
begins an import cycle.
- Add handling for list, set, and dict container types
- Fix circular reference resolution (now returns correct list[Model] type)
- Refactor resolve_with_constraints to reduce return statements
@koxudaxi koxudaxi closed this Jan 2, 2026
@koxudaxi koxudaxi deleted the feature/dynamic-model-generation branch January 2, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants