views:

467

answers:

2

Please excuse the vague title. If anyone has a suggestion, please let me know! Also please retag with more appropriate tags!

The Problem

I want to have an instance of an imported class be able to view things in the scope (globals, locals) of the importer. Since I'm not sure of the exact mechanism at work here, I can describe it much better with snippets than words.

## File 1
def f1():  print "go f1!"

class C1(object):
    def do_eval(self,x):  # maybe this should be do_evil, given what happens
        print "evaling"
        eval(x)
        eval(x,globals(),locals())

Then run this code from an iteractive session, there there will be lots of NameErrors

## interactive
class C2(object):
    def do_eval(self,x):  # maybe this should be do_evil, given what happens
        print "evaling"
        eval(x)
        eval(x,globals(),locals())

def f2():
    print "go f2!"

from file1 import C1
import file1

C1().do_eval('file1.f1()')
C1().do_eval('f1()')
C1().do_eval('f2()')

file1.C1().do_eval('file1.f1()')
file1.C1().do_eval('f1()')
file1.C1().do_eval('f2()')

C2().do_eval('f2()')
C2().do_eval('file1.f1()')
C2().do_eval('f1()')

Is there a common idiom / pattern for this sort of task? Am I barking up the wrong tree entirely?

+1  A: 

Functions are always executed in the scope they are defined in, as are methods and class bodies. They are never executed in another scope. Because importing is just another assignment statement, and everything in Python is a reference, the functions, classes and modules don't even know where they are imported to.

You can do two things: explicitly pass the 'environment' you want them to use, or use stack hackery to access their caller's namespace. The former is vastly preferred over the latter, as it's not as implementation-dependent and fragile as the latter.

You may wish to look at the string.Template class, which tries to do something similar.

Thomas Wouters
+1  A: 

In this example, you can simply hand over functions as objects to the methods in C1:

>>> class C1(object):
>>>    def eval(self, x):
>>>        x()
>>>
>>> def f2(): print "go f2"
>>> c = C1()
>>> c.eval(f2)
go f2

In Python, you can pass functions and classes to other methods and invoke/create them there.

If you want to actually evaluate a code string, you have to specify the environment, as already mentioned by Thomas.

Your module from above, slightly changed:

## File 1
def f1():  print "go f1!"

class C1(object):
    def do_eval(self, x, e_globals = globals(), e_locals = locals()):
        eval(x, e_globals, e_locals)

Now, in the interactive interpreter:

>>> def f2():
>>>    print "go f2!"
>>> from file1 import *    # 1
>>> C1().do_eval("f2()")   # 2
NameError: name 'f2' is not defined

>>> C1().do_eval("f2()", globals(), locals()) #3
go f2!
>>> C1().do_eval("f1()", globals(), locals()) #4
go f1!

Some annotations

  1. Here, we insert all objects from file1 into this module's namespace
  2. f2 is not in the namespace of file1, therefore we get a NameError
  3. Now we pass the environment explictly, and the code can be evaluated
  4. f1 is in the namespace of this module, because we imported it

Edit: Added code sample on how to explicitly pass environment for eval.

Torsten Marek
thanks for adding this, and you interpreting to comment well enough. Mostly, I like to see alternative approaches as well.
Gregg Lind
I hope that makes it a bit clearer! Sorry for the confusion on my part.
Torsten Marek