diff --git a/doc/whatsnew/fragments/10711.false_positive b/doc/whatsnew/fragments/10711.false_positive new file mode 100644 index 0000000000..af4b822638 --- /dev/null +++ b/doc/whatsnew/fragments/10711.false_positive @@ -0,0 +1,4 @@ +Fix a false positive when an UPPER_CASED class attribute was raising an +``invalid-name`` when typed with ``Final``. + +Closes #10711 diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py index d282ab5eda..4530e78822 100644 --- a/pylint/checkers/base/name_checker/checker.py +++ b/pylint/checkers/base/name_checker/checker.py @@ -373,7 +373,9 @@ def leave_module(self, _: nodes.Module) -> None: def visit_classdef(self, node: nodes.ClassDef) -> None: self._check_name("class", node.name, node) for attr, anodes in node.instance_attrs.items(): - if not any(node.instance_attr_ancestors(attr)): + if not any( + node.instance_attr_ancestors(attr) + ) and not utils.is_assign_name_annotated_with(anodes[0], "Final"): self._check_name("attr", attr, anodes[0]) @utils.only_required_for_messages("disallowed-name", "invalid-name") diff --git a/tests/functional/i/invalid/invalid_name/invalid_name_with_final_typing.py b/tests/functional/i/invalid/invalid_name/invalid_name_with_final_typing.py new file mode 100644 index 0000000000..3d32d8a2b4 --- /dev/null +++ b/tests/functional/i/invalid/invalid_name/invalid_name_with_final_typing.py @@ -0,0 +1,26 @@ +"""Regression test for https://github.com/pylint-dev/pylint/issues/10711.""" + +# pylint: disable=missing-class-docstring, missing-function-docstring + +from dataclasses import dataclass +from typing import Final + +module_snake_case_constant: Final[int] = 42 # [invalid-name] +MODULE_UPPER_CASE_CONSTANT: Final[int] = 42 + + +def function() -> None: + function_snake_case_constant: Final[int] = 42 + FUNCTION_UPPER_CASE_CONSTANT: Final[int] = 42 # [invalid-name] + print(function_snake_case_constant, FUNCTION_UPPER_CASE_CONSTANT) + + +@dataclass +class Class: + class_snake_case_constant: Final[int] = 42 # [invalid-name] + CLASS_UPPER_CASE_CONSTANT: Final[int] = 42 + + def method(self) -> None: + method_snake_case_constant: Final[int] = 42 + METHOD_UPPER_CASE_CONSTANT: Final[int] = 42 # [invalid-name] + print(method_snake_case_constant, METHOD_UPPER_CASE_CONSTANT) diff --git a/tests/functional/i/invalid/invalid_name/invalid_name_with_final_typing.txt b/tests/functional/i/invalid/invalid_name/invalid_name_with_final_typing.txt new file mode 100644 index 0000000000..595b35224c --- /dev/null +++ b/tests/functional/i/invalid/invalid_name/invalid_name_with_final_typing.txt @@ -0,0 +1,4 @@ +invalid-name:8:0:8:26::"Constant name ""module_snake_case_constant"" doesn't conform to UPPER_CASE naming style":HIGH +invalid-name:14:4:14:32:function:"Variable name ""FUNCTION_UPPER_CASE_CONSTANT"" doesn't conform to snake_case naming style":HIGH +invalid-name:20:4:20:29:Class:"Class constant name ""class_snake_case_constant"" doesn't conform to UPPER_CASE naming style":HIGH +invalid-name:25:8:25:34:Class.method:"Variable name ""METHOD_UPPER_CASE_CONSTANT"" doesn't conform to snake_case naming style":HIGH