In py.test monkeypatching/mocking documentation this is not mentioned, but it is possible to monkeypatch a local variable introduced in a function body?
my experiment:
def my_method():
my_var = 'foo'
return my_var[:2]
test is:
def test_my_method(monkeypatch):
monkeypatch.setattr(my_module.MyClass.my_method.my_var, lambda: 'bar')
assert my_method() == 'ba'
AttributeError: 'function' object at MyClass.my_method has no attribute 'my_var'
This is not possible as the variable does not exist ahead of time and py.test cannot hook into the creation of a local variable as far as i know.
With a little care, it would be possible to patch the consts in the function code object using ctypes.
import ctypes
from contextlib import contextmanager
def tuple_setitem(tup, index, item):
obj = ctypes.py_object(tup)
item = ctypes.py_object(item)
ref_count = ctypes.c_long.from_address(id(tup))
original_count = ref_count.value
if original_count != 1:
ref_count.value = 1
ctypes.pythonapi.Py_IncRef(item)
ctypes.pythonapi.PyTuple_SetItem(obj, ctypes.c_ssize_t(index), item)
ref_count.value = original_count
@contextmanager
def patch_tuple_item(tup, index, item):
old = tup[index]
try:
tuple_setitem(tup, index, item)
yield
finally:
tuple_setitem(tup, index, old)
Demo:
>>> def my_method():
... my_var = "foo"
... return my_var[:2]
...
>>> consts = my_method.__code__.co_consts
>>> consts
(None, 'foo', 2)
>>> with patch_tuple_item(consts, index=1, item="bar"):
... print(my_method())
...
ba
>>> print(my_method())
fo
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