hdl.ast: improve interaction of ValueCastable with custom __getattr__.
Avoid calling `__getattr__("_ValueCastable__lowered_to")` when a ValueCastable has custom `__getattr__` implementation; this avoids the need for downstream code to be aware of this implementataion detail.
This commit is contained in:
parent
fac1b4b2d1
commit
11914a1e67
|
@ -1305,16 +1305,18 @@ class ValueCastable:
|
|||
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
|
||||
|
|
|
@ -1060,6 +1060,18 @@ class MockValueCastableNoOverride(ValueCastable):
|
|||
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,
|
||||
|
@ -1083,6 +1095,10 @@ class ValueCastableTestCase(FHDLTestCase):
|
|||
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):
|
||||
|
|
Loading…
Reference in a new issue