@@ -323,7 +323,7 @@ def __init__(self, layout, target=None, *, name=None, reset=None, reset_less=Non
323323 try :
324324 cast_layout = Layout .cast (layout )
325325 except TypeError as e :
326- raise TypeError ("View layout must be a Layout instance , not {!r}"
326+ raise TypeError ("View layout must be a layout , not {!r}"
327327 .format (layout )) from e
328328 if target is not None :
329329 if (name is not None or reset is not None or reset_less is not None or
@@ -398,28 +398,51 @@ def __getattr__(self, name):
398398
399399
400400class _AggregateMeta (ShapeCastable , type ):
401- def __new__ (metacls , name , bases , namespace , * , _layout_cls = None , ** kwargs ):
402- cls = type .__new__ (metacls , name , bases , namespace , ** kwargs )
403- if _layout_cls is not None :
404- cls .__layout_cls = _layout_cls
405- if "__annotations__" in namespace :
401+ def __new__ (metacls , name , bases , namespace ):
402+ if "__annotations__" not in namespace :
403+ # This is a base class without its own layout. It is not shape-castable, and cannot
404+ # be instantiated. It can be used to share behavior.
405+ return type .__new__ (metacls , name , bases , namespace )
406+ elif all (not hasattr (base , "_AggregateMeta__layout" ) for base in bases ):
407+ # This is a leaf class with its own layout. It is shape-castable and can
408+ # be instantiated. It can also be subclassed, and used to share layout and behavior.
409+ reset = dict ()
410+ for name in namespace ["__annotations__" ]:
411+ if name in namespace :
412+ reset [name ] = namespace .pop (name )
413+ cls = type .__new__ (metacls , name , bases , namespace )
406414 cls .__layout = cls .__layout_cls (namespace ["__annotations__" ])
407- return cls
415+ cls .__reset = reset
416+ return cls
417+ else :
418+ # This is a class that has a base class with a layout and annotations. Such a class
419+ # is not well-formed.
420+ raise TypeError ("Aggregate class '{}' must either inherits or specify a layout, "
421+ "not both"
422+ .format (name ))
408423
409424 def as_shape (cls ):
425+ if not hasattr (cls , "_AggregateMeta__layout" ):
426+ raise TypeError ("Aggregate class '{}.{}' does not have a defined shape"
427+ .format (cls .__module__ , cls .__qualname__ ))
410428 return cls .__layout
411429
412430
413431class _Aggregate (View , metaclass = _AggregateMeta ):
414432 def __init__ (self , target = None , * , name = None , reset = None , reset_less = None ,
415433 attrs = None , decoder = None , src_loc_at = 0 ):
434+ if target is None and hasattr (self .__class__ , "_AggregateMeta__reset" ):
435+ if reset is None :
436+ reset = self .__class__ ._AggregateMeta__reset
437+ else :
438+ reset = {** self .__class__ ._AggregateMeta__reset , ** reset }
416439 super ().__init__ (self .__class__ , target , name = name , reset = reset , reset_less = reset_less ,
417440 attrs = attrs , decoder = decoder , src_loc_at = src_loc_at + 1 )
418441
419442
420- class Struct (_Aggregate , _layout_cls = StructLayout ):
421- pass
443+ class Struct (_Aggregate ):
444+ _AggregateMeta__layout_cls = StructLayout
422445
423446
424- class Union (_Aggregate , _layout_cls = UnionLayout ):
425- pass
447+ class Union (_Aggregate ):
448+ _AggregateMeta__layout_cls = UnionLayout
0 commit comments