parent
6ad0d21cc9
commit
8e6ae9e6e0
|
@ -806,43 +806,41 @@ def connect(m, *args, **kwargs):
|
|||
|
||||
|
||||
class Component(Elaboratable):
|
||||
def __init__(self):
|
||||
for name in self.signature.members:
|
||||
if hasattr(self, name):
|
||||
raise NameError(f"Cannot initialize attribute for signature member {name!r} "
|
||||
f"because an attribute with the same name already exists")
|
||||
self.__dict__.update(self.signature.members.create(path=()))
|
||||
|
||||
@property
|
||||
def signature(self):
|
||||
def __init__(self, signature=None):
|
||||
cls = type(self)
|
||||
members = {}
|
||||
for base in reversed(cls.mro()[:cls.mro().index(Component)]):
|
||||
for name, annot in base.__dict__.get("__annotations__", {}).items():
|
||||
if name.startswith("_"):
|
||||
continue
|
||||
if (annot is Value or annot is Signal or annot is Const or
|
||||
(isinstance(annot, type) and issubclass(annot, ValueCastable)) or
|
||||
isinstance(annot, Signature)):
|
||||
if isinstance(annot, type):
|
||||
annot_repr = annot.__name__
|
||||
else:
|
||||
annot_repr = repr(annot)
|
||||
# To suppress this warning in the rare cases where it is necessary (and naming
|
||||
# the field with a leading underscore is infeasible), override the property.
|
||||
warnings.warn(
|
||||
message=f"Component '{cls.__module__}.{cls.__qualname__}' has "
|
||||
f"an annotation '{name}: {annot_repr}', which is not "
|
||||
f"a signature member; did you mean '{name}: In({annot_repr})' "
|
||||
f"or '{name}: Out({annot_repr})'?",
|
||||
category=SyntaxWarning,
|
||||
stacklevel=2)
|
||||
elif type(annot) is Member:
|
||||
if type(annot) is Member:
|
||||
if name in members:
|
||||
raise SignatureError(f"Member '{name}' is redefined in {base.__module__}.{base.__qualname__}")
|
||||
members[name] = annot
|
||||
if not members:
|
||||
raise NotImplementedError(
|
||||
f"Component '{cls.__module__}.{cls.__qualname__}' does not have signature member "
|
||||
f"annotations")
|
||||
return Signature(members)
|
||||
if signature is None:
|
||||
raise NotImplementedError(
|
||||
f"Component '{cls.__module__}.{cls.__qualname__}' does not have signature "
|
||||
f"member annotations")
|
||||
if isinstance(signature, dict):
|
||||
signature = Signature(signature)
|
||||
elif not isinstance(signature, Signature):
|
||||
raise TypeError(f"Object {signature!r} is not a signature nor a dict")
|
||||
else:
|
||||
if signature is not None:
|
||||
raise TypeError(
|
||||
f"Signature was passed as an argument, but component "
|
||||
f"'{cls.__module__}.{cls.__qualname__}' already has signature "
|
||||
f"member annotations")
|
||||
signature = Signature(members)
|
||||
|
||||
self.__signature = signature
|
||||
for name in signature.members:
|
||||
if hasattr(self, name):
|
||||
raise NameError(f"Cannot initialize attribute for signature member {name!r} "
|
||||
f"because an attribute with the same name already exists")
|
||||
self.__dict__.update(signature.members.create(path=()))
|
||||
|
||||
@property
|
||||
def signature(self):
|
||||
return self.__signature
|
||||
|
|
|
@ -64,6 +64,7 @@ Implemented RFCs
|
|||
.. _RFC 34: https://amaranth-lang.org/rfcs/0034-interface-rename.html
|
||||
.. _RFC 35: https://amaranth-lang.org/rfcs/0035-shapelike-valuelike.html
|
||||
.. _RFC 37: https://amaranth-lang.org/rfcs/0037-make-signature-immutable.html
|
||||
.. _RFC 38: https://amaranth-lang.org/rfcs/0038-component-signature-immutability.html
|
||||
|
||||
|
||||
* `RFC 1`_: Aggregate data structure library
|
||||
|
@ -85,6 +86,7 @@ Implemented RFCs
|
|||
* `RFC 34`_: Rename ``amaranth.lib.wiring.Interface`` to ``PureInterface``
|
||||
* `RFC 35`_: Add ``ShapeLike``, ``ValueLike``
|
||||
* `RFC 37`_: Make ``Signature`` immutable
|
||||
* `RFC 38`_: ``Component.signature`` immutability
|
||||
|
||||
|
||||
Language changes
|
||||
|
|
|
@ -711,17 +711,15 @@ class FlippedInterfaceTestCase(unittest.TestCase):
|
|||
|
||||
def test_propagate_flipped(self):
|
||||
class InterfaceWithFlippedSub(Component):
|
||||
signature = Signature({
|
||||
"a": In(Signature({
|
||||
"b": Out(Signature({
|
||||
"c": Out(1)
|
||||
})),
|
||||
"d": In(Signature({
|
||||
"e": Out(1)
|
||||
})),
|
||||
"f": Out(1)
|
||||
}))
|
||||
})
|
||||
a: In(Signature({
|
||||
"b": Out(Signature({
|
||||
"c": Out(1)
|
||||
})),
|
||||
"d": In(Signature({
|
||||
"e": Out(1)
|
||||
})),
|
||||
"f": Out(1)
|
||||
}))
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
@ -984,53 +982,6 @@ class ComponentTestCase(unittest.TestCase):
|
|||
r"with the same name already exists$"):
|
||||
C()
|
||||
|
||||
def test_missing_in_out_warning(self):
|
||||
class C1(Component):
|
||||
prt1 : In(1)
|
||||
sig2 : Signal
|
||||
|
||||
with self.assertWarnsRegex(SyntaxWarning,
|
||||
r"^Component '.+\.C1' has an annotation 'sig2: Signal', which is not a signature "
|
||||
r"member; did you mean 'sig2: In\(Signal\)' or 'sig2: Out\(Signal\)'\?$"):
|
||||
C1().signature
|
||||
|
||||
class C2(Component):
|
||||
prt1 : In(1)
|
||||
sig2 : Signature({})
|
||||
|
||||
with self.assertWarnsRegex(SyntaxWarning,
|
||||
r"^Component '.+\.C2' has an annotation 'sig2: Signature\({}\)', which is not "
|
||||
r"a signature member; did you mean 'sig2: In\(Signature\({}\)\)' or "
|
||||
r"'sig2: Out\(Signature\({}\)\)'\?$"):
|
||||
C2().signature
|
||||
|
||||
class MockValueCastable(ValueCastable):
|
||||
def shape(self): pass
|
||||
@ValueCastable.lowermethod
|
||||
def as_value(self): pass
|
||||
|
||||
class C3(Component):
|
||||
prt1 : In(1)
|
||||
val2 : MockValueCastable
|
||||
|
||||
with self.assertWarnsRegex(SyntaxWarning,
|
||||
r"^Component '.+\.C3' has an annotation 'val2: MockValueCastable', which is not "
|
||||
r"a signature member; did you mean 'val2: In\(MockValueCastable\)' or "
|
||||
r"'val2: Out\(MockValueCastable\)'\?$"):
|
||||
C3().signature
|
||||
|
||||
def test_bug_882(self):
|
||||
class PageBuffer(Component):
|
||||
rand: Signature({}).flip()
|
||||
other: Out(1)
|
||||
|
||||
with self.assertWarnsRegex(SyntaxWarning,
|
||||
r"^Component '.+\.PageBuffer' has an annotation 'rand: Signature\({}\)\.flip\(\)', "
|
||||
r"which is not a signature member; did you mean "
|
||||
r"'rand: In\(Signature\({}\)\.flip\(\)\)' or "
|
||||
r"'rand: Out\(Signature\({}\)\.flip\(\)\)'\?$"):
|
||||
PageBuffer()
|
||||
|
||||
def test_inherit(self):
|
||||
class A(Component):
|
||||
clk: In(1)
|
||||
|
@ -1054,3 +1005,49 @@ class ComponentTestCase(unittest.TestCase):
|
|||
with self.assertRaisesRegex(SignatureError,
|
||||
r"^Member 'a' is redefined in .*<locals>.B$"):
|
||||
B()
|
||||
|
||||
def test_create(self):
|
||||
class C(Component):
|
||||
def __init__(self, width):
|
||||
super().__init__(Signature({
|
||||
"a": In(width)
|
||||
}))
|
||||
|
||||
c = C(2)
|
||||
self.assertEqual(c.signature, Signature({"a": In(2)}))
|
||||
self.assertIsInstance(c.a, Signal)
|
||||
self.assertEqual(c.a.shape(), unsigned(2))
|
||||
|
||||
def test_create_dict(self):
|
||||
class C(Component):
|
||||
def __init__(self, width):
|
||||
super().__init__({
|
||||
"a": In(width)
|
||||
})
|
||||
|
||||
c = C(2)
|
||||
self.assertEqual(c.signature, Signature({"a": In(2)}))
|
||||
self.assertIsInstance(c.a, Signal)
|
||||
self.assertEqual(c.a.shape(), unsigned(2))
|
||||
|
||||
def test_create_wrong(self):
|
||||
class C(Component):
|
||||
a: In(2)
|
||||
|
||||
def __init__(self, width):
|
||||
super().__init__(Signature({
|
||||
"a": In(width)
|
||||
}))
|
||||
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Signature was passed as an argument, but component '.*.C' already has signature member annotations$"):
|
||||
C(2)
|
||||
|
||||
def test_create_wrong_type(self):
|
||||
class C(Component):
|
||||
def __init__(self, width):
|
||||
super().__init__(4)
|
||||
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Object 4 is not a signature nor a dict$"):
|
||||
C(2)
|
||||
|
|
Loading…
Reference in a new issue