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. | |
| priority | important |
|---|---|
| type | bug |
| appeared in | <not specified> |
| done in | 0.20.0 |
| load | 1.000 |
| load left | 0.000 |
| closed by | <not specified> |



#19422 test/fulltest.sh can't find runtests.py
Comments
ads by barry: I've definitely hit the same problem with the latest pylint, and I agree it has something to do with getattrs. If the devs need another test case, you can use the Mailman 3 source code: 'bzr branch lp:mailman'
benjamin reported a while ago a similar issue :
In fact I tested it a little more and it happens whenever I use some object from Panda3D (http://www.panda3d.org) Here's the code I tested which uses some object from Panda3D. It's the only test I did that gives this result
If I don't call "getDefault" or "setSize", I have no problem though.
def read_config( f): lines = ['junk'] for line in f: line = line.split('#', 1)[0].rstrip() # remove comments and newlines if len(line) != 0 and not line[0].isspace(): lines.append(line) # start a new config item else: lines[-1] += line # continue the previous config item pairs = [(line.split(None, 1) + [None])[:2] for line in lines] return pairs[1:]see for (much) more info: https://bugs.launchpad.net/pylint/+bug/456870
The error with the panda example from Benjamin is not exactly the same. And I tried with the latest revision of pylint/astng and it's still present. The traceback is :
File "/usr/local/lib/python2.6/dist-packages/logilab_astng-0.20.0-py2.6.egg/logilab/astng/bases.py", line 402, in root
return self.parent.root()
File "/usr/local/lib/python2.6/dist-packages/logilab_astng-0.20.0-py2.6.egg/logilab/astng/bases.py", line 402, in root
return self.parent.root()
File "/usr/local/lib/python2.6/dist-packages/logilab_astng-0.20.0-py2.6.egg/logilab/astng/bases.py", line 402, in root
return self.parent.root()
File "/usr/local/lib/python2.6/dist-packages/logilab_astng-0.20.0-py2.6.egg/logilab/astng/bases.py", line 402, in root
return self.parent.root()
RuntimeError: maximum recursion depth exceeded
Very basic code which triggers the bug:
===
def f(var="test"):
splits = var.split("_")
===
=> Exception RuntimeError: 'maximum recursion depth exceeded while calling a Python object' in <type 'exceptions.RuntimeError'> ignored
However, the following code does not trigger the bug:
===
def f(var):
splits = var.split("_")
===
Sander