logilab-astng #19641 "maximum recursion depth exceeded" messages w/ python 2.6 [validation pending]

Daniel Harding reported on python-projects.

This past June problems were reported with seeing messages like the following on stderr when running pylint:

Exception RuntimeError: 'maximum recursion depth exceeded while
calling a Python object' in <type 'exceptions.AttributeError'>
ignored

At the time it was attributed to a bug in Python 2.6.2. However, after upgrading to Python 2.6.4 I still saw the problem, so decided to investigate further, and eventually tracked it down to an infinite recursion in logilab-astng

I have attached a Python file which triggers the problem. The call stack which produces the message is as follows:

0  File "C:\Python26\Scripts\pylint.bat", line 12, in <module>
     lint.Run(sys.argv[1:])
1  File "C:\Python26\lib\site-packages\pylint-0.18.1-py2.6.egg\pylint\lint.py", line 884, in __init__
     linter.check(args)
2  File "C:\Python26\lib\site-packages\pylint-0.18.1-py2.6.egg\pylint\lint.py", line 501, in check
    self.check_astng_module(astng, checkers)
3  File "C:\Python26\lib\site-packages\pylint-0.18.1-py2.6.egg\pylint\lint.py", line 578, in check_astng_module
     if implements(checker, IASTNGChecker)])
4  File "C:\Python26\lib\site-packages\pylint-0.18.1-py2.6.egg\pylint\lint.py", line 595, in astng_events
     self.astng_events(child, checkers, _reversed_checkers)
5  File "C:\Python26\lib\site-packages\pylint-0.18.1-py2.6.egg\pylint\lint.py", line 595, in astng_events
     self.astng_events(child, checkers, _reversed_checkers)
6  File "C:\Python26\lib\site-packages\pylint-0.18.1-py2.6.egg\pylint\lint.py", line 592, in astng_events
     checker.visit(astng)
7  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\utils.py", line 320, in visit
     method(node)
8  File "C:\Python26\lib\site-packages\pylint-0.18.1-py2.6.egg\pylint\checkers\typecheck.py", line 207, in visit_callfunc
     called = safe_infer(node.func)
9  File "C:\Python26\lib\site-packages\pylint-0.18.1-py2.6.egg\pylint\checkers\utils.py", line 38, in safe_infer
     value = inferit.next()
10  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\infutils.py", line 203, in wrapped
     for res in _func(node, context, **kwargs):
11  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\infutils.py", line 231, in wrapper
     for node in func(*args, **kwargs):
12  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\inference.py", line 281, in infer_getattr
     for obj in owner.igetattr(self.attrname, context):
13  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\infutils.py", line 265, in igetattr
     self._wrap_attr(self.getattr(name, context, lookupclass=False), context),
14  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\infutils.py", line 248, in getattr
     return self._proxied.instance_attr(name, context)
15  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\inference.py", line 53, in _set_proxied
     if not hasattr(const, '__proxied'):
16  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\infutils.py", line 44, in __getattr__
     return getattr(self._proxied, name)
17  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\inference.py", line 53, in _set_proxied
     if not hasattr(const, '__proxied'):
18  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\infutils.py", line 44, in __getattr__
     return getattr(self._proxied, name)
19  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\inference.py", line 53, in _set_proxied
     if not hasattr(const, '__proxied'):
20  File "C:\Python26\lib\site-packages\logilab_astng-0.19.1-py2.6.egg\logilab\astng\infutils.py", line 44, in __getattr__
     return getattr(self._proxied, name)

and so on...

What is happening is that Instance.getattr (entry 14 in the call stack) is accessing the _proxied attribute of the node. This attribute is actually a property whose getter is the _set_proxied function (entry 15 in the call stack). The first thing this function does is call hasattr on the node to see if it has a __proxied attribute. Because this attribute does not yet exist on the node, the node's __getattr__ method is invoked (entry 16 in the call stack). It in turn attempts to call getattr on its _proxied attribute. This in turn invokes the _proxied property's _set_proxied function (entry 17 in the call stack), resulting in infinite recursion.

To reproduce, run

pylint -r n -f parseable bug.py

on the attached bug.py. I've also attached my .pylintrc.

Here's the output from running pylint --version on my system:

pylint.bat 0.18.1,
astng 0.19.1, common 0.45.2
Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)]

I don't understand what the inference system is doing well enough to know how to fix the problem, but hopefully this will give someone enough information to work on it.

priorityimportant
typebug
appeared in<not specified>
done in0.20.0
load1.000
load left0.000
closed by<not specified>