diff --git a/doc/whatsnew/fragments/9770.false_negative b/doc/whatsnew/fragments/9770.false_negative new file mode 100644 index 0000000000..a6df92e631 --- /dev/null +++ b/doc/whatsnew/fragments/9770.false_negative @@ -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 diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py index 1d8589a576..098fb07cff 100644 --- a/pylint/checkers/base/name_checker/checker.py +++ b/pylint/checkers/base/name_checker/checker.py @@ -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): diff --git a/tests/functional/ext/private_import/private_import.py b/tests/functional/ext/private_import/private_import.py index a73e69e758..14a3fcd328 100644 --- a/tests/functional/ext/private_import/private_import.py +++ b/tests/functional/ext/private_import/private_import.py @@ -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] diff --git a/tests/functional/ext/private_import/private_import.rc b/tests/functional/ext/private_import/private_import.rc index 5920812a5c..c24cd6520c 100644 --- a/tests/functional/ext/private_import/private_import.rc +++ b/tests/functional/ext/private_import/private_import.rc @@ -1,2 +1,3 @@ [MAIN] load-plugins=pylint.extensions.private_import, +const-naming-style=snake_case diff --git a/tests/functional/f/function_redefined.py b/tests/functional/f/function_redefined.py index a87c79680a..b6e6b52499 100644 --- a/tests/functional/f/function_redefined.py +++ b/tests/functional/f/function_redefined.py @@ -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 diff --git a/tests/functional/g/generic_alias/generic_alias_collections.py b/tests/functional/g/generic_alias/generic_alias_collections.py index 33f6b6e2c3..78ba4cbb3e 100644 --- a/tests/functional/g/generic_alias/generic_alias_collections.py +++ b/tests/functional/g/generic_alias/generic_alias_collections.py @@ -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 @@ -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] diff --git a/tests/functional/g/generic_alias/generic_alias_mixed_py39.py b/tests/functional/g/generic_alias/generic_alias_mixed_py39.py index 9c6e63e661..2d491e0a29 100644 --- a/tests/functional/g/generic_alias/generic_alias_mixed_py39.py +++ b/tests/functional/g/generic_alias/generic_alias_mixed_py39.py @@ -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 @@ -9,7 +9,7 @@ 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] @@ -17,7 +17,7 @@ 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' diff --git a/tests/functional/i/invalid/invalid_name.py b/tests/functional/i/invalid/invalid_name.py index 28638bcb4c..94fd3240e6 100644 --- a/tests/functional/i/invalid/invalid_name.py +++ b/tests/functional/i/invalid/invalid_name.py @@ -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: diff --git a/tests/functional/i/invalid/invalid_name.txt b/tests/functional/i/invalid/invalid_name.txt index e8622f864a..5e6f4f4f84 100644 --- a/tests/functional/i/invalid/invalid_name.txt +++ b/tests/functional/i/invalid/invalid_name.txt @@ -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 diff --git a/tests/functional/r/regression_02/regression_3979.py b/tests/functional/r/regression_02/regression_3979.py index e14c5edb53..5e29ffbdca 100644 --- a/tests/functional/r/regression_02/regression_3979.py +++ b/tests/functional/r/regression_02/regression_3979.py @@ -10,4 +10,4 @@ else: BasePathLike = os.PathLike -Foo: Union[str, BasePathLike] = "bar" +SOME_VAR: Union[str, BasePathLike] = "bar" diff --git a/tests/functional/t/type/typealias_naming_style_default.py b/tests/functional/t/type/typealias_naming_style_default.py index 6a2c81569b..2da8210f24 100644 --- a/tests/functional/t/type/typealias_naming_style_default.py +++ b/tests/functional/t/type/typealias_naming_style_default.py @@ -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] diff --git a/tests/functional/u/unsubscriptable_value_py37.py b/tests/functional/u/unsubscriptable_value_py37.py index acbbe6bdd8..25fcdc4cd7 100644 --- a/tests/functional/u/unsubscriptable_value_py37.py +++ b/tests/functional/u/unsubscriptable_value_py37.py @@ -14,4 +14,4 @@ def __class_getitem__(cls, params): Subscriptable[0] Subscriptable()[0] # [unsubscriptable-object] -a: typing.List[int] +A: typing.List[int] diff --git a/tests/functional/u/unused/unused_import.py b/tests/functional/u/unused/unused_import.py index a2e3ceca3f..7bd8b57fc4 100644 --- a/tests/functional/u/unused/unused_import.py +++ b/tests/functional/u/unused/unused_import.py @@ -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 [] diff --git a/tests/functional/u/unused/unused_name_in_string_literal_type_annotation.py b/tests/functional/u/unused/unused_name_in_string_literal_type_annotation.py index 400e7725e0..ae86c6c4e1 100644 --- a/tests/functional/u/unused/unused_name_in_string_literal_type_annotation.py +++ b/tests/functional/u/unused/unused_name_in_string_literal_type_annotation.py @@ -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.""" @@ -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""" diff --git a/tests/functional/u/unused/unused_name_in_string_literal_type_annotation_py38.py b/tests/functional/u/unused/unused_name_in_string_literal_type_annotation_py38.py index 96658ae369..42d1e8c523 100644 --- a/tests/functional/u/unused/unused_name_in_string_literal_type_annotation_py38.py +++ b/tests/functional/u/unused/unused_name_in_string_literal_type_annotation_py38.py @@ -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(): @@ -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]" diff --git a/tests/input/func_noerror_cycle/a.py b/tests/input/func_noerror_cycle/a.py index 85bd49e0ca..cb6eee7b5a 100644 --- a/tests/input/func_noerror_cycle/a.py +++ b/tests/input/func_noerror_cycle/a.py @@ -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) diff --git a/tests/input/func_noerror_cycle/b.py b/tests/input/func_noerror_cycle/b.py index df17057fa4..a36e4be272 100644 --- a/tests/input/func_noerror_cycle/b.py +++ b/tests/input/func_noerror_cycle/b.py @@ -4,4 +4,4 @@ if TYPE_CHECKING: from .a import LstT -var: "LstT" = [1, 2] +VAR: "LstT" = [1, 2]