On Apr 1, 2015 3:02 PM, "Andrew Barnert" firstname.lastname@example.org wrote:
On Wednesday, April 1, 2015 12:40 PM, Ron Adam email@example.com wrote:
When exec is given a code object, it call's PyEval_EvalCodeEx in ceval.c directly with local and global dictionaries. (That should
some of your comments as to why I uses the dictionary.)
Yes, and running the block of code directly with the local and global
dictionaries is exactly what you want it to do, so why are you telling it not to do that?
def macro(): x += 1 code = fix_code(macro.__code__) def f(code_obj): x = 1 loc = locals() eval(code_obj) return loc['x']
(Or, if you prefer, use "run_code_obj" instead of "eval".)
The problem here is that if you just "return x" at the end instead of
"return loc['x']", you will likely see 1 instead of 2. It's the same problem you get if you "exec('x += 1')", exactly as described in the docs.
That happens because f was compiled to look up x by index in the
LOAD_FAST locals array, instead of by name in the locals dict, but your modified code objects mutate only the dict, not the array. That's the big problem you need to solve. Adding more layers of indirection doesn't get you any closer to fixing it.
You can propagate changes to the dict back to the array by calling the c api function PyFrame_LocalsToDict. It's pretty easy to do via ctypes, see e.g.
I guess you could append some byte code to do this to your modified function bodies.