Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

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/10711.false_positive
Original file line number Diff line number Diff line change
@@ -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
4 changes: 3 additions & 1 deletion pylint/checkers/base/name_checker/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should not raise [invalid-name], because Final attributes can still be re-assigned a new value that is different from the default value in __init__(). Final only protects the value after the instance is created.

Final is like const in C/C++. UPPER_CASE is like the marco which gets expanded during static interpretation of the code.

So UPPER_CASE is more immutable than Final.

So marking a dataclass attribute Final is not the sufficient condition for UPPER_CASE.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss that somewhere else. I don't want to make Final more special than it already is. The only thing we grok about Final is ...

CLASS_UPPER_CASE_CONSTANT: Final[int] = 42

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should be OK. Stacking them together should not raise any [invalid-name].

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... right here. (I agree with you)

Everything else in this file should be like Final doesn't even exist.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest adding a just UPPER_CASE attribute test case like:

CLASS_UPPER_CASE_ATTRIBUTE: int = 42

This line should not raise [invalid-name] here, because UPPER_CASE can be used alone to identify a static constant.

But later on if it gets re-assigned new value in the below method, then [invalid-name] should be raised there to ask for snake_case because it is no longer a constant.


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)
Original file line number Diff line number Diff line change
@@ -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