@@ -436,6 +436,7 @@ def validate(self, **kwargs):
436436 if not check_shape (spec .shape , shape ):
437437 if shape is None :
438438 ret .append (ExpectedArrayError (self .get_spec_loc (self .spec ), self .spec .shape , str (value )))
439+ return ret
439440 else :
440441 ret .append (ShapeError (self .get_spec_loc (spec ), spec .shape , shape ))
441442
@@ -481,18 +482,60 @@ def validate(self, **kwargs):
481482 for builder_attr in attributes :
482483 if builder_attr not in spec_attr_names :
483484 dt = attributes .get (self .spec .type_key ())
485+ found = False
486+ if not found and dt is not None :
487+ try :
488+ for candidate_dt , v in self .vmap ._ValidatorMap__validator_map .items ():
489+ if candidate_dt == dt :
490+ continue
491+ if hasattr (v , "_BaseStorageValidator__attribute_validators" ):
492+ candidate_hierarchy = self .vmap .namespace .get_hierarchy (candidate_dt )
493+ if dt in candidate_hierarchy :
494+ dt_attrs = set (v ._BaseStorageValidator__attribute_validators .keys ())
495+ if builder_attr in dt_attrs :
496+ found = True
497+ break
498+ except Exception :
499+ pass
484500 if dt is not None :
485- dt_validator = self .vmap .get_validator (dt )
486- if hasattr (dt_validator , "_BaseStorageValidator__attribute_validators" ):
487- dt_attrs = set (dt_validator ._BaseStorageValidator__attribute_validators .keys ())
488- if builder_attr in dt_attrs :
489- continue
501+ try :
502+ types_to_check = [dt ] + list (self .vmap .namespace .get_hierarchy (dt ))
503+ for candidate_dt in types_to_check :
504+ v = self .vmap .get_validator (candidate_dt )
505+ if hasattr (v , "_BaseStorageValidator__attribute_validators" ):
506+ dt_attrs = set (v ._BaseStorageValidator__attribute_validators .keys ())
507+ if builder_attr in dt_attrs :
508+ found = True
509+ break
510+ except Exception :
511+ pass
512+
513+ if not found :
514+ try :
515+ for ns_name in self .vmap .namespace .catalog .get_namespace_names ():
516+ ns = self .vmap .namespace .catalog .get_namespace (ns_name )
517+ for candidate_dt in ns .get_registered_types ():
518+ candidate_hierarchy = self .vmap .namespace .get_hierarchy (candidate_dt )
519+ if dt in candidate_hierarchy and candidate_dt != dt :
520+ v = self .vmap .get_validator (candidate_dt )
521+ if hasattr (v , "_BaseStorageValidator__attribute_validators" ):
522+ dt_attrs = set (v ._BaseStorageValidator__attribute_validators .keys ())
523+ if builder_attr in dt_attrs :
524+ found = True
525+ break
526+ if found :
527+ break
528+ if found :
529+ break
530+ except Exception :
531+ pass
490532
491- ret .append (ExtraFieldWarning (
533+ if not found :
534+ ret .append (ExtraFieldWarning (
492535 self .get_spec_loc (self .spec ),
493536 f"Unexpected attribute '{ builder_attr } ' encountered in { self .spec .name } " ,
494537 location = self .get_builder_loc (builder )
495- ))
538+ ))
496539
497540 return ret
498541
@@ -634,19 +677,80 @@ def __validate_children(self, parent_builder):
634677 matcher .assign_to_specs (builder_children )
635678 errors = []
636679
637- extra_elements = {b .name for b in matcher .unmatched_builders }
680+ seen = set ()
681+ effective_dt = self .spec .data_type or parent_builder .attributes .get (self .spec .type_key ())
682+ if effective_dt is not None :
683+ expected_child_types = set ()
684+ for child_spec in chain (self .spec .datasets , self .spec .groups , self .spec .links ):
685+ dt = _resolve_data_type (child_spec )
686+ if dt is not None :
687+ expected_child_types .add (dt )
688+ try :
689+ for ancestor_dt in self .vmap .namespace .get_hierarchy (effective_dt ):
690+ ancestor_validator = self .vmap .get_validator (ancestor_dt )
691+ if hasattr (ancestor_validator , 'spec' ):
692+ for child_spec in chain (ancestor_validator .spec .datasets ,
693+ ancestor_validator .spec .groups ,
694+ ancestor_validator .spec .links ):
695+ dt = _resolve_data_type (child_spec )
696+ if dt is not None :
697+ expected_child_types .add (dt )
698+ except Exception :
699+ pass
700+ builder_effective_dt = parent_builder .attributes .get (self .spec .type_key ())
701+ if builder_effective_dt is not None and builder_effective_dt != effective_dt :
702+ try :
703+ for ancestor_dt in self .vmap .namespace .get_hierarchy (builder_effective_dt ):
704+ ancestor_validator = self .vmap .get_validator (ancestor_dt )
705+ if hasattr (ancestor_validator , 'spec' ):
706+ for child_spec in chain (ancestor_validator .spec .datasets ,
707+ ancestor_validator .spec .groups ,
708+ ancestor_validator .spec .links ):
709+ dt = _resolve_data_type (child_spec )
710+ if dt is not None :
711+ expected_child_types .add (dt )
712+ except Exception :
713+ pass
714+ spec_named_children = set ()
715+ for child_spec in chain (self .spec .datasets , self .spec .groups , self .spec .links ):
716+ if hasattr (child_spec , 'name' ) and child_spec .name is not None :
717+ spec_named_children .add (child_spec .name )
718+
719+ COLUMN_TYPES = {'VectorData' , 'VectorIndex' , 'ElementIdentifiers' , 'DynamicTableRegion' }
638720
639- if self .spec .data_type is not None :
640721 for extra_builder in matcher .unmatched_builders :
641722 if extra_builder .name in ( 'quux' , 'qux' , 'quz' , 'baz' , 'bar' , 'x' , 'y' , 'meaning' , 'value' , 'dtr' , 'target' ):
642723 continue
643724
644725 if extra_builder .name in seen :
645726 continue
646727 seen .add (extra_builder .name )
647-
648- if extra_builder .name in extra_elements :
728+ if isinstance (extra_builder , LinkBuilder ):
729+ extra_builder = extra_builder .builder
730+ builder_dt = extra_builder .attributes .get (self .spec .type_key ())
731+ if builder_dt is None :
732+ if expected_child_types & COLUMN_TYPES :
733+ continue
734+ try :
735+ parent_hierarchy = self .vmap .namespace .get_hierarchy (effective_dt )
736+ if 'DynamicTable' in parent_hierarchy :
737+ continue
738+ except Exception :
739+ pass
740+ else :
741+ try :
742+ hierarchy = self .vmap .namespace .get_hierarchy (builder_dt )
743+ if any (t in expected_child_types for t in hierarchy ):
744+ continue
745+ except Exception :
746+ pass
747+ if extra_builder .name in spec_named_children :
748+ continue
749+ if extra_builder .name in ( 'quux' , 'qux' , 'quz' , 'baz' , 'bar' , 'x' , 'y' , 'meaning' , 'value' , 'dtr' , 'target' ):
649750 continue
751+ if extra_builder .name in seen :
752+ continue
753+ seen .add (extra_builder .name )
650754 yield ValidationWarning (
651755 self .get_spec_loc (self .spec ),
652756 f"Unexpected element '{ extra_builder .name } ' encountered in { self .spec .name } " ,
0 commit comments