parent
							
								
									6ad0d21cc9
								
							
						
					
					
						commit
						8e6ae9e6e0
					
				|  | @ -806,43 +806,41 @@ def connect(m, *args, **kwargs): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Component(Elaboratable): | class Component(Elaboratable): | ||||||
|     def __init__(self): |     def __init__(self, signature=None): | ||||||
|         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): |  | ||||||
|         cls = type(self) |         cls = type(self) | ||||||
|         members = {} |         members = {} | ||||||
|         for base in reversed(cls.mro()[:cls.mro().index(Component)]): |         for base in reversed(cls.mro()[:cls.mro().index(Component)]): | ||||||
|             for name, annot in base.__dict__.get("__annotations__", {}).items(): |             for name, annot in base.__dict__.get("__annotations__", {}).items(): | ||||||
|                 if name.startswith("_"): |                 if name.startswith("_"): | ||||||
|                     continue |                     continue | ||||||
|                 if (annot is Value or annot is Signal or annot is Const or |                 if type(annot) is Member: | ||||||
|                         (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 name in members: |                     if name in members: | ||||||
|                         raise SignatureError(f"Member '{name}' is redefined in {base.__module__}.{base.__qualname__}") |                         raise SignatureError(f"Member '{name}' is redefined in {base.__module__}.{base.__qualname__}") | ||||||
|                     members[name] = annot |                     members[name] = annot | ||||||
|         if not members: |         if not members: | ||||||
|             raise NotImplementedError( |             if signature is None: | ||||||
|                 f"Component '{cls.__module__}.{cls.__qualname__}' does not have signature member " |                 raise NotImplementedError( | ||||||
|                 f"annotations") |                     f"Component '{cls.__module__}.{cls.__qualname__}' does not have signature " | ||||||
|         return Signature(members) |                     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 34: https://amaranth-lang.org/rfcs/0034-interface-rename.html | ||||||
| .. _RFC 35: https://amaranth-lang.org/rfcs/0035-shapelike-valuelike.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 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 | * `RFC 1`_: Aggregate data structure library | ||||||
|  | @ -85,6 +86,7 @@ Implemented RFCs | ||||||
| * `RFC 34`_: Rename ``amaranth.lib.wiring.Interface`` to ``PureInterface`` | * `RFC 34`_: Rename ``amaranth.lib.wiring.Interface`` to ``PureInterface`` | ||||||
| * `RFC 35`_: Add ``ShapeLike``, ``ValueLike`` | * `RFC 35`_: Add ``ShapeLike``, ``ValueLike`` | ||||||
| * `RFC 37`_: Make ``Signature`` immutable | * `RFC 37`_: Make ``Signature`` immutable | ||||||
|  | * `RFC 38`_: ``Component.signature`` immutability | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Language changes | Language changes | ||||||
|  |  | ||||||
|  | @ -711,17 +711,15 @@ class FlippedInterfaceTestCase(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|     def test_propagate_flipped(self): |     def test_propagate_flipped(self): | ||||||
|         class InterfaceWithFlippedSub(Component): |         class InterfaceWithFlippedSub(Component): | ||||||
|             signature = Signature({ |             a: In(Signature({ | ||||||
|                 "a": In(Signature({ |                 "b": Out(Signature({ | ||||||
|                     "b": Out(Signature({ |                     "c": Out(1) | ||||||
|                         "c": Out(1) |                 })), | ||||||
|                     })), |                 "d": In(Signature({ | ||||||
|                     "d": In(Signature({ |                     "e": Out(1) | ||||||
|                         "e": Out(1) |                 })), | ||||||
|                     })), |                 "f": Out(1) | ||||||
|                     "f": Out(1) |             })) | ||||||
|                 })) |  | ||||||
|             }) |  | ||||||
| 
 | 
 | ||||||
|             def __init__(self): |             def __init__(self): | ||||||
|                 super().__init__() |                 super().__init__() | ||||||
|  | @ -984,53 +982,6 @@ class ComponentTestCase(unittest.TestCase): | ||||||
|                 r"with the same name already exists$"): |                 r"with the same name already exists$"): | ||||||
|             C() |             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): |     def test_inherit(self): | ||||||
|         class A(Component): |         class A(Component): | ||||||
|             clk: In(1) |             clk: In(1) | ||||||
|  | @ -1054,3 +1005,49 @@ class ComponentTestCase(unittest.TestCase): | ||||||
|         with self.assertRaisesRegex(SignatureError, |         with self.assertRaisesRegex(SignatureError, | ||||||
|                 r"^Member 'a' is redefined in .*<locals>.B$"): |                 r"^Member 'a' is redefined in .*<locals>.B$"): | ||||||
|             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
	
	 Wanda
						Wanda