views:

101

answers:

4

I want to have a function in a different module, that when called, has access to all variables that its caller has access to, and functions just as if its body had been pasted into the caller rather than having its own context, basically like a C Macro instead of a normal function. I know I can pass locals() into the function and then it can access the local variables as a dict, but I want to be able to access them normally (eg x.y, not x["y"] and I want all names the caller has access to not just the locals, as well as things that were 'imported' into the caller's file but not into the module that contains the function.

Is this possible to pull off?

Edit 2 Here's the simplest possible example I can come up with of what I'm really trying to do:

def getObj(expression)
  ofs = expression.rfind(".")
  obj = eval(expression[:ofs])  
  print "The part of the expression Left of the period is of type ", type(obj), 

Problem is that 'expression' requires the imports and local variables of the caller in order to eval without error.In reality theres a lot more than just an eval, so I'm trying to avoid the solution of just passing locals() in and through to the eval() since that won't fix my general case problem.

+2  A: 

Is this possible to pull off?

Yes (sort of, in a very roundabout way) which I would strongly advise against it in general (more on that later).

Consider:

myfile.py

def func_in_caller():
    print "in caller"

import otherfile
globals()["imported_func"] = otherfile.remote_func

imported_func(123, globals())  

otherfile.py

def remote_func(x1, extra):
    for k,v in extra.iteritems(): 
        globals()[k] = v
    print x1
    func_in_caller()

This yields (as expected):

123
in caller

What we're doing here is trickery: we just copy every item into another namespace in order to make this work. This can (and will) break very easily and/or lead to hard to find bugs.

There's almost certainly a better way of solving your problem / structuring your code (we need more information in general on what you're trying to achieve).

ChristopheD
Or even in `remote_func`, just `locals().update(extra)` . A little less messed up.
gilesc
Thanks, I'll edit my question with the larger context of what I'm trying to do
bdk
+1  A: 

I don't presume this is the answer that you wanted to hear, but trying to access local variables from a caller module's scope is not a good idea. If you normally program in PHP or C, you might be used to this sort of thing?

If you still want to do this, you might consider creating a class and passing an instance of that class in place of locals():

#other_module.py
def some_func(lcls):
    print(lcls.x)

Then,

>>> import other_module
>>> 
>>> 
>>> x = 'Hello World'
>>> 
>>> class MyLocals(object):
...     def __init__(self, lcls):
...             self.lcls = lcls
...     def __getattr__(self, name):
...             return self.lcls[name]
... 
>>> # Call your function with an instance of this instead.
>>> other_module.some_func(MyLocals(locals()))
'Hello World'

Give it a whirl.

orokusaki
I'm accepting this answer because I think it gives the best answer to my original question, giving the syntax I'm after, but after reading all the responses, I'm going to see if I can get the interface specification changed to avoid having to do this at all
bdk
+2  A: 

From The Zen of Python:

2) Explicit is better than implicit.

In other words, pass in the parameter and don't try to get really fancy just because you think it would be easier for you. Writing code is not just about you.

unholysampler
Not sure I follow, once I pass the actual variable, My function just gets the current value and can't perform the wsdl reflection which requires knowing the actual path strings.
bdk
+2  A: 

And another, even uglier way to do it -- please don't do this, even if it's possible --

import sys

def insp():
    l = sys._getframe(1).f_locals
    expression = l["expression"]
    ofs = expression.rfind(".")
    expofs = expression[:ofs]
    obj = eval(expofs, globals(), l)
    print "The part of the expression %r Left of the period (%r) is of type %r" % (expression, expofs, type(obj)), 

def foo():
    derp = 5
    expression = "derp.durr"
    insp()

foo()

outputs

The part of the expression 'derp.durr' Left of the period ('derp') is of type (type 'int')

AKX