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
23
34import attr
45
@@ -54,6 +55,18 @@ def get_imports(self, *, prefix: str) -> Set[str]:
5455 return imports
5556
5657
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+
5770class _PropertyData (NamedTuple ):
5871 optional_props : List [Property ]
5972 required_props : List [Property ]
@@ -64,33 +77,50 @@ class _PropertyData(NamedTuple):
6477def _process_properties (* , data : oai .Schema , schemas : Schemas , class_name : str ) -> Union [_PropertyData , PropertyError ]:
6578 from . import property_from_data
6679
67- required_properties : List [Property ] = []
68- optional_properties : List [Property ] = []
80+ properties : Dict [str , Property ] = {}
6981 relative_imports : Set [str ] = set ()
7082 required_set = set (data .required or [])
7183
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+
7293 all_props = data .properties or {}
7394 for sub_prop in data .allOf or []:
7495 if isinstance (sub_prop , oai .Reference ):
7596 source_name = Reference .from_ref (sub_prop .ref ).class_name
7697 sub_model = schemas .models .get (source_name )
7798 if sub_model is None :
7899 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
82104 else :
83105 all_props .update (sub_prop .properties or {})
84106 required_set .update (sub_prop .required or [])
85107
86108 for key , value in all_props .items ():
87109 prop_required = key in required_set
88- prop , schemas = property_from_data (
110+ prop_or_error , schemas = property_from_data (
89111 name = key , required = prop_required , data = value , schemas = schemas , parent_name = class_name
90112 )
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 :
94124 required_properties .append (prop )
95125 else :
96126 optional_properties .append (prop )
0 commit comments