Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite recursion when inferring from monkey-patched class #749

Closed
eggertm opened this issue Feb 3, 2020 · 1 comment
Closed

Infinite recursion when inferring from monkey-patched class #749

eggertm opened this issue Feb 3, 2020 · 1 comment
Labels

Comments

@eggertm
Copy link

eggertm commented Feb 3, 2020

Steps to reproduce

monkeypatch.zip

I encountered this when attempting to lint my own project which is using the simplekml package (pip install simplekml).
simplekml has functionality to monkey-patch xml.dom.minidom.Element with a special class, simplekml.KmlElement, which provides a special writexml method, while inheriting the monkey-patched Element class.
Apparently, astroid does not like this monkey patching behavior, and appears to enter an infinite loop when trying to infer the parent class. This is probably due to KmlElement overwriting what is already declared as it's parent?

Anyway, I've reduced the test down to the two files in the attached zip file, extracting those should be enough.

  1. extract the files from the monkeypatch.zip file
  2. pylint monkeypatch.py

Current behavior

monkeypatch.py:8:52: C0303: Trailing whitespace (trailing-whitespace)
monkeypatch.py:1:0: C0114: Missing module docstring (missing-module-docstring)
monkeypatch.py:3:0: C0115: Missing class docstring (missing-class-docstring)
Traceback (most recent call last):
File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
"main", mod_spec)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/usr/local/lib/python3.6/dist-packages/pylint/main.py", line 7, in
pylint.run_pylint()
File "/usr/local/lib/python3.6/dist-packages/pylint/init.py", line 23, in run_pylint
PylintRun(sys.argv[1:])
File "/usr/local/lib/python3.6/dist-packages/pylint/lint.py", line 1731, in init
linter.check(args)
File "/usr/local/lib/python3.6/dist-packages/pylint/lint.py", line 1004, in check
self._do_check(files_or_modules)
File "/usr/local/lib/python3.6/dist-packages/pylint/lint.py", line 1165, in _do_check
self.check_astroid_module(ast_node, walker, rawcheckers, tokencheckers)
File "/usr/local/lib/python3.6/dist-packages/pylint/lint.py", line 1252, in check_astroid_module
walker.walk(ast_node)
File "/usr/local/lib/python3.6/dist-packages/pylint/utils/ast_walker.py", line 77, in walk
self.walk(child)
File "/usr/local/lib/python3.6/dist-packages/pylint/utils/ast_walker.py", line 74, in walk
callback(astroid)
File "/usr/local/lib/python3.6/dist-packages/pylint/checkers/typecheck.py", line 845, in visit_classdef
metaclass = node.declared_metaclass()
File "/usr/local/lib/python3.6/dist-packages/astroid/scoped_nodes.py", line 2594, in declared_metaclass
for baseobj in base.infer(context=context):
File "/usr/local/lib/python3.6/dist-packages/astroid/decorators.py", line 141, in raise_if_nothing_inferred
yield from generator
File "/usr/local/lib/python3.6/dist-packages/astroid/decorators.py", line 95, in wrapped
res = next(generator)
File "/usr/local/lib/python3.6/dist-packages/astroid/inference.py", line 311, in infer_attribute
yield from owner.igetattr(self.attrname, context)
File "/usr/local/lib/python3.6/dist-packages/astroid/bases.py", line 133, in _infer_stmts
for inferred in stmt.infer(context=context):
File "/usr/local/lib/python3.6/dist-packages/astroid/util.py", line 160, in limit_inference
yield from islice(iterator, size)
File "/usr/local/lib/python3.6/dist-packages/astroid/context.py", line 113, in cache_generator
for result in generator:

.....
File "/usr/local/lib/python3.6/dist-packages/astroid/decorators.py", line 95, in wrapped
res = next(generator)
File "/usr/local/lib/python3.6/dist-packages/astroid/inference.py", line 287, in infer_attribute
for owner in self.expr.infer(context):
File "/usr/local/lib/python3.6/dist-packages/astroid/util.py", line 160, in limit_inference
yield from islice(iterator, size)
File "/usr/local/lib/python3.6/dist-packages/astroid/context.py", line 113, in cache_generator
for result in generator:
File "/usr/local/lib/python3.6/dist-packages/astroid/decorators.py", line 131, in raise_if_nothing_inferred
yield next(generator)
File "/usr/local/lib/python3.6/dist-packages/astroid/decorators.py", line 92, in wrapped
generator = _func(node, context, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/astroid/inference.py", line 197, in infer_name
context = contextmod.copy_context(context)
File "/usr/local/lib/python3.6/dist-packages/astroid/context.py", line 155, in copy_context
return context.clone()
File "/usr/local/lib/python3.6/dist-packages/astroid/context.py", line 102, in clone
clone = InferenceContext(self.path, inferred=self.inferred)
RecursionError: maximum recursion depth exceeded while calling a Python object

Expected behavior

Ideally this weird monkeypatch behavior should be tolerated (perhaps with a warning from pylint?).

python -c "from astroid import __pkginfo__; print(__pkginfo__.version)" output

2.3.3, also 1.6.6

@PCManticore
Copy link
Contributor

Thanks for the report! I'd have to dive deep to figure out why the recursion happens, but as you mentioned it's definitely related to the monkeypatching overwriting an existing attribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants