Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting variables into the caller's scope?

Can I define a function which, when called, inserts new locals into the caller's scope? I have a feeling that passing the caller's locals() into the function might work, but is there a way to do what I want without having to do this?

like image 237
jogloran Avatar asked Mar 25 '10 12:03

jogloran


2 Answers

Check out the inspect module, it is used by minimock to mock the caller's scope.

This code ought to do what you want exactly:

import inspect
def mess_with_caller():
    stack = inspect.stack()
    try:
        locals_ = stack[1][0].f_locals
    finally:
        del stack
    locals_['my_new_function'] = lambda : 'yaaaaaay'


mess_with_caller()
print my_new_function()
>>> Output: 'yaaaaaay'
like image 192
Pykler Avatar answered Nov 10 '22 21:11

Pykler


By Python's rules, you cannot alter your caller's locals; in the current implementations, if you try (e.g. with the black magic Anurag suggests) you will not get an exception (though I'd like to add that error check to some future version), but it will be essentially inoperative if your caller is a function (not if your caller is module top-level code) -- the caller's actual local variables won't in fact be affected. This holds whether the caller's locals are explicitly passed in, or fetched through black magic: they still need to be treated as a read-only dict if your code is to have any sanity.

Rather, you could have the caller pass in an explicit, real, normal dict (which could be initialized from locals() if you want), and all alterations your code does in that dict will still be there for the caller's use -- just not as "new barenames" in the caller's local scope of course, but the functionality is the same whether the caller needs to use x['foo'] or x.foo or (as you'd prefer) just barename foo.

BTW, to use attribute-access syntax rather than dict indexing syntax, you can do:

class Bunch(object): pass

...

# caller code
b = Bunch()
thefun(b)
print b.foo

...

# called function
def thefun(b):
  b.foo = 23

This also covers, with a tiny variation, the case in which thefun wants to work with dict indexing syntax (say its body is b['foo'] = 23 instead of b.foo = 23): in that case, the caller just needs to use thefun(vars(b)) instead of the plain thefun(b), but it can keep working with the b.foo access syntax afterwards.

like image 39
Alex Martelli Avatar answered Nov 10 '22 20:11

Alex Martelli