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):
|
def lowermethod(func):
|
||||||
"""Decorator to memoize lowering methods.
|
"""Decorator to memoize lowering methods.
|
||||||
|
|
||||||
Ensures the decorated method is called only once, with subsequent method calls returning the
|
Ensures the decorated method is called only once, with subsequent method calls returning
|
||||||
object returned by the first first method call.
|
the object returned by the first first method call.
|
||||||
|
|
||||||
This decorator is required to decorate the ``as_value`` method of ``ValueCastable`` subclasses.
|
This decorator is required to decorate the ``as_value`` method of ``ValueCastable``
|
||||||
This is to ensure that nMigen's view of representation of all values stays internally
|
subclasses. This is to ensure that nMigen's view of representation of all values stays
|
||||||
consistent.
|
internally consistent.
|
||||||
"""
|
"""
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapper_memoized(self, *args, **kwargs):
|
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)
|
self.__lowered_to = func(self, *args, **kwargs)
|
||||||
return self.__lowered_to
|
return self.__lowered_to
|
||||||
wrapper_memoized.__memoized = True
|
wrapper_memoized.__memoized = True
|
||||||
|
|
|
@ -1060,6 +1060,18 @@ class MockValueCastableNoOverride(ValueCastable):
|
||||||
pass
|
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):
|
class ValueCastableTestCase(FHDLTestCase):
|
||||||
def test_not_decorated(self):
|
def test_not_decorated(self):
|
||||||
with self.assertRaisesRegex(TypeError,
|
with self.assertRaisesRegex(TypeError,
|
||||||
|
@ -1083,6 +1095,10 @@ class ValueCastableTestCase(FHDLTestCase):
|
||||||
sig3 = Value.cast(vc)
|
sig3 = Value.cast(vc)
|
||||||
self.assertIs(sig1, sig3)
|
self.assertIs(sig1, sig3)
|
||||||
|
|
||||||
|
def test_custom_getattr(self):
|
||||||
|
vc = MockValueCastableCustomGetattr()
|
||||||
|
vc.as_value() # shouldn't call __getattr__
|
||||||
|
|
||||||
|
|
||||||
class SampleTestCase(FHDLTestCase):
|
class SampleTestCase(FHDLTestCase):
|
||||||
def test_const(self):
|
def test_const(self):
|
||||||
|
|
Loading…
Reference in a new issue