I was arguing with some Python code that used closures, and found it is very hard to manipulate them in inappropriate ways. Not being one to let both good programming practices and simple logic to get in my way, I hacked this together. Using this in production code may incur the wrath of all programmers within a 500 mile radius.
def top(x):
blarg = [2]
def mid(q):
data = {1:x}
asd = -1
return lambda n: data[n+asd+q[0]]
return mid(blarg)
f = top(2)
# ------
def get_closed_ns(f):
import itertools
def get_val(n):
try:
return f.func_closure[n].cell_contents
except AttributeError:
import types
import dis
old_code = f.func_code
code_string = (dis.opmap['LOAD_DEREF'], n%256, n//256, dis.opmap['RETURN_VALUE'])
new_code = types.CodeType(
0, #old_code.co_argcount,
old_code.co_nlocals,
old_code.co_stacksize,
old_code.co_flags,
''.join([chr(n) for n in code_string]), #old_code.co_code,
old_code.co_consts,
old_code.co_names,
old_code.co_varnames,
old_code.co_filename,
old_code.co_name,
old_code.co_firstlineno,
old_code.co_lnotab,
old_code.co_freevars,
old_code.co_cellvars,
)
new_f = types.FunctionType(
new_code,
f.func_globals,
f.func_name,
f.func_defaults,
f.func_closure,
)
return new_f()
names = {}
for i, name in enumerate(itertools.chain(f.func_code.co_cellvars, f.func_code.co_freevars)):
names[name] = get_val(i)
return names
print f(0)
ns = get_closed_ns(f)
ns['data'][1] = 'BOOM'
ns['q'][0] = 0
The function returns the entire closed namespace, though the usual reference mutation semantics apply (i.e. the closed value "asd" is not mutable). Enjoy!