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
# 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.
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.stackLimi t = 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(frame, None)
print( '(debugger. stop_here) stackCount %s' % len(stack))
self. interaction( frame,None)
stack,junk = self.get_
if len(stack) > self.stackLimit:
# Existing code.
self. botframe = frame.f_back # (CT) Note that this may also be None! here(frame) or self.break_ anywhere( frame)) :
self.user_ call(frame, arg)
if self.botframe is None:
# First call of dispatch since reset()
return self.trace_dispatch
if not (self.stop_
# No need to trace this function
return # None
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
sys.settrace( None)
frame = sys._getframe( ).f_back
while frame and frame is not self.botframe:
del frame.f_trace
frame = frame.f_back
if False:
if not self.breaks:
# no breakpoints; run without debugger overhead
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 lib\site- packages\ pylint- 0.19.0- py2.6.egg\ pylint\ lint.py( 782)__init_ _() repo\until\ pylintTest. py pylintTest) repo\until\ leo\__init_ _.py repo\until\ leo\core\ __init_ _.py leo.core) repo\until\ leo\core\ leoGlobals. py leo.core. leoGlobals) repo\until\ leo\core\ leoApp. py leo.core. leoApp) stop_here) stackCount 201 lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\inference .py(52) _set_proxied( ) const): print_stack_ trace', 201) lib\bdb. py(404) runcall( ) lib\site- packages\ pylint- 0.19.0- py2.6.egg\ pylint\ lint.py( 915)__init_ _() lib\site- packages\ pylint- 0.19.0- py2.6.egg\ pylint\ lint.py( 503)check( ) astng_module( astng, checkers) lib\site- packages\ pylint- 0.19.0- py2.6.egg\ pylint\ lint.py( 583)check_ astng_module( ) events( astng, aList,top=True) lib\site- packages\ pylint- 0.19.0- py2.6.egg\ pylint\ lint.py( 618)astng_ events( ) events( child, checkers, _reversed_ checkers, top=False) lib\site- packages\ pylint- 0.19.0- py2.6.egg\ pylint\ lint.py( 610)astng_ events( ) visit(astng) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\utils. py(323) visit() lib\site- packages\ pylint- 0.19.0- py2.6.egg\ pylint\ checkers\ variables. py(408) visit_import( ) module_ attrs(node, module, parts[1:]) lib\site- packages\ pylint- 0.19.0- py2.6.egg\ pylint\ checkers\ variables. py(464) _check_ module_ attrs() getattr( name)[0] .infer( ).next( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\scoped_ nodes.py( 50)wrapper( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\scoped_ nodes.py( 154)getattr( ) module( name, relative_ only=True) ] lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\scoped_ nodes.py( 203)import_ module( ) astng_from_ module_ name(absmodname ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\manager. py(187) astng_from_ module_ name() from_file( filepath, modname, fallback=False) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\manager. py(138) astng_from_ file() self).file_ build(filepath, modname) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\builder. py(118) file_build( ) build(data, modname, path) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\builder. py(128) string_ build() build(parse( data + '\n'), modname, path) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\builder. py(147) ast_build( ) walk(node) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\rebuilder .py(94) walk() visit_assattr( dnode) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\rebuilder .py(278) delayed_ visit_assattr( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(203) wrapped( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(168) _infer_ stmts() context) : lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(203) wrapped( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\inference .py(391) infer_ass( ) assigned_ stmts(context= context) ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(231) wrapper( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\protocols .py(251) assign_ assigned_ stmts() asspart( self.value. infer(context) , asspath, context): lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\protocols .py(261) _resolve_ asspart( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(203) wrapped( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(231) wrapper( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\inference .py(201) infer_callfunc( ) infer(context) : lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(203) wrapped( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(231) wrapper( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\inference .py(259) infer_getattr( ) self.attrname, context): lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(273) igetattr( ) attr(self. getattr( name, context, lookupclass=False), context), lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(248) getattr( ) instance_ attr(name, context) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\inference .py(53) _set_proxied( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(44)_ _getattr_ _() self._proxied, name) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\inference .py(53) _set_proxied( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(44)_ _getattr_ _() self._proxied, name) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\inference .py(53) _set_proxied( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(44)_ _getattr_ _() self._proxied, name) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\inference .py(53) _set_proxied( ) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\infutils. py(44)_ _getattr_ _() self._proxied, name) lib\site- packages\ logilab_ astng-0. 19.3-py2. 6.egg\logilab\ astng\inference .py(53) _set_proxied( )
> c:\python26\
-> self._rcfile = None
(Pdb) disable 1
(Pdb) c
manager.py: astng_from_file: not in cache: c:\leo.
manager.py: astng_from_file: calling file_build(
manager.py: astng_from_file: not in cache: c:\leo.
manager.py: astng_from_file: calling file_build(leo)
manager.py: astng_from_file: not in cache: c:\leo.
manager.py: astng_from_file: calling file_build(
manager.py: astng_from_file: not in cache: c:\leo.
manager.py: astng_from_file: calling file_build(
manager.py: astng_from_file: not in cache: c:\leo.
manager.py: astng_from_file: calling file_build(
(debugger.
> c:\python26\
-> def _set_proxied(
(Pdb) w
('pdb.py:
c:\python26\
-> res = func(*args, **kwds)
c:\python26\
-> linter.check(args)
c:\python26\
-> self.check_
c:\python26\
-> self.astng_
c:\python26\
-> self.astng_
c:\python26\
-> checker.
c:\python26\
-> method(node)
c:\python26\
-> self._check_
c:\python26\
-> module = module.
c:\python26\
-> nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)]
c:\python26\
-> return [self.import_
c:\python26\
-> return MANAGER.
c:\python26\
-> return self.astng_
c:\python26\
-> astng = ASTNGBuilder(
c:\python26\
-> node = self.string_
c:\python26\
-> return self.ast_
c:\python26\
-> self.rebuilder.
c:\python26\
-> self.delayed_
c:\python26\
-> for infered in node.expr.infer():
c:\python26\
-> for res in _func(node, context, **kwargs):
c:\python26\
-> for infered in stmt.infer(
c:\python26\
-> for res in _func(node, context, **kwargs):
c:\python26\
-> stmts = list(self.
c:\python26\
-> for node in func(*args, **kwargs):
c:\python26\
-> for infered in _resolve_
c:\python26\
-> for part in parts:
c:\python26\
-> for res in _func(node, context, **kwargs):
c:\python26\
-> for node in func(*args, **kwargs):
c:\python26\
-> for callee in self.func.
c:\python26\
-> for res in _func(node, context, **kwargs):
c:\python26\
-> for node in func(*args, **kwargs):
c:\python26\
-> for obj in owner.igetattr(
c:\python26\
-> self._wrap_
c:\python26\
-> values = self._proxied.
c:\python26\
-> if not hasattr(const, '__proxied'):
c:\python26\
-> return getattr(
c:\python26\
-> if not hasattr(const, '__proxied'):
c:\python26\
-> return getattr(
c:\python26\
-> if not hasattr(const, '__proxied'):
c:\python26\
-> return getattr(
c:\python26\
-> if not hasattr(const, '__proxied'):
c:\python26\
-> return getattr(
c:\python26\
And so on...
I still don't understand the code, but now I know where the unbounded recursion is.
Edward