views:

41

answers:

2

I've written a remote Python debugger and one of the features I need is to execute arbitrary code while stopped at a breakpoint. My debugger uses the following to execute code received from the remote debugger:

exec (compile(code, '<string>', 'single') , frame.f_globals, frame.f_locals)

This works fine for the most part, but I've noticed a couple issues.

  1. Assignment statements aren't actually applied to the original locals dictionary. This is probably due to the fact that f_locals is supposed to be read-only.

  2. If stopped within a class method, accessing protected attributes (names beginning with double underscore) does not work. I'm assuming this is due to the name mangling that Python performs on protected attributes.

So my question is, is there a way around these limitations? Can I trick Python into thinking that the code is being executed in the actual local scope of that frame?

I'm using CPython 2.7, and I'm willing to accept a solution/hack specific to this version.

A: 

I'm not sure I've understood you correctly, but exec does populate the locals parameter with assignments inside the code:

>>> loc = {}
>>> exec(compile('a=3', '<string>', 'single'), {}, loc)
>>> loc
{'a': 3}

Perhaps f_locals doesn't allow writes.

Marcelo Cantos
In my experience, f_locals is writable, but it seems to be a copy of the original locals() dictionary. So any changes made to it, won't affect the original scope.
flashk
+2  A: 

Assignment statements aren't actually applied to the original locals dictionary. This is probably due to the fact that f_locals is supposed to be read-only.

Not exactly, but the bytecode for the function will not look at locals, using rather a simple but crucial optimization whereby local variables are in a simple array, avoiding runtime lookups. The only way to avoid this (and make the function much, much slower) is compiling different code, e.g. code starting with an exec '' to force the compiler to avoid the optimization (in Python 2; no way, in Python 3). If you need to work with existing bytecode, you're out of luck: there is no way to accomplish what you desire.

If stopped within a class method, accessing protected attributes (names beginning with double underscore) does not work. I'm assuming this is due to the name mangling that Python performs on protected attributes.

Yep, so this issue does allow a workaround: prepend _Classname to the name to mimic what the compiler does. Note that double-underscore prefixes means private: protected would be a single underscore (and would give you no trouble). Private names are specifically meant to avoid accidental classes with names bound in subclasses (and work decently for that one purpose, though not perfectly, and not for anything else;-).

Alex Martelli
Not the answer I was hoping for, but thanks for the clarification Alex.
flashk