1

My understanding was, that in order for this snippet to work:

with some_obj:
    # do something

the some_obj object must have __enter__ and __exit__ methods. And this is more or less the only requirement.

The following code demonstrates, that this is not correct:

class X:
    pass

def my_enter(*args, **kwargs):
    print("enter ", args, kwargs)

def my_exit(*args, **kwargs):
    print("exit ", args, kwargs)

some_obj = X()

some_obj.__enter__ = my_enter
some_obj.__exit__ = my_exit

with some_obj:
    print("inside with")

Even though the some_obj obviously have attribute __enter__ the above code fails:

Traceback (most recent call last):
  File "tst_with.py", line 17, in <module>
    with some_obj:
AttributeError: __enter__

Interestingly, if I replace the two lines where I add methods to object with the following:

X.__enter__ = my_enter
X.__exit__ = my_exit

everything is ok.

Is there any reason why object having these magic methods is not enough for with statement to work?

(I encountered this problem when crafting a mock object, and the original code was more sane than the code above.)

lesnik
  • 2,507
  • 2
  • 25
  • 24
  • 1
    I don't think the duplicate answers your question. See https://docs.python.org/3/reference/datamodel.html#special-method-lookup; it's definitely more helpful :) – iz_ Jan 14 '20 at 19:19
  • 1
    Because *dunder magic methods need to be on the class* due to optimizations always skipping instance look-up. I know there's gotta be a dupe around here somewhere – juanpa.arrivillaga Jan 14 '20 at 19:21
  • @juanpa.arrivillaga, iz_ thank you very much! I did not know about this optimization. – lesnik Jan 14 '20 at 19:44

0 Answers0