Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix FN for invalid-name for type-annotated module constants #9771

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]
Loading