Comment 11 for bug 456870

Revision history for this message
Edward K. Ream (edreamleo) wrote :

I can now demonstrate exactly where the unbound recursion happens.

The following is a debugger that warns me when the stack gets too big. It is a subclass of pdb, but it overrides two methods in bdb.py. Here it is::

class debugger(pdb.Pdb):

    def __init__(self,completekey='tab',stdin=None,stdout=None):
        pdb.Pdb.__init__(self,completekey,stdin,stdout)
        self.stackLimit = 200 # Found by trial and error.

    # dispatch_call (override bdb)
    def dispatch_call(self, frame, arg):
        # XXX 'arg' is no longer used

        # EKR: new code.
        stack,junk = self.get_stack(frame,None)
        if len(stack) > self.stackLimit:
            print('(debugger.stop_here) stackCount %s' % len(stack))
            self.interaction(frame,None)

        # Existing code.
        if self.botframe is None:
            # First call of dispatch since reset()
            self.botframe = frame.f_back # (CT) Note that this may also be None!
            return self.trace_dispatch
        if not (self.stop_here(frame) or self.break_anywhere(frame)):
            # No need to trace this function
            return # None
        self.user_call(frame, arg)
        if self.quitting: raise BdbQuit
        return self.trace_dispatch

    # set_continue (override bdb)
    def set_continue(self):

        # Don't stop except at breakpoints or when finished
        self._set_stopinfo(self.botframe, None)

        # EKR: Always run with overhead
        if False:
            if not self.breaks:
                # no breakpoints; run without debugger overhead
                sys.settrace(None)
                frame = sys._getframe().f_back
                while frame and frame is not self.botframe:
                    del frame.f_trace
                    frame = frame.f_back

And here is the stack trace when the stack limit tripped. It includes some print statements I inserted while stepping through the code.

Breakpoint 1 at c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\node_classes.py:256
> c:\python26\lib\site-packages\pylint-0.19.0-py2.6.egg\pylint\lint.py(782)__init__()
-> self._rcfile = None
(Pdb) disable 1
(Pdb) c
manager.py: astng_from_file: not in cache: c:\leo.repo\until\pylintTest.py
manager.py: astng_from_file: calling file_build(pylintTest)
manager.py: astng_from_file: not in cache: c:\leo.repo\until\leo\__init__.py
manager.py: astng_from_file: calling file_build(leo)
manager.py: astng_from_file: not in cache: c:\leo.repo\until\leo\core\__init__.py
manager.py: astng_from_file: calling file_build(leo.core)
manager.py: astng_from_file: not in cache: c:\leo.repo\until\leo\core\leoGlobals.py
manager.py: astng_from_file: calling file_build(leo.core.leoGlobals)
manager.py: astng_from_file: not in cache: c:\leo.repo\until\leo\core\leoApp.py
manager.py: astng_from_file: calling file_build(leo.core.leoApp)
(debugger.stop_here) stackCount 201
> c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\inference.py(52)_set_proxied()
-> def _set_proxied(const):
(Pdb) w
('pdb.py:print_stack_trace', 201)
  c:\python26\lib\bdb.py(404)runcall()
-> res = func(*args, **kwds)
  c:\python26\lib\site-packages\pylint-0.19.0-py2.6.egg\pylint\lint.py(915)__init__()
-> linter.check(args)
  c:\python26\lib\site-packages\pylint-0.19.0-py2.6.egg\pylint\lint.py(503)check()
-> self.check_astng_module(astng, checkers)
  c:\python26\lib\site-packages\pylint-0.19.0-py2.6.egg\pylint\lint.py(583)check_astng_module()
-> self.astng_events(astng, aList,top=True)
  c:\python26\lib\site-packages\pylint-0.19.0-py2.6.egg\pylint\lint.py(618)astng_events()
-> self.astng_events(child, checkers, _reversed_checkers,top=False)
  c:\python26\lib\site-packages\pylint-0.19.0-py2.6.egg\pylint\lint.py(610)astng_events()
-> checker.visit(astng)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\utils.py(323)visit()
-> method(node)
  c:\python26\lib\site-packages\pylint-0.19.0-py2.6.egg\pylint\checkers\variables.py(408)visit_import()
-> self._check_module_attrs(node, module, parts[1:])
  c:\python26\lib\site-packages\pylint-0.19.0-py2.6.egg\pylint\checkers\variables.py(464)_check_module_attrs()
-> module = module.getattr(name)[0].infer().next()
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\scoped_nodes.py(50)wrapper()
-> nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)]
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\scoped_nodes.py(154)getattr()
-> return [self.import_module(name, relative_only=True)]
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\scoped_nodes.py(203)import_module()
-> return MANAGER.astng_from_module_name(absmodname)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\manager.py(187)astng_from_module_name()
-> return self.astng_from_file(filepath, modname, fallback=False)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\manager.py(138)astng_from_file()
-> astng = ASTNGBuilder(self).file_build(filepath, modname)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\builder.py(118)file_build()
-> node = self.string_build(data, modname, path)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\builder.py(128)string_build()
-> return self.ast_build(parse(data + '\n'), modname, path)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\builder.py(147)ast_build()
-> self.rebuilder.walk(node)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\rebuilder.py(94)walk()
-> self.delayed_visit_assattr(dnode)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\rebuilder.py(278)delayed_visit_assattr()
-> for infered in node.expr.infer():
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(203)wrapped()
-> for res in _func(node, context, **kwargs):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(168)_infer_stmts()
-> for infered in stmt.infer(context):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(203)wrapped()
-> for res in _func(node, context, **kwargs):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\inference.py(391)infer_ass()
-> stmts = list(self.assigned_stmts(context=context))
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(231)wrapper()
-> for node in func(*args, **kwargs):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\protocols.py(251)assign_assigned_stmts()
-> for infered in _resolve_asspart(self.value.infer(context), asspath, context):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\protocols.py(261)_resolve_asspart()
-> for part in parts:
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(203)wrapped()
-> for res in _func(node, context, **kwargs):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(231)wrapper()
-> for node in func(*args, **kwargs):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\inference.py(201)infer_callfunc()
-> for callee in self.func.infer(context):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(203)wrapped()
-> for res in _func(node, context, **kwargs):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(231)wrapper()
-> for node in func(*args, **kwargs):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\inference.py(259)infer_getattr()
-> for obj in owner.igetattr(self.attrname, context):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(273)igetattr()
-> self._wrap_attr(self.getattr(name, context, lookupclass=False), context),
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(248)getattr()
-> values = self._proxied.instance_attr(name, context)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\inference.py(53)_set_proxied()
-> if not hasattr(const, '__proxied'):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(44)__getattr__()
-> return getattr(self._proxied, name)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\inference.py(53)_set_proxied()
-> if not hasattr(const, '__proxied'):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(44)__getattr__()
-> return getattr(self._proxied, name)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\inference.py(53)_set_proxied()
-> if not hasattr(const, '__proxied'):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(44)__getattr__()
-> return getattr(self._proxied, name)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\inference.py(53)_set_proxied()
-> if not hasattr(const, '__proxied'):
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\infutils.py(44)__getattr__()
-> return getattr(self._proxied, name)
  c:\python26\lib\site-packages\logilab_astng-0.19.3-py2.6.egg\logilab\astng\inference.py(53)_set_proxied()

And so on...

I still don't understand the code, but now I know where the unbounded recursion is.

Edward