<type>documentation</type>
<domain>debug</domain>
<id>debug-console-limitations</id>
<title>Debug Console Limitations</title>
<date>$Date: 2006-12-28 17:21:05 -0500 (Thu, 28 Dec 2006) $</date>
<version>$Revision: 14455 $</version>
<product>pro+</product>
<product-version>6+</product-version>

Some code will work in unexpected ways in Wing's ``Debug Console`` due to how list
comprehensions, generator expressions, and nested functions work in Python 3. This results
in inability to evaluate some code when stopped at a breakpoint in a function or method.

Nested Function Scope
---------------------

The most comonly noticed example is inability access variables in an enclosing scope when
within a nested function. For example when the debugger is stopped on the line ``return
1`` in the following code, typing ``self`` in the ``Debug Console`` raises a ``NameError``::

  class C:
  
    def m(self):
      def nested():
        return 1
      nested()
        
  c = C()
  c.m()

This is a result of how Python's compiler binds variables from the nested scope into
nested functions. If the variable is not used in the nested function then it will not be
defined there at all.

There is no work-around for this problem, other than moving up to the enclosing stack
frame in the debugger and inspecting the variable there instead.

List Comprehensions and Generators
----------------------------------

List comprehensions and generator expressions suffer from a related problem when used in
the ``Debug Console``. For an example, try stopping on ``print(foo)`` in the following code::

  def x():
    from string import capwords
    foo = ['one two', 'three four']
    print(foo)
  
  x()
  
Now typing the following list comprehension in the ``Debug Console`` will raise a ``NameError``
indicating that ``capwords`` is not defined::

  y = [capwords(x) for x in foo]

This is because in Python 3 the list comprehension is implemented internally as a nested
function and Python's compiler plays tricks to bind the necessary variables from the
enclosing scope into the nested function. Even though capwords is defined in locals() the
compiler does not use that when creating the code object for the list comprehension.
Instead, it references the symbol table of the enclosing function which in the case of the
``Debug Console`` is (unavoidably) not x().

Generator expressions have the same problem::

  y = (capwords(x) for x in foo)
  x = list(y)
  
And so do nested functions, if defined within the ``Debug Console``::
    
  def f():
    capwords('test me')
  f()
  
A possible work-around to use in some cases is to first load the locals into globals by
typing the following in the ``Debug Console``::

  globals().update(locals())
  
However, this drastically alters program state in ways that may be destructive even if the
original contents of ``globals()`` is restored after the evaluation.

