77from __future__ import annotations
88
99import enum as _enum
10+ import importlib
1011import json
1112import re
1213from collections import defaultdict
@@ -561,6 +562,7 @@ class JsonSchemaParser(Parser):
561562 }
562563
563564 PYTHON_TYPE_IMPORTS : ClassVar [dict [str , Import ]] = {
565+ # collections.abc
564566 "Callable" : Import .from_full_path ("collections.abc.Callable" ),
565567 "Iterable" : Import .from_full_path ("collections.abc.Iterable" ),
566568 "Iterator" : Import .from_full_path ("collections.abc.Iterator" ),
@@ -570,9 +572,57 @@ class JsonSchemaParser(Parser):
570572 "AsyncIterable" : Import .from_full_path ("collections.abc.AsyncIterable" ),
571573 "AsyncIterator" : Import .from_full_path ("collections.abc.AsyncIterator" ),
572574 "AsyncGenerator" : Import .from_full_path ("collections.abc.AsyncGenerator" ),
575+ "Mapping" : Import .from_full_path ("collections.abc.Mapping" ),
576+ "MutableMapping" : Import .from_full_path ("collections.abc.MutableMapping" ),
577+ "Sequence" : Import .from_full_path ("collections.abc.Sequence" ),
578+ "MutableSequence" : Import .from_full_path ("collections.abc.MutableSequence" ),
579+ "Set" : Import .from_full_path ("collections.abc.Set" ),
580+ "MutableSet" : Import .from_full_path ("collections.abc.MutableSet" ),
581+ "Collection" : Import .from_full_path ("collections.abc.Collection" ),
582+ "Reversible" : Import .from_full_path ("collections.abc.Reversible" ),
583+ # collections
584+ "defaultdict" : Import .from_full_path ("collections.defaultdict" ),
585+ "OrderedDict" : Import .from_full_path ("collections.OrderedDict" ),
586+ "Counter" : Import .from_full_path ("collections.Counter" ),
587+ "deque" : Import .from_full_path ("collections.deque" ),
588+ "ChainMap" : Import .from_full_path ("collections.ChainMap" ),
589+ # re
573590 "Pattern" : Import .from_full_path ("re.Pattern" ),
574591 "Match" : Import .from_full_path ("re.Match" ),
592+ # typing
593+ "Any" : Import .from_full_path ("typing.Any" ),
575594 "Type" : Import .from_full_path ("typing.Type" ),
595+ "Union" : Import .from_full_path ("typing.Union" ),
596+ "Optional" : Import .from_full_path ("typing.Optional" ),
597+ "Literal" : Import .from_full_path ("typing.Literal" ),
598+ "Final" : Import .from_full_path ("typing.Final" ),
599+ "ClassVar" : Import .from_full_path ("typing.ClassVar" ),
600+ "Annotated" : Import .from_full_path ("typing.Annotated" ),
601+ "TypeVar" : Import .from_full_path ("typing.TypeVar" ),
602+ "TypeAlias" : Import .from_full_path ("typing.TypeAlias" ),
603+ "Never" : Import .from_full_path ("typing.Never" ),
604+ "NoReturn" : Import .from_full_path ("typing.NoReturn" ),
605+ "Self" : Import .from_full_path ("typing.Self" ),
606+ "LiteralString" : Import .from_full_path ("typing.LiteralString" ),
607+ "TypeGuard" : Import .from_full_path ("typing.TypeGuard" ),
608+ # pathlib
609+ "Path" : Import .from_full_path ("pathlib.Path" ),
610+ "PurePath" : Import .from_full_path ("pathlib.PurePath" ),
611+ # decimal
612+ "Decimal" : Import .from_full_path ("decimal.Decimal" ),
613+ # uuid
614+ "UUID" : Import .from_full_path ("uuid.UUID" ),
615+ # datetime
616+ "datetime" : Import .from_full_path ("datetime.datetime" ),
617+ "date" : Import .from_full_path ("datetime.date" ),
618+ "time" : Import .from_full_path ("datetime.time" ),
619+ "timedelta" : Import .from_full_path ("datetime.timedelta" ),
620+ # enum
621+ "Enum" : Import .from_full_path ("enum.Enum" ),
622+ "IntEnum" : Import .from_full_path ("enum.IntEnum" ),
623+ "StrEnum" : Import .from_full_path ("enum.StrEnum" ),
624+ "Flag" : Import .from_full_path ("enum.Flag" ),
625+ "IntFlag" : Import .from_full_path ("enum.IntFlag" ),
576626 }
577627
578628 # Types that require x-python-type override regardless of schema type
@@ -1355,6 +1405,34 @@ def _extract_all_type_names(self, type_str: str) -> list[str]: # noqa: PLR6301
13551405 pattern = r"(?<![.\w])([A-Z]\w*)"
13561406 return re .findall (pattern , type_str )
13571407
1408+ @staticmethod
1409+ @lru_cache (maxsize = 256 )
1410+ def _resolve_type_import_dynamic (type_name : str ) -> Import | None :
1411+ """Dynamically resolve import for a type name from known modules."""
1412+ modules_to_check = (
1413+ "typing" ,
1414+ "collections.abc" ,
1415+ "collections" ,
1416+ "pathlib" ,
1417+ "decimal" ,
1418+ "uuid" ,
1419+ "datetime" ,
1420+ "enum" ,
1421+ "re" ,
1422+ )
1423+ for module_name in modules_to_check :
1424+ with suppress (ImportError ):
1425+ module = importlib .import_module (module_name )
1426+ if hasattr (module , type_name ):
1427+ return Import .from_full_path (f"{ module_name } .{ type_name } " )
1428+ return None
1429+
1430+ def _resolve_type_import (self , type_name : str ) -> Import | None :
1431+ """Resolve import for a type name, with dynamic fallback."""
1432+ if type_name in self .PYTHON_TYPE_IMPORTS :
1433+ return self .PYTHON_TYPE_IMPORTS [type_name ]
1434+ return self ._resolve_type_import_dynamic (type_name )
1435+
13581436 def _get_python_type_override (self , obj : JsonSchemaObject ) -> DataType | None :
13591437 """Get DataType from x-python-type if it's incompatible with schema type."""
13601438 x_python_type = obj .extras .get ("x-python-type" )
@@ -1366,7 +1444,7 @@ def _get_python_type_override(self, obj: JsonSchemaObject) -> DataType | None:
13661444 return None
13671445
13681446 base_type = self ._get_python_type_base (x_python_type )
1369- import_ = self .PYTHON_TYPE_IMPORTS . get (base_type )
1447+ import_ = self ._resolve_type_import (base_type )
13701448
13711449 # Convert fully qualified path to short name when import is added
13721450 type_str = x_python_type
@@ -1389,7 +1467,7 @@ def _get_python_type_override(self, obj: JsonSchemaObject) -> DataType | None:
13891467 # Collect imports for all nested types (e.g., Iterable inside Callable[[Iterable[str]], str])
13901468 for type_name in self ._extract_all_type_names (type_str ):
13911469 if type_name != base_type :
1392- nested_import = self .PYTHON_TYPE_IMPORTS . get (type_name )
1470+ nested_import = self ._resolve_type_import (type_name )
13931471 if nested_import :
13941472 nested_imports .append (self .data_type (import_ = nested_import ))
13951473
0 commit comments