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
	
	 whitequark
						whitequark