Get a dict of all variables currently in scope and their values
Solution 1:
Best way to merge two dicts as you're doing (with locals overriding globals) is dict(globals(), **locals())
.
What the approach of merging globals and locals is missing is (a) builtins (I imagine that's deliberate, i.e. you don't think of builtins as "variables"... but, they COULD be, if you so choose!-), and (b) if you're in a nested function, any variables that are local to enclosing functions (no really good way to get a dict with all of those, plus -- only those explicitly accessed in the nested function, i.e. "free variables" thereof, survive as cells in a closure, anyway).
I imagine these issues are no big deal for your intended use, but you did mention "corner cases";-). If you need to cover them, there are ways to get the built-ins (that's easy) and (not so easy) all the cells (variables from enclosing functions that you explicitly mention in the nested function -- thefunction.func_code.co_freevars
to get the names, thefunction.func_closure
to get the cells, cell_contents
on each cell to get its value). (But, remember, those will only be variables from enclosing functions that are explicitly accessed in your nested function's code!).
Solution 2:
Does this do what you intended?
d = dict(globals())
d.update(locals())
If I read the documentation correctly, you create a copy of the globals()
dict, then you overwrite any duplicates and insert new entries from the locals()
dict (since the locals()
should have preference within your scope, anyway).
I haven't had any luck in getting a proper function to return the full dictionary of variables in scope of the calling function. Here's the code (I only used pprint to format the output nicely for SO):
from pprint import *
def allvars_bad():
fake_temp_var = 1
d = dict(globals())
d.update(locals())
return d
def foo_bad():
x = 5
return allvars_bad()
def foo_good():
x = 5
fake_temp_var = "good"
d = dict(globals())
d.update(locals())
return d
pprint (foo_bad(), width=50)
pprint (foo_good(), width=50)
and the output:
{'PrettyPrinter': <class pprint.PrettyPrinter at 0xb7d316ec>,
'__builtins__': <module '__builtin__' (built-in)>,
'__doc__': None,
'__file__': 'temp.py',
'__name__': '__main__',
'__package__': None,
'allvars_bad': <function allvars_bad at 0xb7d32b1c>,
'd': <Recursion on dict with id=3084093748>,
'fake_temp_var': 1,
'foo_bad': <function foo_bad at 0xb7d329cc>,
'foo_good': <function foo_good at 0xb7d32f0c>,
'isreadable': <function isreadable at 0xb7d32c34>,
'isrecursive': <function isrecursive at 0xb7d32c6c>,
'pformat': <function pformat at 0xb7d32bc4>,
'pprint': <function pprint at 0xb7d32b8c>,
'saferepr': <function saferepr at 0xb7d32bfc>}
{'PrettyPrinter': <class pprint.PrettyPrinter at 0xb7d316ec>,
'__builtins__': <module '__builtin__' (built-in)>,
'__doc__': None,
'__file__': 'temp.py',
'__name__': '__main__',
'__package__': None,
'allvars_bad': <function allvars_bad at 0xb7d32b1c>,
'd': <Recursion on dict with id=3084093884>,
'fake_temp_var': 'good',
'foo_bad': <function foo_bad at 0xb7d329cc>,
'foo_good': <function foo_good at 0xb7d32f0c>,
'isreadable': <function isreadable at 0xb7d32c34>,
'isrecursive': <function isrecursive at 0xb7d32c6c>,
'pformat': <function pformat at 0xb7d32bc4>,
'pprint': <function pprint at 0xb7d32b8c>,
'saferepr': <function saferepr at 0xb7d32bfc>,
'x': 5}
Note that in the second output, we have overwritten fake_temp_var
, and x is present; the first output only included the local vars within the scope of allvars_bad
.
So if you want to access the full variable scope, you cannot put locals() inside another function.
I had suspected there was some sort of frame object, I just didn't (know where to) look for it.
This works to your spec, I believe:
def allvars_good(offset=0):
frame = sys._getframe(1+offset)
d = frame.f_globals
d.update(frame.f_locals)
return d
def foo_good2():
a = 1
b = 2
return allvars_good()
-->
{'PrettyPrinter': <class pprint.PrettyPrinter at 0xb7d6474c>,
'__builtins__': <module '__builtin__' (built-in)>,
'__doc__': None,
'__file__': 'temp.py',
'__name__': '__main__',
'__package__': None,
'a': 1,
'allvars_bad': <function allvars_bad at 0xb7d65b54>,
'allvars_good': <function allvars_good at 0xb7d65a04>,
'b': 2,
'foo_bad': <function foo_bad at 0xb7d65f44>,
'foo_good': <function foo_good at 0xb7d65f7c>,
'foo_good2': <function foo_good2 at 0xb7d65fb4>,
'isreadable': <function isreadable at 0xb7d65c6c>,
'isrecursive': <function isrecursive at 0xb7d65ca4>,
'pformat': <function pformat at 0xb7d65bfc>,
'pprint': <function pprint at 0xb7d65bc4>,
'saferepr': <function saferepr at 0xb7d65c34>,
'sys': <module 'sys' (built-in)>}
Solution 3:
You could make your own:
allvars = dict()
allvars.update(globals())
allvars.update(locals())
or combine the first two lines:
allvars = dict(globals())
allvars.update(locals())