Skip to content

Commit a3cd521

Browse files
authored
Fix generic type import with module path (#2858)
1 parent 86ad441 commit a3cd521

3 files changed

Lines changed: 42 additions & 2 deletions

File tree

src/datamodel_code_generator/__main__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,9 +689,16 @@ def _serialize_callable(args: tuple[type, ...]) -> str:
689689

690690

691691
def _get_origin_name(origin: type) -> str:
692-
"""Get the name of a generic origin."""
692+
"""Get the fully qualified name of a generic origin.
693+
694+
For types from builtins, typing, or collections.abc, returns just the name.
695+
For other types (custom generics), returns module.name format.
696+
"""
693697
name = getattr(origin, "__name__", None)
694698
if name:
699+
module = getattr(origin, "__module__", "")
700+
if module and module not in {"builtins", "typing", "collections.abc"}:
701+
return f"{module}.{name}"
695702
return name
696703

697704
# Fallback for origins without __name__ (rare edge case)

tests/data/python/input_model/pydantic_models.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,22 @@
22

33
from __future__ import annotations
44

5+
from collections import UserDict
56
from collections.abc import Callable, Mapping, Sequence
6-
from typing import Any, FrozenSet, Optional, Set, Type, Union
7+
from typing import Any, FrozenSet, Generic, Optional, Set, Type, TypeVar, Union
78

89
from pydantic import BaseModel
910

11+
# Custom generic type for testing generic type import
12+
TK = TypeVar("TK")
13+
TV = TypeVar("TV")
14+
15+
16+
class CustomGenericDict(UserDict[TK, TV], Generic[TK, TV]):
17+
"""Custom generic dict for testing generic type import."""
18+
19+
pass
20+
1021

1122
class User(BaseModel):
1223
"""User model with basic fields."""
@@ -86,3 +97,11 @@ class ModelWithUnionCallable(BaseModel):
8697

8798
union_callback: Union[Callable[[str], str], int]
8899
raw_callable: Callable # Callable without type args
100+
101+
102+
class ModelWithCustomGeneric(BaseModel):
103+
"""Model with custom generic type that requires module import."""
104+
105+
model_config = {"arbitrary_types_allowed": True}
106+
custom_dict: CustomGenericDict[str, int]
107+
optional_custom_dict: CustomGenericDict[str, str] | None

tests/test_input_model.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,20 @@ def test_input_model_union_callable(tmp_path: Path) -> None:
727727
)
728728

729729

730+
@SKIP_PYDANTIC_V1
731+
def test_input_model_custom_generic_type_import(tmp_path: Path) -> None:
732+
"""Test that custom generic types are properly imported with full module path."""
733+
run_input_model_and_assert(
734+
input_model="tests.data.python.input_model.pydantic_models:ModelWithCustomGeneric",
735+
output_path=tmp_path / "output.py",
736+
expected_output_contains=[
737+
"from tests.data.python.input_model.pydantic_models import CustomGenericDict",
738+
"CustomGenericDict[str, int]",
739+
"CustomGenericDict[str, str] | None",
740+
],
741+
)
742+
743+
730744
# ============================================================================
731745
# --input-model-ref-strategy tests
732746
# ============================================================================

0 commit comments

Comments
 (0)