def lowermethod(func):
"""Decorator to memoize lowering methods.
- Ensures the decorated method is called only once, with subsequent method calls returning the
- object returned by the first first method call.
+ Ensures the decorated method is called only once, with subsequent method calls returning
+ the object returned by the first first method call.
- This decorator is required to decorate the ``as_value`` method of ``ValueCastable`` subclasses.
- This is to ensure that nMigen's view of representation of all values stays internally
- consistent.
+ This decorator is required to decorate the ``as_value`` method of ``ValueCastable``
+ subclasses. This is to ensure that nMigen's view of representation of all values stays
+ internally consistent.
"""
@functools.wraps(func)
def wrapper_memoized(self, *args, **kwargs):
- if not hasattr(self, "_ValueCastable__lowered_to"):
+ # Use `in self.__dict__` instead of `hasattr` to avoid interfering with custom
+ # `__getattr__` implementations.
+ if not "_ValueCastable__lowered_to" in self.__dict__:
self.__lowered_to = func(self, *args, **kwargs)
return self.__lowered_to
wrapper_memoized.__memoized = True
pass
+class MockValueCastableCustomGetattr(ValueCastable):
+ def __init__(self):
+ pass
+
+ @ValueCastable.lowermethod
+ def as_value(self):
+ return Const(0)
+
+ def __getattr__(self, attr):
+ assert False
+
+
class ValueCastableTestCase(FHDLTestCase):
def test_not_decorated(self):
with self.assertRaisesRegex(TypeError,
sig3 = Value.cast(vc)
self.assertIs(sig1, sig3)
+ def test_custom_getattr(self):
+ vc = MockValueCastableCustomGetattr()
+ vc.as_value() # shouldn't call __getattr__
+
class SampleTestCase(FHDLTestCase):
def test_const(self):