1
1
from __future__ import annotations
2
2
3
+ from dataclasses import dataclass
3
4
from itertools import chain
4
5
from typing import Any , ClassVar , NamedTuple
5
6
14
15
from .schemas import Class , ReferencePath , Schemas , parse_reference_path
15
16
16
17
18
+ @dataclass
19
+ class ModelDetails :
20
+ required_properties : list [Property ] | None = None
21
+ optional_properties : list [Property ] | None = None
22
+ additional_properties : Property | None = None
23
+ relative_imports : set [str ] | None = None
24
+ lazy_imports : set [str ] | None = None
25
+
26
+
17
27
@define
18
28
class ModelProperty (PropertyProtocol ):
19
29
"""A property which refers to another Schema"""
@@ -27,11 +37,7 @@ class ModelProperty(PropertyProtocol):
27
37
data : oai .Schema
28
38
description : str
29
39
roots : set [ReferencePath | utils .ClassName ]
30
- required_properties : list [Property ] | None
31
- optional_properties : list [Property ] | None
32
- relative_imports : set [str ] | None
33
- lazy_imports : set [str ] | None
34
- additional_properties : Property | None
40
+ details : ModelDetails
35
41
_json_type_string : ClassVar [str ] = "Dict[str, Any]"
36
42
37
43
template : ClassVar [str ] = "model_property.py.jinja"
@@ -75,22 +81,18 @@ def build(
75
81
class_string = title
76
82
class_info = Class .from_string (string = class_string , config = config )
77
83
model_roots = {* roots , class_info .name }
78
- required_properties : list [Property ] | None = None
79
- optional_properties : list [Property ] | None = None
80
- relative_imports : set [str ] | None = None
81
- lazy_imports : set [str ] | None = None
82
- additional_properties : Property | None = None
84
+ details = ModelDetails ()
83
85
if process_properties :
84
86
data_or_err , schemas = _process_property_data (
85
87
data = data , schemas = schemas , class_info = class_info , config = config , roots = model_roots
86
88
)
87
89
if isinstance (data_or_err , PropertyError ):
88
90
return data_or_err , schemas
89
- property_data , additional_properties = data_or_err
90
- required_properties = property_data .required_props
91
- optional_properties = property_data .optional_props
92
- relative_imports = property_data .relative_imports
93
- lazy_imports = property_data .lazy_imports
91
+ property_data , details . additional_properties = data_or_err
92
+ details . required_properties = property_data .required_props
93
+ details . optional_properties = property_data .optional_props
94
+ details . relative_imports = property_data .relative_imports
95
+ details . lazy_imports = property_data .lazy_imports
94
96
for root in roots :
95
97
if isinstance (root , utils .ClassName ):
96
98
continue
@@ -100,11 +102,7 @@ def build(
100
102
class_info = class_info ,
101
103
data = data ,
102
104
roots = model_roots ,
103
- required_properties = required_properties ,
104
- optional_properties = optional_properties ,
105
- relative_imports = relative_imports ,
106
- lazy_imports = lazy_imports ,
107
- additional_properties = additional_properties ,
105
+ details = details ,
108
106
description = data .description or "" ,
109
107
default = None ,
110
108
required = required ,
@@ -125,14 +123,39 @@ def build(
125
123
)
126
124
return prop , schemas
127
125
126
+ def needs_processing (self ) -> bool :
127
+ return not (
128
+ isinstance (self .details .required_properties , list ) and isinstance (self .details .optional_properties , list )
129
+ )
130
+
131
+ @property
132
+ def required_properties (self ) -> list [Property ]:
133
+ return self .details .required_properties or []
134
+
135
+ @property
136
+ def optional_properties (self ) -> list [Property ]:
137
+ return self .details .optional_properties or []
138
+
139
+ @property
140
+ def additional_properties (self ) -> Property | None :
141
+ return self .details .additional_properties
142
+
143
+ @property
144
+ def relative_imports (self ) -> set [str ]:
145
+ return self .details .relative_imports or set ()
146
+
147
+ @property
148
+ def lazy_imports (self ) -> set [str ] | None :
149
+ return self .details .lazy_imports or set ()
150
+
128
151
@classmethod
129
152
def convert_value (cls , value : Any ) -> Value | None | PropertyError :
130
153
if value is not None :
131
154
return PropertyError (detail = "ModelProperty cannot have a default value" ) # pragma: no cover
132
155
return None
133
156
134
157
def __attrs_post_init__ (self ) -> None :
135
- if self .relative_imports :
158
+ if self .details . relative_imports :
136
159
self .set_relative_imports (self .relative_imports )
137
160
138
161
@property
@@ -175,15 +198,15 @@ def set_relative_imports(self, relative_imports: set[str]) -> None:
175
198
Args:
176
199
relative_imports: The set of relative import strings
177
200
"""
178
- object . __setattr__ ( self , " relative_imports" , {ri for ri in relative_imports if self .self_import not in ri })
201
+ self . details . relative_imports = {ri for ri in relative_imports if self .self_import not in ri }
179
202
180
203
def set_lazy_imports (self , lazy_imports : set [str ]) -> None :
181
204
"""Set the lazy imports set for this ModelProperty, filtering out self imports
182
205
183
206
Args:
184
207
lazy_imports: The set of lazy import strings
185
208
"""
186
- object . __setattr__ ( self , " lazy_imports" , {li for li in lazy_imports if self .self_import not in li })
209
+ self . details . lazy_imports = {li for li in lazy_imports if self .self_import not in li }
187
210
188
211
def get_type_string (
189
212
self ,
@@ -289,9 +312,7 @@ def _add_if_no_conflict(new_prop: Property) -> PropertyError | None:
289
312
if not isinstance (sub_model , ModelProperty ):
290
313
return PropertyError ("Cannot take allOf a non-object" )
291
314
# Properties of allOf references first should be processed first
292
- if not (
293
- isinstance (sub_model .required_properties , list ) and isinstance (sub_model .optional_properties , list )
294
- ):
315
+ if sub_model .needs_processing ():
295
316
return PropertyError (f"Reference { sub_model .name } in allOf was not processed" , data = sub_prop )
296
317
for prop in chain (sub_model .required_properties , sub_model .optional_properties ):
297
318
err = _add_if_no_conflict (prop )
@@ -437,9 +458,10 @@ def process_model(model_prop: ModelProperty, *, schemas: Schemas, config: Config
437
458
438
459
property_data , additional_properties = data_or_err
439
460
440
- object .__setattr__ (model_prop , "required_properties" , property_data .required_props )
441
- object .__setattr__ (model_prop , "optional_properties" , property_data .optional_props )
461
+ model_prop .details .required_properties = property_data .required_props
462
+ model_prop .details .optional_properties = property_data .optional_props
463
+ model_prop .details .additional_properties = additional_properties
442
464
model_prop .set_relative_imports (property_data .relative_imports )
443
465
model_prop .set_lazy_imports (property_data .lazy_imports )
444
- object . __setattr__ ( model_prop , "additional_properties" , additional_properties )
466
+
445
467
return schemas
0 commit comments