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.)