If I eval something in Python:
eval("a + b")
This will evaluate the expression using the current scope (both locals and globals).
What I am developing requires that an expression be evaluated "later". Such word implies that I want to keep the current scope (i.e. locals()
and globals()
). But I want to pass such values... transparently, or fetch them from an upper stack frame. Consider this implementation (actually, this one exists):
def __eval(self, expr):
if isinstance(expr, (list, tuple, set, frozenset, dict, Expression)):
return Expression.eval(expr, self)
elif callable(expr):
try:
return expr(self)
except (AttributeError, IndexError, KeyError, TypeError):
return UNDEFINED
else:
try:
return eval(expr, {}, {'self': self})
except (AttributeError, IndexError, KeyError, TypeError):
return UNDEFINED
This implementation is explained as follows:
locals()
and globals()
.I know I could explicitly call:
o._O__eval("self.a + self.b", globals(), locals())
#THIS WOULD REQUIRE to alter the method to allow two additional dict parameters
#NO, I will not call this function directly, but lets follow the example
But I'd like to get such globals()
and locals
without the user passing it explicitly and make use of such values in the eval
.
Question: Is it possible to get the locals()
and globals()
from an upper stack frame?
A stack frame represents a single function call. You can visualize functions that call one another as virtual frames stacking on top of one another. The stack data structure is actually used for this! When one function call returns its data to its caller, then its stack frame is removed from the stack.
globals() always returns the dictionary of the module namespace. locals() always returns a dictionary of the current namespace. vars() returns either a dictionary of the current namespace (if called with no argument) or the dictionary of the argument.
When a function is called in Python, a new frame is pushed onto the call stack for its local execution, and every time a function call returns, its frame is popped off the call stack. The module in which the program runs has the bottom-most frame which is called the global frame or the module frame.
In order to prevent stack overflow bugs, you must have a base case where the function stops make new recursive calls. If there is no base case then the function calls will never stop and eventually a stack overflow will occur. Here is an example of a recursive function with a base case.
You should really consider just passing in globals()
and locals()
:
def __eval(self, expr, eval_globals=None, eval_locals=None):
if eval_globals is None:
eval_globals = globals()
if eval_locals is None:
eval_locals = eval_globals()
If that is not an option for you, you can access the parent frame with the sys._getframe()
function, and the locals and globals are attributes on that frame:
def __eval(self, expr, eval_globals=None, eval_locals=None):
call_frame = sys._getframe(1)
eval_globals, eval_locals = call_frame.f_globals, call_frame.f_locals
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With