@@ -19,6 +19,7 @@ class NoneProperty(Property):
1919 """ A property that is always None (used for empty schemas) """
2020
2121 _type_string : ClassVar [str ] = "None"
22+ _json_type_string : ClassVar [str ] = "None"
2223 template : ClassVar [Optional [str ]] = "none_property.pyi"
2324
2425
@@ -29,6 +30,7 @@ class StringProperty(Property):
2930 max_length : Optional [int ] = None
3031 pattern : Optional [str ] = None
3132 _type_string : ClassVar [str ] = "str"
33+ _json_type_string : ClassVar [str ] = "str"
3234
3335
3436@attr .s (auto_attribs = True , frozen = True )
@@ -38,6 +40,7 @@ class DateTimeProperty(Property):
3840 """
3941
4042 _type_string : ClassVar [str ] = "datetime.datetime"
43+ _json_type_string : ClassVar [str ] = "str"
4144 template : ClassVar [str ] = "datetime_property.pyi"
4245
4346 def get_imports (self , * , prefix : str ) -> Set [str ]:
@@ -58,6 +61,7 @@ class DateProperty(Property):
5861 """ A property of type datetime.date """
5962
6063 _type_string : ClassVar [str ] = "datetime.date"
64+ _json_type_string : ClassVar [str ] = "str"
6165 template : ClassVar [str ] = "date_property.pyi"
6266
6367 def get_imports (self , * , prefix : str ) -> Set [str ]:
@@ -78,6 +82,8 @@ class FileProperty(Property):
7882 """ A property used for uploading files """
7983
8084 _type_string : ClassVar [str ] = "File"
85+ # Return type of File.to_tuple()
86+ _json_type_string : ClassVar [str ] = "Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]]"
8187 template : ClassVar [str ] = "file_property.pyi"
8288
8389 def get_imports (self , * , prefix : str ) -> Set [str ]:
@@ -98,20 +104,23 @@ class FloatProperty(Property):
98104 """ A property of type float """
99105
100106 _type_string : ClassVar [str ] = "float"
107+ _json_type_string : ClassVar [str ] = "float"
101108
102109
103110@attr .s (auto_attribs = True , frozen = True )
104111class IntProperty (Property ):
105112 """ A property of type int """
106113
107114 _type_string : ClassVar [str ] = "int"
115+ _json_type_string : ClassVar [str ] = "int"
108116
109117
110118@attr .s (auto_attribs = True , frozen = True )
111119class BooleanProperty (Property ):
112120 """ Property for bool """
113121
114122 _type_string : ClassVar [str ] = "bool"
123+ _json_type_string : ClassVar [str ] = "bool"
115124
116125
117126InnerProp = TypeVar ("InnerProp" , bound = Property )
@@ -122,18 +131,11 @@ class ListProperty(Property, Generic[InnerProp]):
122131 """ A property representing a list (array) of other properties """
123132
124133 inner_property : InnerProp
134+ _json_type_string : ClassVar [str ] = "List[Any]"
125135 template : ClassVar [str ] = "list_property.pyi"
126136
127- def get_type_string (self , no_optional : bool = False ) -> str :
128- """ Get a string representation of type that should be used when declaring this property """
129- type_string = f"List[{ self .inner_property .get_type_string ()} ]"
130- if no_optional :
131- return type_string
132- if self .nullable :
133- type_string = f"Optional[{ type_string } ]"
134- if not self .required :
135- type_string = f"Union[Unset, { type_string } ]"
136- return type_string
137+ def get_base_type_string (self ) -> str :
138+ return f"List[{ self .inner_property .get_type_string ()} ]"
137139
138140 def get_instance_type_string (self ) -> str :
139141 """Get a string representation of runtime type that should be used for `isinstance` checks"""
@@ -167,18 +169,38 @@ def __attrs_post_init__(self) -> None:
167169 self , "has_properties_without_templates" , any (prop .template is None for prop in self .inner_properties )
168170 )
169171
170- def get_type_string (self , no_optional : bool = False ) -> str :
171- """ Get a string representation of type that should be used when declaring this property """
172- inner_types = [p .get_type_string (no_optional = True ) for p in self .inner_properties ]
173- inner_prop_string = ", " .join (inner_types )
174- type_string = f"Union[{ inner_prop_string } ]"
172+ def _get_inner_prop_string (self , json : bool = False ) -> str :
173+ inner_types = [p .get_type_string (no_optional = True , json = json ) for p in self .inner_properties ]
174+ unique_inner_types = list (dict .fromkeys (inner_types ))
175+ return ", " .join (unique_inner_types )
176+
177+ def get_base_type_string (self , json : bool = False ) -> str :
178+ return f"Union[{ self ._get_inner_prop_string (json = json )} ]"
179+
180+ def get_type_string (self , no_optional : bool = False , query_parameter : bool = False , json : bool = False ) -> str :
181+ """
182+ Get a string representation of type that should be used when declaring this property.
183+
184+ This implementation differs slightly from `Property.get_type_string` in order to collapse
185+ nested union types.
186+ """
187+ type_string = self .get_base_type_string (json = json )
175188 if no_optional :
176189 return type_string
177- if not self .required :
178- type_string = f"Union[Unset, { inner_prop_string } ]"
179- if self .nullable :
180- type_string = f"Optional[{ type_string } ]"
181- return type_string
190+ if self .required :
191+ if self .nullable :
192+ return f"Union[None, { self ._get_inner_prop_string (json = json )} ]"
193+ else :
194+ return type_string
195+ else :
196+ if self .nullable :
197+ return f"Union[Unset, None, { self ._get_inner_prop_string (json = json )} ]"
198+ else :
199+ if query_parameter :
200+ # For query parameters, None has the same meaning as Unset
201+ return f"Union[Unset, None, { self ._get_inner_prop_string (json = json )} ]"
202+ else :
203+ return f"Union[Unset, { self ._get_inner_prop_string (json = json )} ]"
182204
183205 def get_imports (self , * , prefix : str ) -> Set [str ]:
184206 """
@@ -250,23 +272,13 @@ def build_model_property(
250272 required_properties : List [Property ] = []
251273 optional_properties : List [Property ] = []
252274 relative_imports : Set [str ] = set ()
253- references : List [oai .Reference ] = []
254275
255276 class_name = data .title or name
256277 if parent_name :
257278 class_name = f"{ utils .pascal_case (parent_name )} { utils .pascal_case (class_name )} "
258279 ref = Reference .from_ref (class_name )
259280
260- all_props = data .properties or {}
261- if not isinstance (data , oai .Reference ) and data .allOf :
262- for sub_prop in data .allOf :
263- if isinstance (sub_prop , oai .Reference ):
264- references += [sub_prop ]
265- else :
266- all_props .update (sub_prop .properties or {})
267- required_set .update (sub_prop .required or [])
268-
269- for key , value in all_props .items ():
281+ for key , value in (data .properties or {}).items ():
270282 prop_required = key in required_set
271283 prop , schemas = property_from_data (
272284 name = key , required = prop_required , data = value , schemas = schemas , parent_name = class_name
@@ -302,7 +314,6 @@ def build_model_property(
302314
303315 prop = ModelProperty (
304316 reference = ref ,
305- references = references ,
306317 required_properties = required_properties ,
307318 optional_properties = optional_properties ,
308319 relative_imports = relative_imports ,
@@ -460,6 +471,9 @@ def _property_from_data(
460471 )
461472 if data .anyOf or data .oneOf :
462473 return build_union_property (data = data , name = name , required = required , schemas = schemas , parent_name = parent_name )
474+ if not data .type :
475+ return NoneProperty (name = name , required = required , nullable = False , default = None ), schemas
476+
463477 if data .type == "string" :
464478 return _string_based_property (name = name , required = required , data = data ), schemas
465479 elif data .type == "number" :
@@ -494,10 +508,8 @@ def _property_from_data(
494508 )
495509 elif data .type == "array" :
496510 return build_list_property (data = data , name = name , required = required , schemas = schemas , parent_name = parent_name )
497- elif data .type == "object" or data . allOf :
511+ elif data .type == "object" :
498512 return build_model_property (data = data , name = name , schemas = schemas , required = required , parent_name = parent_name )
499- elif not data .type :
500- return NoneProperty (name = name , required = required , nullable = False , default = None ), schemas
501513 return PropertyError (data = data , detail = f"unknown type { data .type } " ), schemas
502514
503515
@@ -554,16 +566,6 @@ def build_schemas(*, components: Dict[str, Union[oai.Reference, oai.Schema]]) ->
554566 schemas = schemas_or_err
555567 processing = True # We made some progress this round, do another after it's done
556568 to_process = next_round
557-
558- resolve_errors : List [PropertyError ] = []
559- models = list (schemas .models .values ())
560- for model in models :
561- schemas_or_err = model .resolve_references (components = components , schemas = schemas )
562- if isinstance (schemas_or_err , PropertyError ):
563- resolve_errors .append (schemas_or_err )
564- else :
565- schemas = schemas_or_err
566-
567569 schemas .errors .extend (errors )
568- schemas . errors . extend ( resolve_errors )
570+
569571 return schemas
0 commit comments