2323)
2424from uuid import uuid4
2525
26- try :
27- from pydantic import BaseModel as BaseModelV2
28- from pydantic import create_model as create_model_v2
29- from pydantic .v1 import BaseModel as BaseModelV1
30- from pydantic .v1 import create_model as create_model_v1
31- except ImportError :
32- BaseModelV2 = None
33- create_model_v2 = None
34- from pydantic import BaseModel as BaseModelV1
35- from pydantic import create_model as create_model_v1
36-
37- try :
38- from pydantic .v1 .generics import GenericModel as GenericModelV1
39- except ImportError :
40- try :
41- from pydantic .generics import GenericModel as GenericModelV1
42- except ImportError :
43- GenericModelV1 = None
26+ import pydantic2ts .pydantic_v1 as v1
27+ import pydantic2ts .pydantic_v2 as v2
4428
4529if TYPE_CHECKING :
4630 from pydantic .config import ConfigDict
@@ -63,12 +47,10 @@ def _import_module(path: str) -> ModuleType:
6347 if os .path .exists (path ):
6448 name = uuid4 ().hex
6549 spec = spec_from_file_location (name , path , submodule_search_locations = [])
66- if spec is None :
67- raise ImportError (f"spec_from_file_location failed for { path } " )
50+ assert spec is not None , f"spec_from_file_location failed for { path } "
6851 module = module_from_spec (spec )
6952 sys .modules [name ] = module
70- if spec .loader is None :
71- raise ImportError (f"loader is None for { path } " )
53+ assert spec .loader is not None , f"loader is None for { path } "
7254 spec .loader .exec_module (module )
7355 return module
7456 else :
@@ -87,40 +69,45 @@ def _is_submodule(obj: Any, module_name: str) -> bool:
8769 return inspect .ismodule (obj ) and getattr (obj , "__name__" , "" ).startswith (f"{ module_name } ." )
8870
8971
90- def _is_pydantic_v1_model (obj : Any ) -> bool :
72+ def _is_v1_model (obj : Any ) -> bool :
9173 """
92- Return true if the object is a 'concrete' pydantic V1 model.
74+ Return true if an object is a 'concrete' pydantic V1 model.
9375 """
9476 if not inspect .isclass (obj ):
9577 return False
96- elif obj is BaseModelV1 or obj is GenericModelV1 :
78+ elif obj is v1 . BaseModel :
9779 return False
98- elif GenericModelV1 and issubclass (obj , GenericModelV1 ):
99- return getattr (obj , "__concrete__" , False )
100- return issubclass (obj , BaseModelV1 )
80+ elif v1 .GenericModel and issubclass (obj , v1 .GenericModel ):
81+ return bool (obj .__concrete__ )
82+ else :
83+ return issubclass (obj , v1 .BaseModel )
10184
10285
103- def _is_pydantic_v2_model (obj : Any ) -> bool :
86+ def _is_v2_model (obj : Any ) -> bool :
10487 """
10588 Return true if an object is a 'concrete' pydantic V2 model.
10689 """
107- if not inspect . isclass ( obj ) :
90+ if not v2 . enabled :
10891 return False
109- elif obj is BaseModelV2 or BaseModelV2 is None :
92+ elif not inspect . isclass ( obj ) :
11093 return False
111- return issubclass (obj , BaseModelV2 ) and not getattr (
112- obj , "__pydantic_generic_metadata__" , {}
113- ).get ("parameters" )
94+ elif obj is v2 .BaseModel :
95+ return False
96+ elif not issubclass (obj , v2 .BaseModel ):
97+ return False
98+ generic_metadata = getattr (obj , "__pydantic_generic_metadata__" , {})
99+ generic_parameters = generic_metadata .get ("parameters" )
100+ return not generic_parameters
114101
115102
116103def _is_pydantic_model (obj : Any ) -> bool :
117104 """
118- Return true if an object is a valid model for either V1 or V2 of pydantic.
105+ Return true if an object is a concrete model for either V1 or V2 of pydantic.
119106 """
120- return _is_pydantic_v1_model (obj ) or _is_pydantic_v2_model (obj )
107+ return _is_v1_model (obj ) or _is_v2_model (obj )
121108
122109
123- def _has_null_variant (schema : Dict [str , Any ]) -> bool :
110+ def _is_nullable (schema : Dict [str , Any ]) -> bool :
124111 """
125112 Return true if a JSON schema has 'null' as one of its types.
126113 """
@@ -129,7 +116,7 @@ def _has_null_variant(schema: Dict[str, Any]) -> bool:
129116 if isinstance (schema .get ("type" ), list ) and "null" in schema ["type" ]:
130117 return True
131118 if isinstance (schema .get ("anyOf" ), list ):
132- return any (_has_null_variant (s ) for s in schema ["anyOf" ])
119+ return any (_is_nullable (s ) for s in schema ["anyOf" ])
133120 return False
134121
135122
@@ -139,15 +126,15 @@ def _get_model_config(model: Type[Any]) -> "Union[ConfigDict, Type[BaseConfig]]"
139126 In version 1 of pydantic, this is a class. In version 2, it's a dictionary.
140127 """
141128 if hasattr (model , "Config" ) and inspect .isclass (model .Config ):
142- return model .Config # type: ignore
129+ return model .Config
143130 return model .model_config
144131
145132
146133def _get_model_json_schema (model : Type [Any ]) -> Dict [str , Any ]:
147134 """
148135 Generate the JSON schema for a pydantic model.
149136 """
150- if _is_pydantic_v1_model (model ):
137+ if _is_v1_model (model ):
151138 return json .loads (model .schema_json ())
152139 return model .model_json_schema (mode = "serialization" )
153140
@@ -188,7 +175,7 @@ def _clean_json_schema(schema: Dict[str, Any], model: Any = None) -> None:
188175 for prop in properties .values ():
189176 prop .pop ("title" , None )
190177
191- if _is_pydantic_v1_model (model ):
178+ if _is_v1_model (model ):
192179 fields : List ["ModelField" ] = list (model .__fields__ .values ())
193180 for field in fields :
194181 try :
@@ -198,7 +185,7 @@ def _clean_json_schema(schema: Dict[str, Any], model: Any = None) -> None:
198185 prop = properties .get (name )
199186 if prop is None :
200187 continue
201- if _has_null_variant (prop ):
188+ if _is_nullable (prop ):
202189 continue
203190 properties [name ] = {"anyOf" : [prop , {"type" : "null" }]}
204191 except Exception :
@@ -254,8 +241,6 @@ def _schema_generation_overrides(
254241 Temporarily override the 'extra' setting for a model,
255242 changing it to 'forbid' unless it was EXPLICITLY set to 'allow'.
256243 This prevents '[k: string]: any' from automatically being added to every interface.
257-
258- TODO: check if overriding 'schema_extra' is necessary, or if there's a better way.
259244 """
260245 revert : Dict [str , Any ] = {}
261246 config = _get_model_config (model )
@@ -264,14 +249,10 @@ def _schema_generation_overrides(
264249 if config .get ("extra" ) != "allow" :
265250 revert ["extra" ] = config .get ("extra" )
266251 config ["extra" ] = "forbid"
267- revert ["json_schema_extra" ] = config .get ("json_schema_extra" )
268- config ["json_schema_extra" ] = staticmethod (_clean_json_schema )
269252 else :
270253 if config .extra != "allow" :
271254 revert ["extra" ] = config .extra
272255 config .extra = "forbid" # type: ignore
273- revert ["schema_extra" ] = config .schema_extra
274- config .schema_extra = staticmethod (_clean_json_schema ) # type: ignore
275256 yield
276257 finally :
277258 for key , value in revert .items ():
@@ -297,8 +278,8 @@ def _generate_json_schema(models: List[type]) -> str:
297278 models_by_name [name ] = model
298279 models_as_fields [name ] = (model , ...)
299280
300- use_v1_tools = any (issubclass (m , BaseModelV1 ) for m in models )
301- create_model = create_model_v1 if use_v1_tools else create_model_v2 # type: ignore
281+ use_v1_tools = any (issubclass (m , v1 . BaseModel ) for m in models )
282+ create_model = v1 . create_model if use_v1_tools else v2 . create_model # type: ignore
302283 master_model = create_model ("_Master_" , ** models_as_fields ) # type: ignore
303284 master_schema = _get_model_json_schema (master_model ) # type: ignore
304285
@@ -320,11 +301,14 @@ def generate_typescript_defs(
320301 """
321302 Convert the pydantic models in a python module into typescript interfaces.
322303
323- :param module: python module containing pydantic model definitions, ex: my_project.api.schemas
304+ :param module: python module containing pydantic model definitions.
305+ example: my_project.api.schemas
324306 :param output: file that the typescript definitions will be written to
325- :param exclude: optional, a tuple of names for pydantic models which should be omitted from the typescript output.
326- :param json2ts_cmd: optional, the command that will execute json2ts. Provide this if the executable is not
327- discoverable or if it's locally installed (ex: 'yarn json2ts').
307+ :param exclude: optional, a tuple of names for pydantic models which
308+ should be omitted from the typescript output.
309+ :param json2ts_cmd: optional, the command that will execute json2ts.
310+ Provide this if the executable is not discoverable
311+ or if it's locally installed (ex: 'yarn json2ts').
328312 """
329313 if " " not in json2ts_cmd and not shutil .which (json2ts_cmd ):
330314 raise Exception (
0 commit comments