I am trying to figure out if it is possible to allow variables in the frames of the stack (as returned inspect.currentframe()).
In other words, I'm looking for a function
def resolve_variable(variable_name, frame_object):
return value_of_that_variable_in_that_stackframe
As an example, consider the following code snippet:
global_var = 'global'
def foo():
closure_var = 'closure'
def bar(param):
local_var = 'local'
frame = inspect.currentframe()
assert resolve_variable('local_var', frame) == local_var
assert resolve_variable('param', frame) == param
assert resolve_variable('closure_var', frame) == closure_var
assert resolve_variable('global_var', frame) == global_var
bar('parameter')
foo()
Local and global variables are trivially viewed through attributes f_localsand f_globalsa frame object:
def resolve_variable(variable_name, frame_object):
try:
return frame_object.f_locals[variable_name]
except KeyError:
try:
return frame_object.f_globals[variable_name]
except KeyError:
raise NameError(varname) from None
But the problem is closure variables. As far as I know, they are not stored in the dictionary, like local and global variables. To make matters worse, variables only become trailing variables if the function actually accesses them (for example, reading its value as _ = closure_varor writing to it with nonlocal closure_var; closure_var = _). So there are actually 3 different cases:
global_var = 'global'
def foo():
unused_cvar = 'unused'
readonly_cvar = 'closure'
nonlocal_cvar = 'nonlocal'
def bar(param):
nonlocal nonlocal_cvar
local_var = 'local'
_ = readonly_cvar
nonlocal_cvar = 'nonlocal'
frame = inspect.currentframe()
assert resolve_variable('local_var', frame) == local_var
assert resolve_variable('param', frame) == param
assert resolve_variable('unused_cvar', frame) == 'unused'
assert resolve_variable('readonly_cvar', frame) == readonly_cvar
assert resolve_variable('nonlocal_cvar', frame) == nonlocal_cvar
assert resolve_variable('global_var', frame) == global_var
bar('parameter')
foo()
resolve_variable ? ?