summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Mueller <30130371+cdce8p@users.noreply.github.com>2021-03-05 03:20:07 +0100
committerPierre Sassoulas <pierre.sassoulas@gmail.com>2021-03-06 16:21:18 +0100
commit947ee47c87d34abcd8982a61c12888e26111175d (patch)
tree9ca4530f358be0f17c0f4c278c3883bcd396e725
parent803a2522639a2cd93f889885b1cbfb47642477b3 (diff)
downloadpylint-git-947ee47c87d34abcd8982a61c12888e26111175d.tar.gz
Add --class-const-naming-style
-rw-r--r--ChangeLog5
-rw-r--r--doc/user_guide/options.rst6
-rw-r--r--pylint/checkers/base.py19
-rw-r--r--tests/functional/n/name_preset_snake_case.py5
-rw-r--r--tests/functional/n/name_preset_snake_case.rc1
-rw-r--r--tests/functional/n/name_preset_snake_case.txt9
-rw-r--r--tests/functional/n/name_styles.py7
-rw-r--r--tests/functional/n/name_styles.txt35
8 files changed, 64 insertions, 23 deletions
diff --git a/ChangeLog b/ChangeLog
index 6bb635efb..4211c8814 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -37,6 +37,11 @@ Release date: TBA
Closes #3167
+* Add ``--class-const-naming-style`` for Enum constants and class variables annotated
+ with ``typing.ClassVar``
+
+ Closes #4181
+
What's New in Pylint 2.7.2?
===========================
diff --git a/doc/user_guide/options.rst b/doc/user_guide/options.rst
index dc464812a..93833d009 100644
--- a/doc/user_guide/options.rst
+++ b/doc/user_guide/options.rst
@@ -35,6 +35,8 @@ name is found in, and not the type of object assigned.
+--------------------+---------------------------------------------------------------------------------------------------+
| ``class-attribute``| Attributes defined in class bodies. |
+--------------------+---------------------------------------------------------------------------------------------------+
+| ``class-const`` | Enum constants and class variables annotated with ``ClassVar`` |
++--------------------+---------------------------------------------------------------------------------------------------+
| ``inlinevar`` | Loop variables in list comprehensions and generator expressions. |
+--------------------+---------------------------------------------------------------------------------------------------+
@@ -76,6 +78,8 @@ Following options are exposed:
.. option:: --class-attribute-naming-style=<style>
+.. option:: --class-const-naming-style=<style>
+
.. option:: --inlinevar-naming-style=<style>
@@ -110,6 +114,8 @@ expression will lead to an instance of ``invalid-name``.
.. option:: --class-attribute-rgx=<regex>
+.. option:: --class-const-rgx=<regex>
+
.. option:: --inlinevar-rgx=<regex>
Multiple naming styles for custom regular expressions
diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py
index 384abb471..33aa8a037 100644
--- a/pylint/checkers/base.py
+++ b/pylint/checkers/base.py
@@ -104,6 +104,7 @@ class NamingStyle:
"argument": cls.DEFAULT_NAME_RGX,
"variable": cls.DEFAULT_NAME_RGX,
"class_attribute": cls.CLASS_ATTRIBUTE_RGX,
+ "class_const": cls.CONST_NAME_RGX,
"inlinevar": cls.COMP_VAR_RGX,
}[name_type]
@@ -1654,6 +1655,7 @@ KNOWN_NAME_TYPES = {
"argument",
"variable",
"class_attribute",
+ "class_const",
"inlinevar",
}
@@ -1667,6 +1669,7 @@ HUMAN_READABLE_TYPES = {
"argument": "argument",
"variable": "variable",
"class_attribute": "class attribute",
+ "class_const": "class constant",
"inlinevar": "inline iteration",
}
@@ -1680,6 +1683,7 @@ DEFAULT_NAMING_STYLES = {
"argument": "snake_case",
"variable": "snake_case",
"class_attribute": "any",
+ "class_const": "UPPER_CASE",
"inlinevar": "any",
}
@@ -1846,6 +1850,7 @@ class NameChecker(_BasicChecker):
badname_inlinevar=0,
badname_argument=0,
badname_class_attribute=0,
+ badname_class_const=0,
)
for group in self.config.name_group:
for name_type in group.split(":"):
@@ -1983,8 +1988,18 @@ class NameChecker(_BasicChecker):
elif isinstance(frame, astroid.ClassDef):
if not list(frame.local_attr_ancestors(node.name)):
for ancestor in frame.ancestors():
- if ancestor.name == "Enum" and ancestor.root().name == "enum":
- self._check_name("const", node.name, node)
+ if ( # pylint: disable=too-many-boolean-expressions
+ ancestor.name == "Enum"
+ and ancestor.root().name == "enum"
+ or isinstance(node.parent, astroid.AnnAssign)
+ and (
+ isinstance(node.parent.annotation, astroid.Subscript)
+ and node.parent.annotation.value.name == "ClassVar"
+ or isinstance(node.parent.annotation, astroid.Name)
+ and node.parent.annotation.name == "ClassVar"
+ )
+ ):
+ self._check_name("class_const", node.name, node)
break
else:
self._check_name("class_attribute", node.name, node)
diff --git a/tests/functional/n/name_preset_snake_case.py b/tests/functional/n/name_preset_snake_case.py
index 892d34081..75350497e 100644
--- a/tests/functional/n/name_preset_snake_case.py
+++ b/tests/functional/n/name_preset_snake_case.py
@@ -1,5 +1,6 @@
# pylint: disable=missing-docstring,too-few-public-methods
from enum import Enum
+from typing import ClassVar
__version__ = "1.0"
SOME_CONSTANT = 42 # [invalid-name]
@@ -28,3 +29,7 @@ def sayHello(): # [invalid-name]
class FooEnum(Enum): # [invalid-name]
const_with_snake_case = 42
another_const = 43
+
+
+class Bar: # [invalid-name]
+ const_with_snake_case: ClassVar[int] = 42
diff --git a/tests/functional/n/name_preset_snake_case.rc b/tests/functional/n/name_preset_snake_case.rc
index 0424cdcfb..2b04af5d4 100644
--- a/tests/functional/n/name_preset_snake_case.rc
+++ b/tests/functional/n/name_preset_snake_case.rc
@@ -7,6 +7,7 @@ module-naming-style=snake_case
method-naming-style=snake_case
variable-naming-style=snake_case
class-attribute-naming-style=snake_case
+class-const-naming-style=snake_case
inlinevar-naming-style=snake_case
class-naming-style=snake_case
diff --git a/tests/functional/n/name_preset_snake_case.txt b/tests/functional/n/name_preset_snake_case.txt
index ffcdd9a66..c189797e9 100644
--- a/tests/functional/n/name_preset_snake_case.txt
+++ b/tests/functional/n/name_preset_snake_case.txt
@@ -1,4 +1,5 @@
-invalid-name:5:0::"Constant name ""SOME_CONSTANT"" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]*|__.*__)$' pattern)"
-invalid-name:12:0:MyClass:"Class name ""MyClass"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]+$' pattern)"
-invalid-name:24:0:sayHello:"Function name ""sayHello"" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]{2,}|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)"
-invalid-name:28:0:FooEnum:"Class name ""FooEnum"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]+$' pattern)"
+invalid-name:6:0::"Constant name ""SOME_CONSTANT"" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]*|__.*__)$' pattern)"
+invalid-name:13:0:MyClass:"Class name ""MyClass"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]+$' pattern)"
+invalid-name:25:0:sayHello:"Function name ""sayHello"" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]{2,}|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)"
+invalid-name:29:0:FooEnum:"Class name ""FooEnum"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]+$' pattern)"
+invalid-name:34:0:Bar:"Class name ""Bar"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]+$' pattern)"
diff --git a/tests/functional/n/name_styles.py b/tests/functional/n/name_styles.py
index ca3572fe4..394c82826 100644
--- a/tests/functional/n/name_styles.py
+++ b/tests/functional/n/name_styles.py
@@ -4,6 +4,7 @@ from __future__ import print_function
import abc
import collections
from enum import Enum
+from typing import ClassVar
GOOD_CONST_NAME = ''
bad_const_name = 0 # [invalid-name]
@@ -147,3 +148,9 @@ class FooEnum(Enum):
"""A test case for enum names."""
GOOD_ENUM_NAME = 1
bad_enum_name = 2 # [invalid-name]
+
+class Bar:
+ """Class with class constants annotated with ClassVar."""
+ CLASS_CONST: ClassVar[int] = 42
+ CLASS_CONST2: ClassVar = "const"
+ variable: ClassVar[str] = "invalid name" # [invalid-name]
diff --git a/tests/functional/n/name_styles.txt b/tests/functional/n/name_styles.txt
index 0785ec65c..ef665c0de 100644
--- a/tests/functional/n/name_styles.txt
+++ b/tests/functional/n/name_styles.txt
@@ -1,17 +1,18 @@
-invalid-name:9:0::"Constant name ""bad_const_name"" doesn't conform to UPPER_CASE naming style"
-invalid-name:12:0:BADFUNCTION_name:"Function name ""BADFUNCTION_name"" doesn't conform to snake_case naming style"
-invalid-name:14:4:BADFUNCTION_name:"Variable name ""BAD_LOCAL_VAR"" doesn't conform to snake_case naming style"
-invalid-name:18:0:func_bad_argname:"Argument name ""NOT_GOOD"" doesn't conform to snake_case naming style"
-invalid-name:28:0:bad_class_name:"Class name ""bad_class_name"" doesn't conform to PascalCase naming style"
-invalid-name:39:8:CorrectClassName.__init__:"Attribute name ""_Bad_AtTR_name"" doesn't conform to snake_case naming style"
-invalid-name:40:8:CorrectClassName.__init__:"Attribute name ""Bad_PUBLIC_name"" doesn't conform to snake_case naming style"
-invalid-name:45:4:CorrectClassName.BadMethodName:"Method name ""BadMethodName"" doesn't conform to snake_case naming style":INFERENCE
-invalid-name:51:4:CorrectClassName.__DunDER_IS_not_free_for_all__:"Method name ""__DunDER_IS_not_free_for_all__"" doesn't conform to snake_case naming style":INFERENCE
-invalid-name:81:0::"Class name ""BAD_NAME_FOR_CLASS"" doesn't conform to PascalCase naming style"
-invalid-name:82:0::"Class name ""NEXT_BAD_NAME_FOR_CLASS"" doesn't conform to PascalCase naming style"
-invalid-name:89:0::"Class name ""NOT_CORRECT"" doesn't conform to PascalCase naming style"
-invalid-name:95:4:test_globals:"Constant name ""AlsoCorrect"" doesn't conform to UPPER_CASE naming style"
-invalid-name:108:4:FooClass.PROPERTY_NAME:"Attribute name ""PROPERTY_NAME"" doesn't conform to snake_case naming style":INFERENCE
-invalid-name:113:4:FooClass.ABSTRACT_PROPERTY_NAME:"Attribute name ""ABSTRACT_PROPERTY_NAME"" doesn't conform to snake_case naming style":INFERENCE
-invalid-name:118:4:FooClass.PROPERTY_NAME_SETTER:"Attribute name ""PROPERTY_NAME_SETTER"" doesn't conform to snake_case naming style":INFERENCE
-invalid-name:149:4:FooEnum:"Constant name ""bad_enum_name"" doesn't conform to UPPER_CASE naming style"
+invalid-name:10:0::"Constant name ""bad_const_name"" doesn't conform to UPPER_CASE naming style"
+invalid-name:13:0:BADFUNCTION_name:"Function name ""BADFUNCTION_name"" doesn't conform to snake_case naming style"
+invalid-name:15:4:BADFUNCTION_name:"Variable name ""BAD_LOCAL_VAR"" doesn't conform to snake_case naming style"
+invalid-name:19:0:func_bad_argname:"Argument name ""NOT_GOOD"" doesn't conform to snake_case naming style"
+invalid-name:29:0:bad_class_name:"Class name ""bad_class_name"" doesn't conform to PascalCase naming style"
+invalid-name:40:8:CorrectClassName.__init__:"Attribute name ""_Bad_AtTR_name"" doesn't conform to snake_case naming style"
+invalid-name:41:8:CorrectClassName.__init__:"Attribute name ""Bad_PUBLIC_name"" doesn't conform to snake_case naming style"
+invalid-name:46:4:CorrectClassName.BadMethodName:"Method name ""BadMethodName"" doesn't conform to snake_case naming style":INFERENCE
+invalid-name:52:4:CorrectClassName.__DunDER_IS_not_free_for_all__:"Method name ""__DunDER_IS_not_free_for_all__"" doesn't conform to snake_case naming style":INFERENCE
+invalid-name:82:0::"Class name ""BAD_NAME_FOR_CLASS"" doesn't conform to PascalCase naming style"
+invalid-name:83:0::"Class name ""NEXT_BAD_NAME_FOR_CLASS"" doesn't conform to PascalCase naming style"
+invalid-name:90:0::"Class name ""NOT_CORRECT"" doesn't conform to PascalCase naming style"
+invalid-name:96:4:test_globals:"Constant name ""AlsoCorrect"" doesn't conform to UPPER_CASE naming style"
+invalid-name:109:4:FooClass.PROPERTY_NAME:"Attribute name ""PROPERTY_NAME"" doesn't conform to snake_case naming style":INFERENCE
+invalid-name:114:4:FooClass.ABSTRACT_PROPERTY_NAME:"Attribute name ""ABSTRACT_PROPERTY_NAME"" doesn't conform to snake_case naming style":INFERENCE
+invalid-name:119:4:FooClass.PROPERTY_NAME_SETTER:"Attribute name ""PROPERTY_NAME_SETTER"" doesn't conform to snake_case naming style":INFERENCE
+invalid-name:150:4:FooEnum:"Class constant name ""bad_enum_name"" doesn't conform to UPPER_CASE naming style"
+invalid-name:156:4:Bar:"Class constant name ""variable"" doesn't conform to UPPER_CASE naming style"