Skip to content

Commit 6e6dee5

Browse files
committed
lib.{data,wiring}: add Python 3.14 annotation support.
1 parent dbd1f72 commit 6e6dee5

File tree

3 files changed

+31
-6
lines changed

3 files changed

+31
-6
lines changed

amaranth/lib/data.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
from collections.abc import Mapping, Sequence
33
import warnings
44
import operator
5+
try:
6+
import annotationlib # py3.14+
7+
except ImportError:
8+
annotationlib = None # py3.13-
59

610
from amaranth._utils import final
711
from amaranth.hdl import *
@@ -1208,22 +1212,29 @@ def __repr__(self):
12081212

12091213
class _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+
if annotationlib is not None:
1222+
if annotate := annotationlib.get_annotate_from_class_namespace(namespace):
1223+
annotations = annotationlib.call_annotate_function(
1224+
annotate, format=annotationlib.Format.VALUE)
1225+
if annotations is None:
1226+
annotations = namespace.get("__annotations__", {})
12161227
# This is a leaf class with its own layout. It is shape-castable and can
12171228
# be instantiated. It can also be subclassed, and used to share layout and behavior.
12181229
layout = dict()
12191230
default = dict()
1220-
for field_name in {**namespace["__annotations__"]}:
1231+
for field_name in {**annotations}:
12211232
try:
1222-
Shape.cast(namespace["__annotations__"][field_name])
1233+
Shape.cast(annotations[field_name])
12231234
except TypeError:
12241235
# Not a shape-castable annotation; leave as-is.
12251236
continue
1226-
layout[field_name] = namespace["__annotations__"].pop(field_name)
1237+
layout[field_name] = annotations.pop(field_name)
12271238
if field_name in namespace:
12281239
default[field_name] = namespace.pop(field_name)
12291240
cls = type.__new__(metacls, name, bases, namespace)

amaranth/lib/wiring.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import enum
33
import re
44
import warnings
5+
try:
6+
import annotationlib # py3.14+
7+
except ImportError:
8+
annotationlib = None # py3.13-
59

610
from .. import tracer
711
from ..hdl._ast import Shape, ShapeCastable, Const, Signal, Value
@@ -1669,7 +1673,14 @@ def __init__(self, signature=None, *, src_loc_at=0):
16691673
cls = type(self)
16701674
members = {}
16711675
for base in reversed(cls.mro()[:cls.mro().index(Component)]):
1672-
for name, annot in base.__dict__.get("__annotations__", {}).items():
1676+
annotations = None
1677+
if annotationlib is not None:
1678+
if annotate := annotationlib.get_annotate_from_class_namespace(base.__dict__):
1679+
annotations = annotationlib.call_annotate_function(
1680+
annotate, format=annotationlib.Format.VALUE)
1681+
if annotations is None:
1682+
annotations = base.__dict__.get("__annotations__", {})
1683+
for name, annot in annotations.items():
16731684
if name.startswith("_"):
16741685
continue
16751686
if type(annot) is Member:

tests/test_lib_data.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from enum import Enum
2+
import sys
23
import operator
3-
from unittest import TestCase
4+
from unittest import TestCase, skipIf
45

56
from amaranth.hdl import *
67
from amaranth.lib import data
@@ -1312,6 +1313,8 @@ class Sb(data.Struct):
13121313
class Sd(Sb):
13131314
b: 1
13141315

1316+
@skipIf(sys.version_info >= (3, 14),
1317+
"unclear what the __annotations__ contract is going forward")
13151318
def test_typing_annotation_coexistence(self):
13161319
class S(data.Struct):
13171320
a: unsigned(1)

0 commit comments

Comments
 (0)