From ac956189b98faba7be0f995817b0ecce3e406bc3 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 8 Nov 2025 09:15:44 +0100 Subject: [PATCH] Fix a false positive for class attribute typed with Final (#10712) Co-authored-by: Jacob Walls (cherry picked from commit 133681e8741d2bb80d0d6e5eb18cd2725e6fe70b) --- doc/whatsnew/fragments/10711.false_positive | 4 +++ pylint/checkers/base/name_checker/checker.py | 4 ++- .../invalid_name_with_final_typing.py | 26 +++++++++++++++++++ .../invalid_name_with_final_typing.txt | 4 +++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 doc/whatsnew/fragments/10711.false_positive create mode 100644 tests/functional/i/invalid/invalid_name/invalid_name_with_final_typing.py create mode 100644 tests/functional/i/invalid/invalid_name/invalid_name_with_final_typing.txt 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