1
- from typing import ClassVar , List , NamedTuple , Optional , Set , Tuple , Union
1
+ from itertools import chain
2
+ from typing import ClassVar , Dict , List , NamedTuple , Optional , Set , Tuple , Union
2
3
3
4
import attr
4
5
@@ -54,6 +55,18 @@ def get_imports(self, *, prefix: str) -> Set[str]:
54
55
return imports
55
56
56
57
58
+ def _merge_properties (first : Property , second : Property ) -> Union [Property , PropertyError ]:
59
+ if first .__class__ != second .__class__ :
60
+ return PropertyError (header = "Cannot merge properties" , detail = "Properties are two different types" )
61
+ nullable = first .nullable and second .nullable
62
+ required = first .required or second .required
63
+ first = attr .evolve (first , nullable = nullable , required = required )
64
+ second = attr .evolve (second , nullable = nullable , required = required )
65
+ if first != second :
66
+ return PropertyError (header = "Cannot merge properties" , detail = "Properties has conflicting values" )
67
+ return first
68
+
69
+
57
70
class _PropertyData (NamedTuple ):
58
71
optional_props : List [Property ]
59
72
required_props : List [Property ]
@@ -64,33 +77,50 @@ class _PropertyData(NamedTuple):
64
77
def _process_properties (* , data : oai .Schema , schemas : Schemas , class_name : str ) -> Union [_PropertyData , PropertyError ]:
65
78
from . import property_from_data
66
79
67
- required_properties : List [Property ] = []
68
- optional_properties : List [Property ] = []
80
+ properties : Dict [str , Property ] = {}
69
81
relative_imports : Set [str ] = set ()
70
82
required_set = set (data .required or [])
71
83
84
+ def _check_existing (prop : Property ) -> Union [Property , PropertyError ]:
85
+ existing = properties .get (prop .name )
86
+ prop_or_error = (existing and _merge_properties (existing , prop )) or prop
87
+ if isinstance (prop_or_error , PropertyError ):
88
+ prop_or_error .header = f"Found conflicting properties named { prop .name } when creating { class_name } "
89
+ return prop_or_error
90
+ properties [prop_or_error .name ] = prop_or_error
91
+ return prop_or_error
92
+
72
93
all_props = data .properties or {}
73
94
for sub_prop in data .allOf or []:
74
95
if isinstance (sub_prop , oai .Reference ):
75
96
source_name = Reference .from_ref (sub_prop .ref ).class_name
76
97
sub_model = schemas .models .get (source_name )
77
98
if sub_model is None :
78
99
return PropertyError (f"Reference { sub_prop .ref } not found" )
79
- required_properties .extend (sub_model .required_properties )
80
- optional_properties .extend (sub_model .optional_properties )
81
- relative_imports .update (sub_model .relative_imports )
100
+ for prop in chain (sub_model .required_properties , sub_model .optional_properties ):
101
+ prop_or_error = _check_existing (prop )
102
+ if isinstance (prop_or_error , PropertyError ):
103
+ return prop_or_error
82
104
else :
83
105
all_props .update (sub_prop .properties or {})
84
106
required_set .update (sub_prop .required or [])
85
107
86
108
for key , value in all_props .items ():
87
109
prop_required = key in required_set
88
- prop , schemas = property_from_data (
110
+ prop_or_error , schemas = property_from_data (
89
111
name = key , required = prop_required , data = value , schemas = schemas , parent_name = class_name
90
112
)
91
- if isinstance (prop , PropertyError ):
92
- return prop
93
- if prop_required and not prop .nullable :
113
+ if isinstance (prop_or_error , Property ):
114
+ prop_or_error = _check_existing (prop_or_error )
115
+ if isinstance (prop_or_error , PropertyError ):
116
+ return prop_or_error
117
+
118
+ properties [prop_or_error .name ] = prop_or_error
119
+
120
+ required_properties = []
121
+ optional_properties = []
122
+ for prop in properties .values ():
123
+ if prop .required and not prop .nullable :
94
124
required_properties .append (prop )
95
125
else :
96
126
optional_properties .append (prop )
0 commit comments