title: Resolve a variable name given only a stack frame object
body: <p>I'm trying to find out if it's possible to resolve variables in stack frames (as returned by <a href="https://docs.python.org/3/library/inspect.html#inspect.currentframe" rel="nofollow noreferrer"><code>inspect.currentframe()</code></a>).</p>
  
  <p>In other words, I'm looking for a function</p>
  
  <pre><code>def resolve_variable(variable_name, frame_object):
      return value_of_that_variable_in_that_stackframe
  </code></pre>
  
  <hr>
  
  <p>For an example, consider the following piece of code:</p>
  
  <pre><code>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()
  </code></pre>
  
  <p>Local and global variables are trivially looked up through the <code>f_locals</code> and <code>f_globals</code> attributes of the frame object:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>But the problem are closure variables. They aren't stored in a dictionary like the local and global variables, as far as I know. To make things even worse, variables only become closure variables if the function actually accesses them (for example by reading its value like <code>_ = closure_var</code> or writing to it with <code>nonlocal closure_var; closure_var = _</code>). So there are actually 3 different cases:</p>
  
  <pre><code>global_var = 'global'
  
  def foo():
      unused_cvar = 'unused'  # actually not a closure variable at all
      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()
  </code></pre>
  
  <p>How can I rewrite my <code>resolve_variable</code> function to support all of these? Is it even possible?</p>