Skip to content

Commit a0cc074

Browse files
Avoid a global state in utils.builtin_lookup and avoid reinstantiating TransformVisitor (#1563)
The global state of `utils.builtin_lookup` could be out-of-date if the builtins module had been rebuilt by AstroidManager.clear_cache(). Also, reinstantiating `TransformVisitor` in `clear_cache()` could lead to to diverging registries of transforms.
1 parent 6698b70 commit a0cc074

File tree

5 files changed

+31
-10
lines changed

5 files changed

+31
-10
lines changed

ChangeLog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ Release date: TBA
1717

1818
* Allowed ``AstroidManager.clear_cache`` to reload necessary brain plugins.
1919

20+
* Fixed incorrect inferences after rebuilding the builtins module, e.g. by calling
21+
``AstroidManager.clear_cache``.
22+
23+
Closes #1559
24+
2025
* Rename ``ModuleSpec`` -> ``module_type`` constructor parameter to match attribute
2126
name and improve typing. Use ``type`` instead.
2227

astroid/manager.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from __future__ import annotations
1111

12+
import collections
1213
import os
1314
import types
1415
import zipimport
@@ -377,7 +378,8 @@ def clear_cache(self) -> None:
377378
clear_inference_tip_cache()
378379

379380
self.astroid_cache.clear()
380-
AstroidManager.brain["_transform"] = TransformVisitor()
381+
# NB: not a new TransformVisitor()
382+
AstroidManager.brain["_transform"].transforms = collections.defaultdict(list)
381383

382384
for lru_cache in (
383385
LookupMixIn.lookup,

astroid/nodes/scoped_nodes/utils.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
from __future__ import annotations
1010

11-
import builtins
1211
from collections.abc import Sequence
1312
from typing import TYPE_CHECKING
1413

@@ -18,18 +17,18 @@
1817
from astroid import nodes
1918

2019

21-
_builtin_astroid: nodes.Module | None = None
22-
23-
2420
def builtin_lookup(name: str) -> tuple[nodes.Module, Sequence[nodes.NodeNG]]:
2521
"""Lookup a name in the builtin module.
2622
2723
Return the list of matching statements and the ast for the builtin module
2824
"""
29-
# pylint: disable-next=global-statement
30-
global _builtin_astroid
31-
if _builtin_astroid is None:
32-
_builtin_astroid = AstroidManager().ast_from_module(builtins)
25+
manager = AstroidManager()
26+
try:
27+
_builtin_astroid = manager.builtins_module
28+
except KeyError:
29+
# User manipulated the astroid cache directly! Rebuild everything.
30+
manager.clear_cache()
31+
_builtin_astroid = manager.builtins_module
3332
if name == "__dict__":
3433
return _builtin_astroid, ()
3534
try:

astroid/transforms.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class TransformVisitor:
2020
:meth:`~visit` with an *astroid* module and the class
2121
will take care of the rest, walking the tree and running the
2222
transforms for each encountered node.
23+
24+
Based on its usage in AstroidManager.brain, it should not be reinstantiated.
2325
"""
2426

2527
def __init__(self):

tests/unittest_manager.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ def test_borg(self) -> None:
318318
self.assertIs(built, second_built)
319319

320320

321-
class ClearCacheTest(unittest.TestCase, resources.AstroidCacheSetupMixin):
321+
class ClearCacheTest(unittest.TestCase):
322322
def test_clear_cache_clears_other_lru_caches(self) -> None:
323323
lrus = (
324324
astroid.nodes.node_classes.LookupMixIn.lookup,
@@ -362,6 +362,19 @@ def test_brain_plugins_reloaded_after_clearing_cache(self) -> None:
362362
inferred = next(format_call.infer())
363363
self.assertIsInstance(inferred, Const)
364364

365+
def test_builtins_inference_after_clearing_cache(self) -> None:
366+
astroid.MANAGER.clear_cache()
367+
isinstance_call = astroid.extract_node("isinstance(1, int)")
368+
inferred = next(isinstance_call.infer())
369+
self.assertIs(inferred.value, True)
370+
371+
def test_builtins_inference_after_clearing_cache_manually(self) -> None:
372+
# Not recommended to manipulate this, so we detect it and call clear_cache() instead
373+
astroid.MANAGER.brain["astroid_cache"].clear()
374+
isinstance_call = astroid.extract_node("isinstance(1, int)")
375+
inferred = next(isinstance_call.infer())
376+
self.assertIs(inferred.value, True)
377+
365378

366379
if __name__ == "__main__":
367380
unittest.main()

0 commit comments

Comments
 (0)