Skip to content

Commit

Permalink
Don't treat strings inside typing.Literal as names
Browse files Browse the repository at this point in the history
  • Loading branch information
lggruspe committed Sep 2, 2022
1 parent 89351a5 commit fd6b29e
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 2 deletions.
19 changes: 19 additions & 0 deletions pylint/checkers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1891,6 +1891,25 @@ def in_type_checking_block(node: nodes.NodeNG) -> bool:
return False


def is_typing_literal(node: nodes.NodeNG) -> bool:
"""Check if a node refers to typing.Literal."""
if isinstance(node, nodes.Name):
import_from = node.lookup(node.name)[1][0]
if isinstance(import_from, nodes.ImportFrom):
return (
import_from.modname == "typing"
and import_from.real_name(node.name) == "Literal"
)
elif isinstance(node, nodes.Attribute):
inferred_module = safe_infer(node.expr)
return (
isinstance(inferred_module, nodes.Module)
and inferred_module.name == "typing"
and node.attrname == "Literal"
)
return False


@lru_cache()
def in_for_else_branch(parent: nodes.NodeNG, stmt: nodes.Statement) -> bool:
"""Returns True if stmt is inside the else branch for a parent For stmt."""
Expand Down
16 changes: 15 additions & 1 deletion pylint/checkers/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -2913,13 +2913,27 @@ def _check_potential_index_error(
"unused-variable",
)
def visit_const(self, node: nodes.Const) -> None:
"""Take note of names that appear inside string literal type annotations."""
"""Take note of names that appear inside string literal type annotations...
...Unless the string is a parameter to typing.Literal.
"""
if node.pytype() != "builtins.str":
return
if not utils.is_node_in_type_annotation_context(node):
return
if not node.value.isidentifier():
return

# Check if parent's or grandparent's first child is typing.Literal
parent = node.parent
if isinstance(parent, nodes.Tuple):
parent = parent.parent

if isinstance(parent, nodes.Subscript):
origin = next(parent.get_children(), None)
if origin is not None and utils.is_typing_literal(origin):
return

self._type_annotation_names.append(node.value)


Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""Test if pylint sees names inside string literal type annotations. #3299"""
from argparse import ArgumentParser, Namespace
from pathlib import Path
from typing import TypeAlias
from sys import argv # [unused-import]
from sys import version_info # [unused-import]
from typing import Literal as Lit, TypeAlias
import typing as t

def unused_variable_should_not_be_emitted():
"""unused-variable shouldn't be emitted for Example1."""
Expand All @@ -19,3 +22,6 @@ def example3(_: "ArgumentParser"):
class Class:
"""unused-import shouldn't be emitted for Namespace"""
cls: "Namespace"

# str inside Literal shouldn't be treated as names
example4: t.Literal["argv", Lit["version_info"]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
unused-import:4:0:4:20::Unused argv imported from sys:UNDEFINED
unused-import:5:0:5:28::Unused version_info imported from sys:UNDEFINED

0 comments on commit fd6b29e

Please sign in to comment.