Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 16 additions & 5 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 @@ -1399,11 +1405,16 @@ def _is_compatible_python_type(self, schema_type: str | None, python_type: str)
return base_type in compatible

def _extract_all_type_names(self, type_str: str) -> list[str]: # noqa: PLR6301
"""Extract all type names from a type annotation string."""
# Match type names: word characters starting with uppercase, not preceded by a dot
# This handles cases like Callable[[Iterable[str]], str]
pattern = r"(?<![.\w])([A-Z]\w*)"
return re.findall(pattern, type_str)
"""Extract all type names from a type annotation string using AST parsing."""
import ast # noqa: PLC0415

try:
tree = ast.parse(type_str, mode="eval")
return [node.id for node in ast.walk(tree) if isinstance(node, ast.Name)]
except SyntaxError: # pragma: no cover
# Fallback to regex for non-standard type strings
pattern = r"(?<![.\w])([A-Za-z_]\w*)"
return re.findall(pattern, type_str)

@staticmethod
@lru_cache(maxsize=256)
Expand Down
Loading