Skip to content

Commit

Permalink
Fix used-before-assignment for variable annotations guarded by TYPE…
Browse files Browse the repository at this point in the history
…_CHECKING (#7810)
  • Loading branch information
jacobtylerwalls authored and Pierre-Sassoulas committed Nov 23, 2022
1 parent 1baf4be commit df5ebb5
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 8 deletions.
4 changes: 4 additions & 0 deletions doc/whatsnew/fragments/7609.false_positive
@@ -0,0 +1,4 @@
Fix a false positive for ``used-before-assignment`` for imports guarded by
``typing.TYPE_CHECKING`` later used in variable annotations.

Closes #7609
18 changes: 14 additions & 4 deletions pylint/checkers/variables.py
Expand Up @@ -1998,12 +1998,22 @@ def _is_variable_violation(
)

# Look for type checking definitions inside a type checking guard.
if isinstance(defstmt, (nodes.Import, nodes.ImportFrom)):
# Relevant for function annotations only, not variable annotations (AnnAssign)
if (
isinstance(defstmt, (nodes.Import, nodes.ImportFrom))
and isinstance(defstmt.parent, nodes.If)
and defstmt.parent.test.as_string() in TYPING_TYPE_CHECKS_GUARDS
):
defstmt_parent = defstmt.parent

if (
isinstance(defstmt_parent, nodes.If)
and defstmt_parent.test.as_string() in TYPING_TYPE_CHECKS_GUARDS
maybe_annotation = utils.get_node_first_ancestor_of_type(
node, nodes.AnnAssign
)
if not (
maybe_annotation
and utils.get_node_first_ancestor_of_type(
maybe_annotation, nodes.FunctionDef
)
):
# Exempt those definitions that are used inside the type checking
# guard or that are defined in both type checking guard branches.
Expand Down
18 changes: 17 additions & 1 deletion tests/functional/u/used/used_before_assignment_typing.py
Expand Up @@ -2,8 +2,10 @@
# pylint: disable=missing-function-docstring


from typing import List, Optional
from typing import List, Optional, TYPE_CHECKING

if TYPE_CHECKING:
import datetime

class MyClass:
"""Type annotation or default values for first level methods can't refer to their own class"""
Expand Down Expand Up @@ -74,3 +76,17 @@ def function(self, var: int) -> None:

def other_function(self) -> None:
_x: MyThirdClass = self


class VariableAnnotationsGuardedByTypeChecking: # pylint: disable=too-few-public-methods
"""Class to test conditional imports guarded by TYPE_CHECKING then used in
local (function) variable annotations, which are not evaluated at runtime.
See: https://github.com/PyCQA/pylint/issues/7609
"""

still_an_error: datetime.date # [used-before-assignment]

def print_date(self, date) -> None:
date: datetime.date = date
print(date)
7 changes: 4 additions & 3 deletions tests/functional/u/used/used_before_assignment_typing.txt
@@ -1,3 +1,4 @@
undefined-variable:12:21:12:28:MyClass.incorrect_typing_method:Undefined variable 'MyClass':UNDEFINED
undefined-variable:17:26:17:33:MyClass.incorrect_nested_typing_method:Undefined variable 'MyClass':UNDEFINED
undefined-variable:22:20:22:27:MyClass.incorrect_default_method:Undefined variable 'MyClass':UNDEFINED
undefined-variable:14:21:14:28:MyClass.incorrect_typing_method:Undefined variable 'MyClass':UNDEFINED
undefined-variable:19:26:19:33:MyClass.incorrect_nested_typing_method:Undefined variable 'MyClass':UNDEFINED
undefined-variable:24:20:24:27:MyClass.incorrect_default_method:Undefined variable 'MyClass':UNDEFINED
used-before-assignment:88:20:88:28:VariableAnnotationsGuardedByTypeChecking:Using variable 'datetime' before assignment:HIGH

0 comments on commit df5ebb5

Please sign in to comment.