Until it runs Python code with non-local ones, I was able to rewrite the AST so that it behaves the way I want. Each variable that is accessed and that is not locally defined in the scope of functions or classes is rewritten to access the mapping.
This is an example demonstrating a possible use case where a is a dictionary that takes precedence over b and receives all variable assignments, but the values ββof b are still taken into account if they are not obscured by a .
from nr.datastructures.chaindict import ChainDict from nr.ast.dynamic_eval import dynamic_exec d1 = {'a': 42} d2 = {'b': 'spam'} code =''' print(a, b) # prints 42 spam a = 'egg' b = 'ham' ''' dynamic_exec(code, ChainDict(d1, d2)) assert d1 == {'a': 'egg', 'b': 'ham'}, d1 assert d2 == {'b': 'spam'}, d2
Available since nr v2.0.4 (disclaimer: I am the developer of this package).
A bit more
The dynamic_exec() function will parse Python code using ast.parse() , and then apply ast.NodeTransformer , which overwrites the names of global variables. Example:
import os from os import path parent_dir = path.dirname(__file__) def main(): filename = path.join(parent_dir, 'foo.py') print(filename)
It will be turned into AST, semantically equivalent
import os; __dict__['os'] = os from os import path; __dict__['path'] = path __dict__['parent_dir'] = __dict__['path'].dirname(__dict__['__file__']) def main(): filename = __dict__['path'].join(__dict__['parent_dir'], 'foo.py') __dict__['print'](filename)
source share