Skip to content

Commit 9518cd4

Browse files
committed
Add early transforms before handling delayed assattr nodes
When importing Gtk, it looks like this: import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk It is vital that gi.require_version() is made before related 'from gi.repository import ...'. The brain_gi tries to do that using transforms. And it works unless Gtk is imported as part of delayed assattr handling. Fix this by adding early transforms that are called before delayed assattr. Fixes #2190 Fixes pylint-dev/pylint#6352
1 parent 453d307 commit 9518cd4

File tree

5 files changed

+41
-1
lines changed

5 files changed

+41
-1
lines changed

astroid/brain/brain_gi.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,6 @@ def _register_require_version(node):
245245

246246
def register(manager: AstroidManager) -> None:
247247
manager.register_failed_import_hook(_import_gi_module)
248-
manager.register_transform(
248+
manager.register_early_transform(
249249
nodes.Call, _register_require_version, _looks_like_require_version
250250
)

astroid/builder.py

+5
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def module_build(
102102
if self._apply_transforms:
103103
# We have to handle transformation by ourselves since the
104104
# rebuilder isn't called for builtin nodes
105+
node = self._manager.visit_early_transforms(node)
105106
node = self._manager.visit_transforms(node)
106107
assert isinstance(node, nodes.Module)
107108
return node
@@ -164,6 +165,10 @@ def _post_build(
164165
for symbol, _ in from_node.names:
165166
module.future_imports.add(symbol)
166167
self.add_from_names_to_locals(from_node)
168+
# Visit the transforms
169+
if self._apply_transforms:
170+
module = self._manager.visit_early_transforms(module)
171+
167172
# handle delayed assattr nodes
168173
for delayed in builder._delayed_assattr:
169174
self.delayed_assattr(delayed)

astroid/manager.py

+18
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class AstroidManager:
6262
"extension_package_whitelist": set(),
6363
"module_denylist": set(),
6464
"_transform": TransformVisitor(),
65+
"_early_transform": TransformVisitor(),
6566
"prefer_stubs": False,
6667
}
6768

@@ -75,6 +76,7 @@ def __init__(self) -> None:
7576
]
7677
self.module_denylist = AstroidManager.brain["module_denylist"]
7778
self._transform = AstroidManager.brain["_transform"]
79+
self._early_transform = AstroidManager.brain["_early_transform"]
7880
self.prefer_stubs = AstroidManager.brain["prefer_stubs"]
7981

8082
@property
@@ -110,6 +112,15 @@ def register_transform(self):
110112
def unregister_transform(self):
111113
return self._transform.unregister_transform
112114

115+
@property
116+
def register_early_transform(self):
117+
# This and unregister_early_transform below are exported for convenience
118+
return self._early_transform.register_transform
119+
120+
@property
121+
def unregister_early_transform(self):
122+
return self._early_transform.unregister_transform
123+
113124
@property
114125
def builtins_module(self) -> nodes.Module:
115126
return self.astroid_cache["builtins"]
@@ -126,6 +137,10 @@ def visit_transforms(self, node: nodes.NodeNG) -> InferenceResult:
126137
"""Visit the transforms and apply them to the given *node*."""
127138
return self._transform.visit(node)
128139

140+
def visit_early_transforms(self, node: nodes.NodeNG) -> InferenceResult:
141+
"""Visit the early transforms and apply them to the given *node*."""
142+
return self._early_transform.visit(node)
143+
129144
def ast_from_file(
130145
self,
131146
filepath: str,
@@ -466,6 +481,9 @@ def clear_cache(self) -> None:
466481
self.astroid_cache.clear()
467482
# NB: not a new TransformVisitor()
468483
AstroidManager.brain["_transform"].transforms = collections.defaultdict(list)
484+
AstroidManager.brain["_early_transform"].transforms = collections.defaultdict(
485+
list
486+
)
469487

470488
for lru_cache in (
471489
LookupMixIn.lookup,

astroid/test_utils.py

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ def brainless_manager():
7373
m.astroid_cache = {}
7474
m._mod_file_cache = {}
7575
m._transform = transforms.TransformVisitor()
76+
m._early_transform = transforms.TransformVisitor()
7677
m.extension_package_whitelist = set()
7778
m.module_denylist = set()
7879
return m

tests/test_builder.py

+16
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,22 @@ def transform_time(node: Module) -> None:
487487
finally:
488488
self.manager.unregister_transform(nodes.Module, transform_time)
489489

490+
def test_inspect_early_transform_module(self) -> None:
491+
# ensure no cached version of the time module
492+
self.manager._mod_file_cache.pop(("time", None), None)
493+
self.manager.astroid_cache.pop("time", None)
494+
495+
def transform_time(node: Module) -> None:
496+
if node.name == "time":
497+
node.transformed = True
498+
499+
self.manager.register_early_transform(nodes.Module, transform_time)
500+
try:
501+
time_ast = self.manager.ast_from_module_name("time")
502+
self.assertTrue(getattr(time_ast, "transformed", False))
503+
finally:
504+
self.manager.unregister_early_transform(nodes.Module, transform_time)
505+
490506
def test_package_name(self) -> None:
491507
"""Test base properties and method of an astroid module."""
492508
datap = resources.build_file("data/__init__.py", "data")

0 commit comments

Comments
 (0)