pylint-dev/astroid

[BUG] Cannot import Astroid with schemathesis 3.19.6

Opened this issue · 4 comments

Describe the bug
When I start a pytest session the astroid package enters an infinite loop when it is imported.

To Reproduce
I prepared a pipenv file that creates an environment to reproduce the issues.

Once you downloaded all files run

  1. make .venv
  2. run-pipenv run python -m pytest t.py --pdb

This will drop you in a debugger session at the place where astroid is stuck in a loop. With version 3.19.0 everything still works.

Expected behavior

Adding schemathesis as a dependency has no impact if I can import astroid in a pytest environment or not.

Environment (please complete the following information):

  • OS: linux
  • Python: 3.11

Additional context
I'm not sure what the root cause is so I will also open tickets for this with pytest and schemathesis

Pure Python reproducer from over at pytest-dev/pytest#11053 (comment):

from dataclasses import dataclass

print(type(__builtins__))
print(hasattr(__builtins__, "__builtins__"))

@dataclass
class Bla(Exception):

    __module__ = "builtins"

print(type(__builtins__))
print(hasattr(__builtins__, "__builtins__"))

import astroid.nodes

Apparently, for whatever reason, declaring a dataclass like this causes a __builtins__.__builtins__ to appear (which is __builtins__ again), and that causes astroid to choke with an endless recursion:

Traceback (most recent call last):
  File "/home/florian/tmp/blabla/dc.py", line 14, in <module>
    import astroid.nodes
  File "/usr/lib/python3.11/site-packages/astroid/__init__.py", line 46, in <module>
    from astroid import inference, raw_building
  File "/usr/lib/python3.11/site-packages/astroid/inference.py", line 17, in <module>
    from astroid import bases, constraint, decorators, helpers, nodes, protocols, util
  File "/usr/lib/python3.11/site-packages/astroid/helpers.py", line 11, in <module>
    from astroid import bases, manager, nodes, objects, raw_building, util
  File "/usr/lib/python3.11/site-packages/astroid/raw_building.py", line 643, in <module>
    _astroid_bootstrapping()
  File "/usr/lib/python3.11/site-packages/astroid/raw_building.py", line 544, in _astroid_bootstrapping
    astroid_builtin = builder.inspect_build(builtins)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/astroid/raw_building.py", line 404, in inspect_build
    self.object_build(node, module)
  File "/usr/lib/python3.11/site-packages/astroid/raw_building.py", line 455, in object_build
    attach_const_node(node, name, member)
  File "/usr/lib/python3.11/site-packages/astroid/raw_building.py", line 78, in attach_const_node
    _attach_local_node(node, nodes.const_factory(value), name)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/astroid/nodes/node_classes.py", line 5641, in const_factory
    instance.postinit(_create_dict_items(value, instance))
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/astroid/nodes/node_classes.py", line 5615, in _create_dict_items
    value_node = const_factory(value)
                 ^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/astroid/nodes/node_classes.py", line 5641, in const_factory
    instance.postinit(_create_dict_items(value, instance))
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/astroid/nodes/node_classes.py", line 5615, in _create_dict_items
    value_node = const_factory(value)
                 ^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/astroid/nodes/node_classes.py", line 5641, in const_factory
    instance.postinit(_create_dict_items(value, instance))
  [...]

Thank you for the investigation @The-Compiler !

Even smaller reproducer:

python3 -c 'exec("", __builtins__.__dict__, {}); import astroid.nodes'

And this behavior actually seems to be documented:

If the globals dictionary does not contain a value for the key __builtins__, a reference to the dictionary of the built-in module builtins is inserted under that key. That way you can control what builtins are available to the executed code by inserting your own __builtins__ dictionary into globals before passing it to exec().

So the solution for astroid would be to handle builtins as a special case and remove the self-reference if it exists.