Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/datamodel_code_generator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -915,8 +915,13 @@ def _serialize_python_type(tp: type) -> str | None: # noqa: PLR0911

def _simple_type_name(tp: type) -> str:
"""Get a simple string representation of a type."""
from typing import get_origin # noqa: PLC0415

if tp is type(None):
return "None"
# For generic types (e.g., dict[str, Any]), use full string representation
if get_origin(tp) is not None:
return str(tp).replace("typing.", "")
if hasattr(tp, "__name__"):
return tp.__name__
return str(tp).replace("typing.", "") # pragma: no cover
Expand Down
12 changes: 7 additions & 5 deletions src/datamodel_code_generator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pathlib import Path # noqa: TC003 - used at runtime by Pydantic
from typing import TYPE_CHECKING, Annotated, Any

from pydantic import BaseModel, Field, WithJsonSchema
from pydantic import BaseModel, Field

from datamodel_code_generator.enums import (
DEFAULT_SHARED_MODULE_NAME,
Expand Down Expand Up @@ -48,10 +48,12 @@
CallableSchema = Callable[[str], str]
DumpResolveReferenceAction = Callable[[Iterable[str]], str]
DefaultPutDictSchema = DefaultPutDict[str, str]
ExtraTemplateDataType = Annotated[
defaultdict[str, Annotated[dict[str, Any], Field(default_factory=dict)]],
WithJsonSchema({"type": "object", "x-python-type": "defaultdict[str, dict[str, Any]]"}),
]
if TYPE_CHECKING:
ExtraTemplateDataType = defaultdict[str, dict[str, Any]]
elif is_pydantic_v2():
ExtraTemplateDataType = defaultdict[str, Annotated[dict[str, Any], Field(default_factory=dict)]]
else:
ExtraTemplateDataType = defaultdict[str, dict[str, Any]]


class GenerateConfig(BaseModel):
Expand Down
17 changes: 17 additions & 0 deletions src/datamodel_code_generator/parser/jsonschema.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,12 @@ class JsonSchemaParser(Parser):
PYTHON_TYPE_OVERRIDE_ALWAYS: ClassVar[frozenset[str]] = frozenset({
"Callable",
"Type",
# collections types that have no JSON Schema equivalent
"defaultdict",
"OrderedDict",
"Counter",
"deque",
"ChainMap",
})

def __init__( # noqa: PLR0913
Expand Down Expand Up @@ -1391,6 +1397,10 @@ def _is_compatible_python_type(self, schema_type: str | None, python_type: str)
all_type_names = self._extract_all_type_names(python_type)
if any(t in self.PYTHON_TYPE_OVERRIDE_ALWAYS for t in all_type_names):
return False
# Check for lowercase types in PYTHON_TYPE_OVERRIDE_ALWAYS (e.g., defaultdict, deque)
for override_type in self.PYTHON_TYPE_OVERRIDE_ALWAYS:
if override_type[0].islower() and override_type in python_type:
return False
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
if schema_type is None:
return True
if base_type in {"Union", "Optional"}:
Expand Down Expand Up @@ -1471,6 +1481,13 @@ def _get_python_type_override(self, obj: JsonSchemaObject) -> DataType | None:
if nested_import:
nested_imports.append(self.data_type(import_=nested_import))

# Collect imports for lowercase types in PYTHON_TYPE_OVERRIDE_ALWAYS (e.g., defaultdict, deque)
for override_type in self.PYTHON_TYPE_OVERRIDE_ALWAYS:
if override_type[0].islower() and override_type in type_str and override_type != base_type:
override_import = self._resolve_type_import(override_type)
if override_import:
nested_imports.append(self.data_type(import_=override_import))

result = self.data_type(type=type_str, import_=import_)
if nested_imports:
result.data_types.extend(nested_imports)
Expand Down
Loading