Skip to content

Commit

Permalink
Fix FN for invalid-name for type-annotated module constants
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtylerwalls committed Sep 18, 2024
1 parent 67acc96 commit 692895e
Show file tree
Hide file tree
Showing 17 changed files with 42 additions and 34 deletions.
4 changes: 4 additions & 0 deletions doc/whatsnew/fragments/9770.false_negative
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Check module-level constants with type annotations for ``invalid-name``.
Remember to adjust ``const-naming-style`` or ``const-rgx`` to your liking.

Closes #9770
6 changes: 3 additions & 3 deletions pylint/checkers/base/name_checker/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,10 +473,10 @@ def visit_assignname( # pylint: disable=too-many-branches

# Check names defined in AnnAssign nodes
elif isinstance(assign_type, nodes.AnnAssign):
if utils.is_assign_name_annotated_with(node, "Final"):
self._check_name("const", node.name, node)
elif self._assigns_typealias(assign_type.annotation):
if self._assigns_typealias(assign_type.annotation):
self._check_name("typealias", node.name, node)
else:
self._check_name("const", node.name, node)

# Check names defined in function scopes
elif isinstance(frame, nodes.FunctionDef):
Expand Down
4 changes: 2 additions & 2 deletions tests/functional/ext/private_import/private_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ def c2_func() -> _TypeContainerC.C:
# This is allowed since all the imports are used for typing
from _TypeContainerExtra import TypeExtraA, TypeExtraB
from MakerContainerExtra import GetA, GetB
extraA: TypeExtraA = GetA()
extraB: TypeExtraB = GetB()
extra_a: TypeExtraA = GetA()
extra_b: TypeExtraB = GetB()

# This is not allowed because there is an import not used for typing
from _TypeContainerExtra2 import TypeExtra2, NotTypeExtra # [import-private-name]
Expand Down
1 change: 1 addition & 0 deletions tests/functional/ext/private_import/private_import.rc
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[MAIN]
load-plugins=pylint.extensions.private_import,
const-naming-style=snake_case
2 changes: 1 addition & 1 deletion tests/functional/f/function_redefined.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def callback2():
return 24
return callback1(), callback2()

do_something: Callable[[], int]
do_something: Callable[[], int] # pylint: disable=invalid-name

def do_something() -> int:
return 1
4 changes: 2 additions & 2 deletions tests/functional/g/generic_alias/generic_alias_collections.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Test generic alias support for stdlib types (added in PY39)."""
# flake8: noqa
# pylint: disable=missing-docstring,pointless-statement
# pylint: disable=missing-docstring,pointless-statement,invalid-name
# pylint: disable=too-few-public-methods,multiple-statements,line-too-long
import abc
import collections
Expand Down Expand Up @@ -110,7 +110,7 @@ class CustomImplementation(CustomAbstractCls2): # [abstract-method,abstract-met
# Type annotations
var_tuple: tuple[int, int]
var_dict: dict[int, str]
var_orderedDict: collections.OrderedDict[int, str]
var_ordereddict: collections.OrderedDict[int, str]
var_container: collections.abc.Container[int]
var_sequence: collections.abc.Sequence[int]
var_iterable: collections.abc.Iterable[int]
Expand Down
6 changes: 3 additions & 3 deletions tests/functional/g/generic_alias/generic_alias_mixed_py39.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Test generic alias support with mix of typing.py and stdlib types (PY39+)."""
# flake8: noqa
# pylint: disable=missing-docstring,pointless-statement
# pylint: disable=missing-docstring,pointless-statement,invalid-name
# pylint: disable=too-few-public-methods,multiple-statements,line-too-long
import collections
import collections.abc
Expand All @@ -9,15 +9,15 @@
import typing

# Type annotations
var_orderedDict: collections.OrderedDict[int, str]
var_ordered_dict: collections.OrderedDict[int, str]
var_container: collections.abc.Container[int]
var_sequence: collections.abc.Sequence[int]
var_iterable: collections.abc.Iterable[int]
var_awaitable: collections.abc.Awaitable[int]
var_pattern: re.Pattern[int]
var_bytestring: collections.abc.ByteString
var_hashable: collections.abc.Hashable
var_ContextManager: contextlib.AbstractContextManager[int]
var_context_manager: contextlib.AbstractContextManager[int]


# No implementation required for 'builtins'
Expand Down
2 changes: 2 additions & 0 deletions tests/functional/i/invalid/invalid_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
except ValueError:
time = None # [invalid-name]

bbb: int = 42 # [invalid-name]

try:
from sys import argv, executable as python
except ImportError:
Expand Down
15 changes: 8 additions & 7 deletions tests/functional/i/invalid/invalid_name.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
invalid-name:12:0:12:3::"Constant name ""aaa"" doesn't conform to UPPER_CASE naming style":HIGH
invalid-name:16:4:16:8::"Constant name ""time"" doesn't conform to UPPER_CASE naming style":HIGH
invalid-name:36:0:36:5:A:"Function name ""A"" doesn't conform to snake_case naming style":HIGH
invalid-name:50:4:50:13::"Constant name ""Foocapfor"" doesn't conform to UPPER_CASE naming style":HIGH
invalid-name:66:0:66:68:a_very_very_very_long_function_name_WithCamelCase_to_make_it_sad:"Function name ""a_very_very_very_long_function_name_WithCamelCase_to_make_it_sad"" doesn't conform to snake_case naming style":HIGH
invalid-name:74:23:74:29:FooBar.__init__:"Argument name ""fooBar"" doesn't conform to snake_case naming style":HIGH
invalid-name:80:8:80:14:FooBar.func1:"Argument name ""fooBar"" doesn't conform to snake_case naming style":HIGH
invalid-name:100:8:100:15:FooBar.test_disable_mixed:"Argument name ""fooBar2"" doesn't conform to snake_case naming style":HIGH
invalid-name:111:4:111:25:FooBarSubclass:"Class attribute name ""tearDownNotInAncestor"" doesn't conform to snake_case naming style":HIGH
invalid-name:18:0:18:3::"Constant name ""bbb"" doesn't conform to UPPER_CASE naming style":HIGH
invalid-name:38:0:38:5:A:"Function name ""A"" doesn't conform to snake_case naming style":HIGH
invalid-name:52:4:52:13::"Constant name ""Foocapfor"" doesn't conform to UPPER_CASE naming style":HIGH
invalid-name:68:0:68:68:a_very_very_very_long_function_name_WithCamelCase_to_make_it_sad:"Function name ""a_very_very_very_long_function_name_WithCamelCase_to_make_it_sad"" doesn't conform to snake_case naming style":HIGH
invalid-name:76:23:76:29:FooBar.__init__:"Argument name ""fooBar"" doesn't conform to snake_case naming style":HIGH
invalid-name:82:8:82:14:FooBar.func1:"Argument name ""fooBar"" doesn't conform to snake_case naming style":HIGH
invalid-name:102:8:102:15:FooBar.test_disable_mixed:"Argument name ""fooBar2"" doesn't conform to snake_case naming style":HIGH
invalid-name:113:4:113:25:FooBarSubclass:"Class attribute name ""tearDownNotInAncestor"" doesn't conform to snake_case naming style":HIGH
2 changes: 1 addition & 1 deletion tests/functional/r/regression_02/regression_3979.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
else:
BasePathLike = os.PathLike

Foo: Union[str, BasePathLike] = "bar"
SOME_VAR: Union[str, BasePathLike] = "bar"
4 changes: 2 additions & 2 deletions tests/functional/t/type/typealias_naming_style_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@

# Regression tests
# They are not TypeAlias, and thus shouldn't flag the message
x: Union[str, int] = 42
y: Union[str, int]
X: Union[str, int] = 42
Y: Union[str, int]
# But the following, using a good TypeAlias name, is:
GoodTypeAliasToUnion: TypeAlias = Union[str, int]

Expand Down
2 changes: 1 addition & 1 deletion tests/functional/u/unsubscriptable_value_py37.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ def __class_getitem__(cls, params):
Subscriptable[0]
Subscriptable()[0] # [unsubscriptable-object]

a: typing.List[int]
A: typing.List[int]
2 changes: 1 addition & 1 deletion tests/functional/u/unused/unused_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class SomeClass:
import xml


example: t.Annotated[str, "Path"] = "/foo/bar"
EXAMPLE: t.Annotated[str, "Path"] = "/foo/bar"

def get_ordered_dict() -> "collections.OrderedDict":
return []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import NoReturn, Set

# unused-import shouldn't be emitted for Path
example1: Set["Path"] = set()
EXAMPLE1: Set["Path"] = set()

def example2(_: "ArgumentParser") -> "NoReturn":
"""unused-import shouldn't be emitted for ArgumentParser or NoReturn."""
Expand All @@ -22,9 +22,9 @@ def example4(_: "PathLike[str]") -> None:
"""unused-import shouldn't be emitted for PathLike."""

# pylint shouldn't crash with the following strings in a type annotation context
example5: Set[""]
example6: Set[" "]
example7: Set["?"]
EXAMPLE5: Set[""]
EXAMPLE6: Set[" "]
EXAMPLE7: Set["?"]

class Class:
"""unused-import shouldn't be emitted for Namespace"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import Literal as Lit

# str inside Literal shouldn't be treated as names
example1: t.Literal["ArgumentParser", Lit["Namespace", "ArgumentParser"]]
EXAMPLE1: t.Literal["ArgumentParser", Lit["Namespace", "ArgumentParser"]]


def unused_variable_example():
Expand All @@ -19,9 +19,9 @@ def unused_variable_example():


# pylint shouldn't crash with the following strings in a type annotation context
example3: Lit["", " ", "?"] = "?"
EXAMPLE3: Lit["", " ", "?"] = "?"


# See https://peps.python.org/pep-0586/#literals-enums-and-forward-references
example4: t.Literal["http.HTTPStatus.OK", "http.HTTPStatus.NOT_FOUND"]
example5: "t.Literal[HTTPStatus.OK, HTTPStatus.NOT_FOUND]"
EXAMPLE4: t.Literal["http.HTTPStatus.OK", "http.HTTPStatus.NOT_FOUND"]
EXAMPLE5: "t.Literal[HTTPStatus.OK, HTTPStatus.NOT_FOUND]"
4 changes: 2 additions & 2 deletions tests/input/func_noerror_cycle/a.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# pylint: disable=missing-docstring
from typing import List

from .b import var
from .b import VAR

LstT = List[int]

print(var)
print(VAR)
2 changes: 1 addition & 1 deletion tests/input/func_noerror_cycle/b.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
if TYPE_CHECKING:
from .a import LstT

var: "LstT" = [1, 2]
VAR: "LstT" = [1, 2]

0 comments on commit 692895e

Please sign in to comment.