22from collections .abc import Mapping , Sequence
33import warnings
44import operator
5+ try :
6+ import annotationlib # py3.14+
7+ except ImportError :
8+ annotationlib = None # py3.13-
59
610from amaranth ._utils import final
711from amaranth .hdl import *
@@ -1208,22 +1212,36 @@ def __repr__(self):
12081212
12091213class _AggregateMeta (ShapeCastable , type ):
12101214 def __new__ (metacls , name , bases , namespace ):
1211- if "__annotations__" not in namespace :
1215+ if "__annotations__" not in namespace and "__annotate_func__" not in namespace :
12121216 # This is a base class without its own layout. It is not shape-castable, and cannot
12131217 # be instantiated. It can be used to share behavior.
12141218 return type .__new__ (metacls , name , bases , namespace )
12151219 elif all (not hasattr (base , "_AggregateMeta__layout" ) for base in bases ):
1220+ annotations = None
1221+ skipped_annotations = set ()
1222+ wrapped_annotate = None
1223+ if annotationlib is not None :
1224+ if annotate := annotationlib .get_annotate_from_class_namespace (namespace ):
1225+ annotations = annotationlib .call_annotate_function (
1226+ annotate , format = annotationlib .Format .VALUE )
1227+ def wrapped_annotate (format ):
1228+ annos = annotationlib .call_annotate_function (annotate , format , owner = cls )
1229+ return {k : v for k , v in annos .items () if k not in skipped_annotations }
1230+ if annotations is None :
1231+ annotations = namespace .get ("__annotations__" , {})
1232+
12161233 # This is a leaf class with its own layout. It is shape-castable and can
12171234 # be instantiated. It can also be subclassed, and used to share layout and behavior.
12181235 layout = dict ()
12191236 default = dict ()
1220- for field_name in {** namespace [ "__annotations__" ] }:
1237+ for field_name in {** annotations }:
12211238 try :
1222- Shape .cast (namespace [ "__annotations__" ] [field_name ])
1239+ Shape .cast (annotations [field_name ])
12231240 except TypeError :
12241241 # Not a shape-castable annotation; leave as-is.
12251242 continue
1226- layout [field_name ] = namespace ["__annotations__" ].pop (field_name )
1243+ skipped_annotations .add (field_name )
1244+ layout [field_name ] = annotations .pop (field_name )
12271245 if field_name in namespace :
12281246 default [field_name ] = namespace .pop (field_name )
12291247 cls = type .__new__ (metacls , name , bases , namespace )
@@ -1234,6 +1252,8 @@ def __new__(metacls, name, bases, namespace):
12341252 .format (", " .join (default .keys ())))
12351253 cls .__layout = cls .__layout_cls (layout )
12361254 cls .__default = default
1255+ if wrapped_annotate is not None :
1256+ cls .__annotate__ = wrapped_annotate
12371257 return cls
12381258 else :
12391259 # This is a class that has a base class with a layout and annotations. Such a class
0 commit comments