I would like to use with
on an object that uses __getattr__
to redirect calls.
Howerver, this does not seem to work with the method __enter__
Please consider the following, simplified code to reproduce the error:
class EnterTest(object):
def myenter(self):
pass
def __exit__(self, type, value, traceback):
pass
def __getattr__(self, name):
if name == '__enter__':
return self.myenter
enter_obj = EnterTest()
print getattr(enter_obj, '__enter__')
with enter_obj:
pass
Output:
<bound method EnterTest.myenter of <__main__.EnterTest object at 0x00000000021432E8>>
Traceback (most recent call last):
File "test.py", line 14, in <module>
with enter_obj:
AttributeError: __enter__
Why doesn't it fall back to __getattr__
since __enter__
does not exist on the object?
Of course, I could make it work if I just create an __enter__
method and redirect from there instead, but I'm wondering why it doesn't work otherwise.
My python version is the following:
C:\Python27\python27.exe 2.7 (r27:82525, Jul 4 2010, 07:43:08) [MSC v.1500 64 bit (AMD64)]
According to upstream, this working was a bug in 2.6 which was "fixed" in 2.7. The short answer is that methods like __enter__
are looked up on the class, not on the object.
The documentation for this obscure behavior is at http://docs.python.org/reference/datamodel#specialnames: x[i] is roughly equivalent to ... type(x).__getitem__(x, i) for new-style classes
.
You can see this behavior with other special methods:
class foo(object):
def __iadd__(self, i):
print i
a = foo()
a += 1
class foo2(object):
def __getattr__(self, key):
print key
raise AttributeError
b = foo2()
b += 1
class foo3(object):
pass
def func(self, i):
print i
c = foo3()
c.__iadd__ = func
c += 1
The first works; the second two don't. Python 2.6 didn't conform to this behavior for __enter__
and __exit__
, but 2.7 does. http://bugs.python.org/issue9259
That said, it's painfully inconsistent that these methods can't be handled dynamically like any other attributes can. Similarly, you can't instrument accesses to these methods with __getattribute__
like you can any other method. I can't find any intrinsic design logic to this. Python is normally very consistent, and this is a fairly unpleasant wart.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With