Unbound name showing up in stack frame using inspect module
<p>I recently ran into a bug that was quite difficult to track down. I had accidentally re-used a class name as a variable (see code below), so when I tried to call the class I (understandably) got an error. The reason it was so hard to track down is that my debugger (Wing IDE 5.1.10) would execute the line successfully in the debug probe, but when I tried to run the same line in the interpreter it errored out. On further investigation, I found that when I examined the frame data using the inspect module, the name was still shown as a global variable bound to my class. So, I was mystified at receiving a UnboundLocalError on a name that was clearly defined and bound in my frame.</p>

<p>This reproduces the issue:</p>

<pre><code>import inspect

class MyClass(object):
    def __init__(self):
        print "MyClass init() method called successfully"

def newscope():

    #MyClass is not in the current frame's locals:
    assert 'MyClass' not in inspect.currentframe().f_locals.keys()

    #MyClass is in the current frame's globals and can be called successfully:
    class_object = inspect.currentframe().f_globals['MyClass']
    print class_object
    class_object()

    #But, calling MyClass by name results in UnboundLocalError: local
    #variable 'MyClass' referenced before assignment:
    print MyClass

    #Strangely, if at this point I go into the debug probe and run the same
    #line (print MyClass) it executes successfully, printing 
    #"&lt;class '__main__.MyClass'&gt;"

    #Re-assigning the name MyClass is what causes the UnboundLocalError:
    MyClass = 5

if __name__ == '__main__':
    newscope()
</code></pre>

<p>Results:</p>

<pre><code>&lt;class '__main__.MyClass'&gt;
MyClass init() method called successfully
Traceback (most recent call last):
  Python Shell, prompt 1, line 29
  Python Shell, prompt 1, line 19
UnboundLocalError: local variable 'MyClass' referenced before assignment
</code></pre>

<p>Again, I understand why I am getting the UnboundLocalError. What I don't understand is why the inspect module is still showing the name as being bound to the class object when clearly that isn't the case. Am I missing something, or is this a bug in the inspect module?</p>

<p>I'm running python 2.7.11.</p>

<p>First, about the exception, I think your IDE doesn't respect the python specs :</p>

<blockquote>
  <p>A scope defines the visibility of a name within a block. If a local variable is defined in a block, its scope includes that block.</p>
</blockquote>

<p>[...]</p>

<blockquote>
  <p>If a name is bound in a block, it is a local variable of that block.
  If a name is bound at the module level, it is a global variable. (The
  variables of the module code block are local and global.) If a
  variable is used in a code block but not defined there, it is a <strong>free
  variable</strong>.</p>
</blockquote>

<p>[...]</p>

<blockquote>
  <p>When a name is not found at all, a NameError exception is raised. If
  the name refers to a local variable that has not been bound, a
  UnboundLocalError exception is raised. UnboundLocalError is a subclass
  of NameError.</p>
</blockquote>

<p><a href="https://docs.python.org/2.7/reference/executionmodel.html#naming-and-binding" rel="nofollow">https://docs.python.org/2.7/reference/executionmodel.html#naming-and-binding</a></p>

<p>Thus, I understand the whole block is parsed, it finds your variable, and it is added to the local scope, but before its assignment, it's considered as a <strong>free variable</strong></p>

<p><strong>EDIT</strong></p>

<p>About <code>inspect</code>, I think it lists the bound variables in the local namespace, thus, you don't see your variable. it's pretty logical : what value would you give to the key 'MyClass' if it is not bound yet ?</p>

<p>Actually, you should use the <code>inspect.currentframe().f_code.co_varnames</code> to get what you want ;)</p>

<pre><code>import inspect
from pprint import pprint

class MyClass(object):
        def __init__(self):
                print("MyClass init() method called successfully")

def newscope():
        pprint(inspect.currentframe().f_code.co_varnames)
        print("----------")
        pprint(inspect.currentframe().f_locals)
        print("----------")
        pprint(inspect.currentframe().f_globals)
        print("----------")
        try:
                pprint(MyClass)
        except Exception as e:
                print(e)
        MyClass = 5
        pprint(inspect.currentframe().f_locals)
        print("----------")
        pprint(inspect.currentframe().f_globals)
        print("----------")

if __name__ == '__main__':
        newscope()
</code></pre>

<p>and you get : </p>

<pre><code>('MyClass', 'e')
----------
{}
----------
{'MyClass': &lt;class '__main__.MyClass'&gt;,
 '__builtins__': &lt;module 'builtins' (built-in)&gt;,
 '__cached__': None,
 '__doc__': None,
 '__file__': 'test.py',
 '__loader__': &lt;_frozen_importlib_external.SourceFileLoader object at 0x7f2fa3901160&gt;,
 '__name__': '__main__',
 '__package__': None,
 '__spec__': None,
 'inspect': &lt;module 'inspect' from '/usr/lib/python3.5/inspect.py'&gt;,
 'newscope': &lt;function newscope at 0x7f2fa39b8f28&gt;,
 'pprint': &lt;function pprint at 0x7f2fa1fe66a8&gt;}
----------
local variable 'MyClass' referenced before assignment
{'MyClass': 5}
----------
{'MyClass': &lt;class '__main__.MyClass'&gt;,
 '__builtins__': &lt;module 'builtins' (built-in)&gt;,
 '__cached__': None,
 '__doc__': None,
 '__file__': 'test.py',
 '__loader__': &lt;_frozen_importlib_external.SourceFileLoader object at 0x7f2fa3901160&gt;,
 '__name__': '__main__',
 '__package__': None,
 '__spec__': None,
 'inspect': &lt;module 'inspect' from '/usr/lib/python3.5/inspect.py'&gt;,
 'newscope': &lt;function newscope at 0x7f2fa39b8f28&gt;,
 'pprint': &lt;function pprint at 0x7f2fa1fe66a8&gt;}
----------
</code></pre>

<p>Remove your variable</p>

<pre><code>import inspect
from pprint import pprint
class MyClass(object):
        def __init__(self):
                print("MyClass init() method called successfully")

def newscope():
        pprint(inspect.currentframe().f_code.co_varnames)
        print("----------")
        pprint(inspect.currentframe().f_locals)
        print("----------")
        pprint(inspect.currentframe().f_globals)
        print("----------")
        try:
                pprint(MyClass)
        except Exception as e:
                print(e)
        # MyClass = 5
        pprint(inspect.currentframe().f_locals)
        print("----------")
        pprint(inspect.currentframe().f_globals)
        print("----------")

if __name__ == '__main__':
        newscope()
</code></pre>

<p>and you get : </p>

<pre><code>('e',)
----------
{}
----------
{'MyClass': &lt;class '__main__.MyClass'&gt;,
 '__builtins__': &lt;module 'builtins' (built-in)&gt;,
 '__cached__': None,
 '__doc__': None,
 '__file__': 'test.py',
 '__loader__': &lt;_frozen_importlib_external.SourceFileLoader object at 0x7fc6d3fcb160&gt;,
 '__name__': '__main__',
 '__package__': None,
 '__spec__': None,
 'inspect': &lt;module 'inspect' from '/usr/lib/python3.5/inspect.py'&gt;,
 'newscope': &lt;function newscope at 0x7fc6d4082f28&gt;,
 'pprint': &lt;function pprint at 0x7fc6d26b06a8&gt;}
----------
&lt;class '__main__.MyClass'&gt;
{}
----------
{'MyClass': &lt;class '__main__.MyClass'&gt;,
 '__builtins__': &lt;module 'builtins' (built-in)&gt;,
 '__cached__': None,
 '__doc__': None,
 '__file__': 'test.py',
 '__loader__': &lt;_frozen_importlib_external.SourceFileLoader object at 0x7fc6d3fcb160&gt;,
 '__name__': '__main__',
 '__package__': None,
 '__spec__': None,
 'inspect': &lt;module 'inspect' from '/usr/lib/python3.5/inspect.py'&gt;,
 'newscope': &lt;function newscope at 0x7fc6d4082f28&gt;,
 'pprint': &lt;function pprint at 0x7fc6d26b06a8&gt;}
----------
</code></pre>

python stack-trace