Issue 20149: 'with instance' references class's enter attribute rather than instance's (original) (raw)
I was writing code for a class that uses one threading.RLock per object. For convenience, I wanted to be able to use that lock's context manager and acquire/release methods without referencing the lock directly, so I opted to take a shortcut by assigning self.enter = self.lock.enter and so forth.
This fails in Python 3.3.1 (self-compiled on Debian) and both "3.3.2+" and "2.7.5+" (currently available Ubuntu packages). Judging by the error messages, it looks 'with' is examining the enter and exit attributes defined on the instance's class, rather than those defined on the instance itself.
The workaround here is simple enough, but I'm under the impression that it shouldn't be needed anyways.
Test case follows:
import threading
class Foo(object):
Uncommenting these yields "NoneType is not callable" rather than an AttributeError
enter = None
exit = None
acquire = None
release = None
lock = None
def __init__(self):
self.lock = threading.RLock()
print(repr(self.lock))
self.__enter__ = self.lock.__enter__
self.__exit__ = self.lock.__exit__
self.acquire = self.lock.acquire
self.release = self.lock.release
foo = Foo()
These all function as expected. The fourth line fails (correctly) on 2.7.5.
print(repr(foo.enter)) print(repr(foo.exit)) print(foo.enter()) print(foo.exit())
This does not
with foo: pass
"magic methods" like enter and exit are only looked up on the class, not on the instance. This is by design.
In some older versions of Python some specific methods (and I think enter and exit were among them for a time) were looked up on the instances, but this was a bug.
(Personally I think it would be nice if they were looked up on the instances, but there are good reasons why this is not done.)