I would like to use the decorator deco
to reshape the __enter__
and __exit__
instance methods. The code runs but the wrapper is not executed in the with
section. Find the actual output below, followed by expected output and finally the code.
Current output:
In __init__
-------------------
In __enter__
-------------------
In wrapper
Blah, blah, blah
-------------------
In __exit__
Expected output:
In __init__
-------------------
In wrapper
In __enter__
-------------------
In wrapper
Blah, blah, blah
-------------------
In wrapper
In __exit__
class contMgr():
def __init__(self):
print("In __init__")
pass
def __enter__(self):
print("In __enter__")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("In __exit__")
pass
def __call__(self, *args, **kwargs):
print("In __call_")
def brol():
print("brol")
def deco(func):
def wrapper(*args,**kwargs):
print("In wrapper")
result = func(*args, **kwargs)
return result
return wrapper
@deco
def test():
print("Blah, blah, blah")
mgr = contMgr()
mgr.__enter__ = deco(mgr.__enter__)
mgr.__exit__ = deco(mgr.__exit__)
print("------------------------")
with mgr:
print("------------------------")
test()
print("------------------------")
As others have pointed out, the problem is that it's the __enter__
and __exit__
methods of the class that will get called, not those of the instance.
But you said this is for testing purposes, and you don't want the decorators to be there permanently, so the solution is to alter the class methods temporarily.
You can do it manually, of course: change the function on the class, run the test, and change it back, but unittest.mock.patch
can automate that for you.
Here's how you can use patch
as a context manager which will restore the original function once you leave the context manager's scope:
with (patch("__main__.contMgr.__enter__", deco(contMgr.__enter__)),
patch("__main__.contMgr.__exit__", deco(contMgr.__exit__))):
with mgr:
test()
will call the decorated enter and exit only inside this with statement.
Here's a longer example with output:
print("Using patch")
with (patch("__main__.contMgr.__enter__", deco(contMgr.__enter__)),
patch("__main__.contMgr.__exit__", deco(contMgr.__exit__))):
with mgr:
test()
print("\nNot using patch")
with mgr:
test()
Output:
Using patch
In wrapper
In __enter__
In wrapper
Blah, blah, blah
In wrapper
In __exit__
Not using patch
In __enter__
In wrapper
Blah, blah, blah
In __exit__