Skip to content

Delay astroid_bootstrapping() until instantiating AstroidBuilder #2210

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

Merged
merged 2 commits into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ Release date: TBA

* Reduce file system access in ``ast_from_file()``.

* Reduce time to ``import astroid`` by delaying ``astroid_bootstrapping()`` until
the first instantiation of ``AstroidBuilder``.

Closes #2161

* Make ``igetattr()`` idempotent. This addresses some reports of varying results
when running pylint with ``--jobs``.

Expand All @@ -37,7 +42,7 @@ Release date: TBA
We have tried to minimize the amount of breaking changes caused by this change
but some are unavoidable.

* ``infer_call_result`` now shares the same interface across all implemenations. Namely:
* ``infer_call_result`` now shares the same interface across all implementations. Namely:
```python
def infer_call_result(
self,
Expand Down
14 changes: 8 additions & 6 deletions astroid/brain/brain_builtin_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,14 @@ def _extend_builtins(class_transforms):
transform(builtin_ast[class_name])


_extend_builtins(
{
"bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"),
"str": partial(_extend_string_class, code=STR_CLASS, rvalue="''"),
}
)
def on_bootstrap():
"""Called by astroid_bootstrapping()."""
_extend_builtins(
{
"bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"),
"str": partial(_extend_string_class, code=STR_CLASS, rvalue="''"),
}
)


def _builtin_filter_predicate(node, builtin_name) -> bool:
Expand Down
19 changes: 9 additions & 10 deletions astroid/brain/brain_nose.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@
import re
import textwrap

import astroid.builder
from astroid.bases import BoundMethod
from astroid.brain.helpers import register_module_extender
from astroid.builder import AstroidBuilder
from astroid.exceptions import InferenceError
from astroid.manager import AstroidManager

_BUILDER = astroid.builder.AstroidBuilder(AstroidManager())

from astroid.nodes import List, Module

CAPITALS = re.compile("([A-Z])")

Expand All @@ -24,7 +23,7 @@ def _pep8(name, caps=CAPITALS):

def _nose_tools_functions():
"""Get an iterator of names and bound methods."""
module = _BUILDER.string_build(
module = AstroidBuilder().string_build(
textwrap.dedent(
"""
import unittest
Expand All @@ -42,10 +41,10 @@ class Test(unittest.TestCase):
for method in case.methods():
if method.name.startswith("assert") and "_" not in method.name:
pep8_name = _pep8(method.name)
yield pep8_name, astroid.BoundMethod(method, case)
yield pep8_name, BoundMethod(method, case)
if method.name == "assertEqual":
# nose also exports assert_equals.
yield "assert_equals", astroid.BoundMethod(method, case)
yield "assert_equals", BoundMethod(method, case)


def _nose_tools_transform(node):
Expand All @@ -55,7 +54,7 @@ def _nose_tools_transform(node):

def _nose_tools_trivial_transform():
"""Custom transform for the nose.tools module."""
stub = _BUILDER.string_build("""__all__ = []""")
stub = AstroidBuilder().string_build("""__all__ = []""")
all_entries = ["ok_", "eq_"]

for pep8_name, method in _nose_tools_functions():
Expand All @@ -65,7 +64,7 @@ def _nose_tools_trivial_transform():
# Update the __all__ variable, since nose.tools
# does this manually with .append.
all_assign = stub["__all__"].parent
all_object = astroid.List(all_entries)
all_object = List(all_entries)
all_object.parent = all_assign
all_assign.value = all_object
return stub
Expand All @@ -75,5 +74,5 @@ def _nose_tools_trivial_transform():
AstroidManager(), "nose.tools.trivial", _nose_tools_trivial_transform
)
AstroidManager().register_transform(
astroid.Module, _nose_tools_transform, lambda n: n.name == "nose.tools"
Module, _nose_tools_transform, lambda n: n.name == "nose.tools"
)
2 changes: 2 additions & 0 deletions astroid/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ def __init__(
) -> None:
super().__init__(manager)
self._apply_transforms = apply_transforms
if not raw_building.InspectBuilder.bootstrapped:
raw_building._astroid_bootstrapping()

def module_build(
self, module: types.ModuleType, modname: str | None = None
Expand Down
10 changes: 9 additions & 1 deletion astroid/raw_building.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ class InspectBuilder:
FunctionDef and ClassDef nodes and some others as guessed.
"""

bootstrapped: bool = False

def __init__(self, manager_instance: AstroidManager | None = None) -> None:
self._manager = manager_instance or AstroidManager()
self._done: dict[types.ModuleType | type, nodes.Module | nodes.ClassDef] = {}
Expand Down Expand Up @@ -725,5 +727,11 @@ def _astroid_bootstrapping() -> None:
builder.object_build(klass, _type)
astroid_builtin[_type.__name__] = klass

InspectBuilder.bootstrapped = True

# pylint: disable-next=import-outside-toplevel
from astroid.brain.brain_builtin_inference import on_bootstrap

_astroid_bootstrapping()
# Instantiates an AstroidBuilder(), which is where
# InspectBuilder.bootstrapped is checked, so place after bootstrapped=True.
on_bootstrap()