@@ -181,6 +181,10 @@ def __init__(self, path: Sequence[Optional[str]], str_rep: Optional[str] = None)
181181 def root (self ) -> bool :
182182 return not bool (self .path )
183183
184+ @property
185+ def single (self ) -> bool :
186+ return len (self .path ) == 1
187+
184188 def child (self , part : Optional [str ]) -> PropertyPath :
185189 update = list (self .path )
186190 update .append (part )
@@ -189,6 +193,10 @@ def child(self, part: Optional[str]) -> PropertyPath:
189193 def unescaped_parts (self ) -> List [str ]:
190194 return [p .rstrip ("[]" ).strip ("`" ) for p in self .path if p is not None ]
191195
196+ @property
197+ def first_part (self ) -> Optional [str ]:
198+ return self .path [0 ] if self .path else None
199+
192200 @property
193201 def last_part (self ) -> Optional [str ]:
194202 return self .path [- 1 ] if self .path else None
@@ -926,9 +934,8 @@ def __init__(
926934 self .__resolved_props : Dict [str , Tuple [Property , Kind ]] = {}
927935 self .__resolved_direct_props : Dict [str , Tuple [Property , Kind ]] = {}
928936 self .__resolved_bases : Dict [str , ComplexKind ] = {}
929- self .__owner_lookup : Dict [PropertyPath , ComplexKind ] = {}
930937 self .__all_props : List [Property ] = list (self .properties )
931- self .__resolved_hierarchy : Set [str ] = {fqn }
938+ self .__resolved_hierarchy : Dict [str , ComplexKind ] = {fqn : self }
932939 self .__property_by_path : List [ResolvedPropertyPath ] = []
933940 self .__synthetic_props : List [ResolvedPropertyPath ] = []
934941
@@ -999,7 +1006,7 @@ def resolve(self, model: Dict[str, Kind]) -> None:
9991006 self .__resolved_hierarchy .update (base .__resolved_hierarchy )
10001007
10011008 # property path -> kind
1002- self .__property_by_path , self . __owner_lookup = ComplexKind .resolve_properties (self , model )
1009+ self .__property_by_path = ComplexKind .resolve_properties (self , model )
10031010 self .__synthetic_props = [p for p in self .__property_by_path if p .prop .synthetic ]
10041011
10051012 # resolve predecessor kinds
@@ -1054,6 +1061,9 @@ def is_root(self) -> bool:
10541061 return not self .bases or (len (self .bases ) == 1 and self .bases [0 ] == self .fqn )
10551062
10561063 def kind_hierarchy (self ) -> Set [str ]:
1064+ return set (self .__resolved_hierarchy .keys ())
1065+
1066+ def resolved_hierarchy (self ) -> Dict [str , ComplexKind ]:
10571067 return self .__resolved_hierarchy
10581068
10591069 def is_a (self , kind : str ) -> bool :
@@ -1062,9 +1072,6 @@ def is_a(self, kind: str) -> bool:
10621072 def resolved_property_paths (self ) -> List [ResolvedPropertyPath ]:
10631073 return self .__property_by_path
10641074
1065- def owned_paths (self ) -> Dict [PropertyPath , ComplexKind ]:
1066- return self .__owner_lookup
1067-
10681075 def resolved_bases (self ) -> Dict [str , ComplexKind ]:
10691076 return self .__resolved_bases
10701077
@@ -1257,14 +1264,10 @@ def walk_element(
12571264 return walk_element (elem , self , initial_level , overrides = overrides )
12581265
12591266 @staticmethod
1260- def resolve_properties (
1261- complex_kind : ComplexKind , model : Dict [str , Kind ]
1262- ) -> Tuple [List [ResolvedPropertyPath ], Dict [PropertyPath , ComplexKind ]]:
1267+ def resolve_properties (complex_kind : ComplexKind , model : Dict [str , Kind ]) -> List [ResolvedPropertyPath ]:
12631268 result : List [ResolvedPropertyPath ] = []
1264- owner_lookup : Dict [PropertyPath , ComplexKind ] = {}
12651269
12661270 def path_for (
1267- owner : ComplexKind ,
12681271 prop : Property ,
12691272 kind : Kind ,
12701273 path : PropertyPath ,
@@ -1284,30 +1287,24 @@ def path_for(
12841287 kind .resolve (model )
12851288 if isinstance (kind , SimpleKind ):
12861289 result .append (ResolvedPropertyPath (relative , prop , kind ))
1287- owner_lookup [relative ] = owner
12881290 elif isinstance (kind , ArrayKind ):
12891291 if name := relative .last_part :
12901292 result .append (ResolvedPropertyPath (relative , Property (name , kind .fqn ), kind ))
1291- owner_lookup [relative ] = owner
1292- path_for (owner , prop , kind .inner , path , visited_kinds , True )
1293+ path_for (prop , kind .inner , path , visited_kinds , True )
12931294 elif isinstance (kind , DictionaryKind ):
12941295 child = relative .child (None )
12951296 if name := relative .last_part :
12961297 result .append (ResolvedPropertyPath (relative , Property (name , kind .fqn ), kind ))
1297- owner_lookup [relative ] = owner
12981298 # Any child path accessing this dictionary will get a property of value kind.
12991299 value = kind .value_kind
13001300 result .append (ResolvedPropertyPath (child , Property ("any" , value .fqn ), value ))
1301- owner_lookup [child ] = owner
1302- path_for (owner , prop , kind .value_kind , child , visited_kinds , add_prop_to_path = False )
1301+ path_for (prop , kind .value_kind , child , visited_kinds , add_prop_to_path = False )
13031302 elif isinstance (kind , ComplexKind ):
13041303 if name := relative .last_part :
13051304 result .append (ResolvedPropertyPath (relative , Property (name , kind .fqn ), kind ))
1306- owner_lookup [relative ] = owner
1307- for_complex_kind (owner , kind , relative , visited_kinds )
1305+ for_complex_kind (kind , relative , visited_kinds )
13081306
13091307 def for_complex_kind (
1310- owner : ComplexKind ,
13111308 current : ComplexKind ,
13121309 relative : PropertyPath ,
13131310 visited_kinds : Optional [Dict [str , Set [str ]]] = None ,
@@ -1318,12 +1315,10 @@ def for_complex_kind(
13181315 if isinstance (cpx := model .get (cpx_fqn ), ComplexKind ):
13191316 cpx .resolve (model )
13201317 for prop in cpx .properties :
1321- path_for (
1322- owner , prop , cpx .__resolved_props [prop .name ][1 ], relative , visited_kinds or defaultdict (set )
1323- )
1318+ path_for (prop , cpx .__resolved_props [prop .name ][1 ], relative , visited_kinds or defaultdict (set ))
13241319
1325- for_complex_kind (complex_kind , complex_kind , PropertyPath ([], "" ))
1326- return result , owner_lookup
1320+ for_complex_kind (complex_kind , PropertyPath ([], "" ))
1321+ return result
13271322
13281323
13291324string_kind = StringKind ("string" )
@@ -1411,26 +1406,30 @@ def from_kinds(kinds: List[Kind]) -> Model:
14111406 prop_kinds_by_path = {}
14121407 # lookup map to get the aggregate root that defined a specific property path
14131408 # Example: instance_cores
1414- complex_by_path_distinct : Dict [PropertyPath , Dict [str , ComplexKind ]] = defaultdict (dict )
1409+ complex_by_prop_distinct : Dict [str , Dict [str , ComplexKind ]] = defaultdict (dict )
14151410 for c in all_kinds :
14161411 if isinstance (c , ComplexKind ) and c .aggregate_root :
14171412 for r in c .resolved_property_paths ():
14181413 prop_kinds_by_path [r .path ] = r
1419- for path , cpl in c .owned_paths ().items ():
1420- complex_by_path_distinct [path ][cpl .fqn ] = cpl
1414+ for cpl in c .resolved_hierarchy ().values ():
1415+ for prop in cpl .properties :
1416+ complex_by_prop_distinct [prop .name ][cpl .fqn ] = cpl
14211417
1422- complex_by_path = {k : list (v .values ()) for k , v in complex_by_path_distinct .items ()}
1423- return Model (kind_dict , list (prop_kinds_by_path .values ()), complex_by_path )
1418+ complex_by_prop = {k : list (v .values ()) for k , v in complex_by_prop_distinct .items ()}
1419+ return Model (kind_dict , list (prop_kinds_by_path .values ()), complex_by_prop )
14241420
14251421 def __init__ (
14261422 self ,
14271423 kinds : Dict [str , Kind ],
14281424 property_kind_by_path : List [ResolvedPropertyPath ],
1429- complex_kinds_by_path : Dict [PropertyPath , List [ComplexKind ]],
1425+ complex_kinds_by_root_prop : Dict [str , List [ComplexKind ]],
14301426 ):
1427+ # all kinds of this model
14311428 self .kinds = kinds
1429+ # all possible paths in the model to efficiently look up the kind of property
14321430 self .__property_kind_by_path : List [ResolvedPropertyPath ] = property_kind_by_path
1433- self .__complex_kinds_by_path : Dict [PropertyPath , List [ComplexKind ]] = complex_kinds_by_path
1431+ # only the root paths point to the owners: foo.bla[*].bar -> lookup foo
1432+ self .__complex_kinds_by_root_prop : Dict [str , List [ComplexKind ]] = complex_kinds_by_root_prop
14341433
14351434 def __contains__ (self , name_or_object : Union [str , Json ]) -> bool :
14361435 if isinstance (name_or_object , str ):
@@ -1481,8 +1480,8 @@ def kind_by_path(self, path: Union[str, List[str]]) -> Kind:
14811480 return self .property_by_path (path ).kind
14821481
14831482 def owners_by_path (self , path_ : Union [str , List [str ]]) -> List [ComplexKind ]:
1484- path = PropertyPath .from_string (path_ ) if isinstance (path_ , str ) else PropertyPath .from_list (path_ )
1485- return self .__complex_kinds_by_path .get (path , [])
1483+ prop_path = PropertyPath .from_string (path_ ) if isinstance (path_ , str ) else PropertyPath .from_list (path_ )
1484+ return self .__complex_kinds_by_root_prop .get (p , []) if ( p := prop_path . first_part ) else []
14861485
14871486 def coerce (self , js : Json ) -> Json :
14881487 try :
@@ -1669,7 +1668,7 @@ def all_predecessor_kinds(kind: ComplexKind) -> Dict[EdgeType, List[str]]:
16691668 )
16701669 else :
16711670 result [kind .fqn ] = kind
1672- return Model (result , self .__property_kind_by_path , self .__complex_kinds_by_path )
1671+ return Model (result , self .__property_kind_by_path , self .__complex_kinds_by_root_prop )
16731672
16741673 def filter_complex (
16751674 self , filter_fn : Callable [[ComplexKind ], bool ], with_bases : bool = True , with_prop_types : bool = True
@@ -1699,7 +1698,7 @@ def add_kind(cpl: ComplexKind) -> None:
16991698 if isinstance (kind , ComplexKind ) and filter_fn (kind ):
17001699 add_kind (kind )
17011700
1702- return Model (kinds , self .__property_kind_by_path , self .__complex_kinds_by_path )
1701+ return Model (kinds , self .__property_kind_by_path , self .__complex_kinds_by_root_prop )
17031702
17041703 def complete_path (
17051704 self ,
0 commit comments