Skip to content

Commit 0e3b9eb

Browse files
Fix wildcard import false positive (#10980)
Signed-off-by: Emmanuel Ferdman <[email protected]>
1 parent f3e3439 commit 0e3b9eb

4 files changed

Lines changed: 41 additions & 5 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix ``used-before-assignment`` false positive for names bound by ``from X import *`` in every branch of an ``if``/``elif``/``else`` chain.
2+
3+
Refs #10980

pylint/checkers/variables.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ def _is_from_future_import(stmt: nodes.ImportFrom, name: str) -> bool | None:
9898
return None
9999

100100

101+
def _wildcard_import_binds(stmt: nodes.ImportFrom, name: str) -> bool:
102+
"""Check if the name is exported by a `from X import *` statement."""
103+
if not any(node_name[0] == "*" for node_name in stmt.names):
104+
return False
105+
try:
106+
return name in stmt.do_import_module(stmt.modname).wildcard_import_names()
107+
except astroid.AstroidBuildingError:
108+
return False
109+
110+
101111
def _get_unpacking_extra_info(node: nodes.Assign, inferred: InferenceResult) -> str:
102112
"""Return extra information to add to the message for unpacking-non-sequence
103113
and unbalanced-tuple/dict-unpacking errors.
@@ -957,11 +967,17 @@ def _defines_name_raises_or_returns(name: str, node: nodes.NodeNG) -> bool:
957967
for child_named_expr in node.nodes_of_class(nodes.NamedExpr)
958968
):
959969
return True
960-
if isinstance(node, (nodes.Import, nodes.ImportFrom)) and any(
961-
(node_name[1] and node_name[1] == name)
962-
or (node_name[0] == name)
963-
or (node_name[0].startswith(name + "."))
964-
for node_name in node.names
970+
if isinstance(node, (nodes.Import, nodes.ImportFrom)) and (
971+
any(
972+
(node_name[1] and node_name[1] == name)
973+
or (node_name[0] == name)
974+
or (node_name[0].startswith(name + "."))
975+
for node_name in node.names
976+
)
977+
or (
978+
isinstance(node, nodes.ImportFrom)
979+
and _wildcard_import_binds(node, name)
980+
)
965981
):
966982
return True
967983
if isinstance(node, nodes.With) and any(

tests/functional/u/used/used_before_assignment.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,19 @@ def conditional_import():
297297
os = None
298298
if os:
299299
pass
300+
301+
302+
def conditional_wildcard_import(): # pylint: disable=too-many-locals
303+
if input():
304+
from os.path import * # pylint: disable=wildcard-import
305+
else:
306+
from os.path import * # pylint: disable=wildcard-import
307+
print(join("a", "b"))
308+
309+
310+
def conditional_wildcard_import_unresolvable():
311+
if input():
312+
x = 1
313+
else:
314+
from __nonexistent_module_xyz__ import * # pylint: disable=wildcard-import, import-error
315+
print(x) # [possibly-used-before-assignment]

tests/functional/u/used/used_before_assignment.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ used-before-assignment:239:10:239:11::Using variable 'x' before assignment:CONTR
1717
possibly-used-before-assignment:253:10:253:15:__:Possibly using variable 'fail1' before assignment:CONTROL_FLOW
1818
used-before-assignment:267:18:267:19:outer_.inner_try:Using variable 'a' before assignment:HIGH
1919
used-before-assignment:278:18:278:19:outer_.inner_while:Using variable 'a' before assignment:HIGH
20+
possibly-used-before-assignment:315:10:315:11:conditional_wildcard_import_unresolvable:Possibly using variable 'x' before assignment:CONTROL_FLOW

0 commit comments

Comments
 (0)