Skip to content

Infinite recursion when inferring from monkey-patched class #749

@eggertm

Description

@eggertm

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions