Search code examples
pythonpython-2.7closuresmonkeypatching

How to create new closure cell objects?


I need to monkey-patch my library to replace an instance of a symbol, and it's getting referenced by some function closures. I need to copy those functions (since I also need access to original unpatched version of the function as well), but __closure__ is immutable, and I can't copy.copy it, so how can I create new closure cells objects in Python 2.7?

I for example given this function

def f():
    def incorrectfunction():
        return 0
    def g():
        return incorrectfunction()
    return g

def correctfunction():
    return 42

func = f()
patched_func = patchit(f)   # replace "incorrectfunction"
print func(), patched_func()

And I want to see

0, 42

Solution

  • The simple way to make a closure cell would be to make a closure:

    def make_cell(val=None):
        x = val
        def closure():
            return x
        return closure.__closure__[0]
    

    If you want to reassign an existing cell's contents, you'll need to make a C API call:

    import ctypes
    PyCell_Set = ctypes.pythonapi.PyCell_Set
    
    # ctypes.pythonapi functions need to have argtypes and restype set manually
    PyCell_Set.argtypes = (ctypes.py_object, ctypes.py_object)
    
    # restype actually defaults to c_int here, but we might as well be explicit
    PyCell_Set.restype = ctypes.c_int
    
    PyCell_Set(cell, new_value)
    

    CPython only, of course.