Skip to content

Commit 3159b17

Browse files
committed
Exempt type checking definitions defined in both clauses of a type checking guard
Close #3127
1 parent 2fa5d43 commit 3159b17

File tree

3 files changed

+61
-33
lines changed

3 files changed

+61
-33
lines changed

pylint/checkers/variables.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,12 +1338,28 @@ def _is_variable_violation(
13381338
# Single statement function, with the statement on the
13391339
# same line as the function definition
13401340
maybee0601 = False
1341+
1342+
# Look for type checking definitions inside a type checking guard.
13411343
if isinstance(defstmt, (astroid.Import, astroid.ImportFrom)):
13421344
defstmt_parent = defstmt.parent
1343-
if isinstance(
1344-
defstmt_parent, astroid.If
1345-
) and not defstmt_parent.parent_of(node):
1346-
if defstmt_parent.test.as_string() in TYPING_TYPE_CHECKS_GUARDS:
1345+
1346+
if (
1347+
isinstance(defstmt_parent, astroid.If)
1348+
and defstmt_parent.test.as_string() in TYPING_TYPE_CHECKS_GUARDS
1349+
):
1350+
# Exempt those definitions that are used inside the type checking
1351+
# guard or that are defined in both type checking guard branches.
1352+
used_in_branch = defstmt_parent.parent_of(node)
1353+
defined_in_or_else = False
1354+
1355+
for definition in defstmt_parent.orelse:
1356+
if isinstance(definition, astroid.Assign):
1357+
defined_in_or_else = any(
1358+
target.name == name for target in definition.targets
1359+
)
1360+
break
1361+
1362+
if not used_in_branch and not defined_in_or_else:
13471363
maybee0601 = True
13481364

13491365
return maybee0601, annotation_return, use_outer_definition

tests/functional/u/undefined_variable.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# pylint: disable=missing-docstring, multiple-statements, useless-object-inheritance,import-outside-toplevel
22
# pylint: disable=too-few-public-methods, no-init, no-self-use,bare-except,broad-except, import-error
33
from __future__ import print_function
4+
5+
# pylint: disable=wrong-import-position
6+
from typing import TYPE_CHECKING
7+
48
DEFINED = 1
59

610
if DEFINED != 1:
@@ -247,8 +251,6 @@ def onclick(event):
247251
plt.show(block=True)
248252

249253

250-
# pylint: disable=wrong-import-position
251-
from typing import TYPE_CHECKING
252254

253255
if TYPE_CHECKING:
254256
from datetime import datetime
@@ -264,3 +266,13 @@ def func_should_fail(_dt: datetime): # [used-before-assignment]
264266
from typing_extensions import Literal
265267

266268
AllowedValues = Literal['hello', 'world']
269+
270+
271+
if TYPE_CHECKING:
272+
from collections import Counter
273+
else:
274+
Counter = object
275+
276+
277+
def tick(counter: Counter, name: str) -> None:
278+
counter[name] += 1
Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
1-
undefined-variable:7::Undefined variable 'unknown'
2-
undefined-variable:13:in_method:Undefined variable 'nomoreknown'
3-
undefined-variable:16::Undefined variable '__revision__'
4-
undefined-variable:18::Undefined variable '__revision__'
5-
undefined-variable:22:bad_default:Undefined variable 'unknown2'
6-
undefined-variable:25:bad_default:Undefined variable 'xxxx'
7-
undefined-variable:26:bad_default:Undefined variable 'augvar'
8-
undefined-variable:27:bad_default:Undefined variable 'vardel'
9-
undefined-variable:29:<lambda>:Undefined variable 'doesnotexist'
10-
undefined-variable:30:<lambda>:Undefined variable 'z'
11-
used-before-assignment:38::Using variable 'POUETT' before assignment
12-
used-before-assignment:51::Using variable 'PLOUF' before assignment
13-
used-before-assignment:60:if_branch_test:Using variable 'xxx' before assignment
14-
used-before-assignment:86:test_arguments:Using variable 'TestClass' before assignment
15-
used-before-assignment:90:TestClass:Using variable 'Ancestor' before assignment
16-
used-before-assignment:93:TestClass.MissingAncestor:Using variable 'Ancestor1' before assignment
17-
used-before-assignment:100:TestClass.test1.UsingBeforeDefinition:Using variable 'Empty' before assignment
18-
undefined-variable:114:Self:Undefined variable 'Self'
19-
undefined-variable:130::Undefined variable 'BAT'
20-
used-before-assignment:141:KeywordArgument.test1:Using variable 'enabled' before assignment
21-
undefined-variable:144:KeywordArgument.test2:Undefined variable 'disabled'
22-
undefined-variable:149:KeywordArgument.<lambda>:Undefined variable 'arg'
23-
undefined-variable:161::Undefined variable 'unicode_2'
24-
undefined-variable:171::Undefined variable 'unicode_4'
25-
undefined-variable:226:LambdaClass4.<lambda>:Undefined variable 'LambdaClass4'
26-
undefined-variable:234:LambdaClass5.<lambda>:Undefined variable 'LambdaClass5'
27-
used-before-assignment:257:func_should_fail:Using variable 'datetime' before assignment
1+
undefined-variable:11::Undefined variable 'unknown'
2+
undefined-variable:17:in_method:Undefined variable 'nomoreknown'
3+
undefined-variable:20::Undefined variable '__revision__'
4+
undefined-variable:22::Undefined variable '__revision__'
5+
undefined-variable:26:bad_default:Undefined variable 'unknown2'
6+
undefined-variable:29:bad_default:Undefined variable 'xxxx'
7+
undefined-variable:30:bad_default:Undefined variable 'augvar'
8+
undefined-variable:31:bad_default:Undefined variable 'vardel'
9+
undefined-variable:33:<lambda>:Undefined variable 'doesnotexist'
10+
undefined-variable:34:<lambda>:Undefined variable 'z'
11+
used-before-assignment:42::Using variable 'POUETT' before assignment
12+
used-before-assignment:55::Using variable 'PLOUF' before assignment
13+
used-before-assignment:64:if_branch_test:Using variable 'xxx' before assignment
14+
used-before-assignment:90:test_arguments:Using variable 'TestClass' before assignment
15+
used-before-assignment:94:TestClass:Using variable 'Ancestor' before assignment
16+
used-before-assignment:97:TestClass.MissingAncestor:Using variable 'Ancestor1' before assignment
17+
used-before-assignment:104:TestClass.test1.UsingBeforeDefinition:Using variable 'Empty' before assignment
18+
undefined-variable:118:Self:Undefined variable 'Self'
19+
undefined-variable:134::Undefined variable 'BAT'
20+
used-before-assignment:145:KeywordArgument.test1:Using variable 'enabled' before assignment
21+
undefined-variable:148:KeywordArgument.test2:Undefined variable 'disabled'
22+
undefined-variable:153:KeywordArgument.<lambda>:Undefined variable 'arg'
23+
undefined-variable:165::Undefined variable 'unicode_2'
24+
undefined-variable:175::Undefined variable 'unicode_4'
25+
undefined-variable:230:LambdaClass4.<lambda>:Undefined variable 'LambdaClass4'
26+
undefined-variable:238:LambdaClass5.<lambda>:Undefined variable 'LambdaClass5'
27+
used-before-assignment:259:func_should_fail:Using variable 'datetime' before assignment

0 commit comments

Comments
 (0)