diff --git a/doc/data/messages/i/invalid-name/details.rst b/doc/data/messages/i/invalid-name/details.rst index 7e2090158a..14cfe6e592 100644 --- a/doc/data/messages/i/invalid-name/details.rst +++ b/doc/data/messages/i/invalid-name/details.rst @@ -80,7 +80,7 @@ Pylint provides predefined naming patterns for some names. These patterns are of based on a Naming Style but there is no option to choose one of the styles mentioned above. The pattern can be overwritten with the options discussed below. -The following type of names are checked with a predefined pattern: +The following types of names are checked with a predefined pattern: +--------------------+-------------------------------------------------------+------------------------------------------------------------+ | Name type | Good names | Bad names | @@ -95,6 +95,13 @@ The following type of names are checked with a predefined pattern: | | ``TopName`` is allowed but ``TTopName`` isn't. | | +--------------------+-------------------------------------------------------+------------------------------------------------------------+ +Before pylint 3.0, most predefined patterns also enforced a minimum length +of three characters. If this behavior is desired in versions 3.0 and following, +it can be had by providing custom regular expressions as described next. (Or, +if the ``disallowed-name`` check is sufficient instead of ``invalid-name``, +providing the single option ``bad-names-rgxs="^..?$"`` will suffice to fail 1-2 +character names. + Custom regular expressions ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/whatsnew/fragments/2018.user_action b/doc/whatsnew/fragments/2018.user_action new file mode 100644 index 0000000000..5e33c8388c --- /dev/null +++ b/doc/whatsnew/fragments/2018.user_action @@ -0,0 +1,20 @@ +The ``invalid-name`` message no longer checks for a minimum length of 3 characters by default. +(This was an unadvertised commingling of concerns between casing +and name length, and users regularly reported this to be surprising.) + +If checking for a minimum length is still desired, it can be regained in two ways: + +- If you are content with a ``disallowed-name`` message (instead of ``invalid-name``), +then simply add the option ``bad-names-rgxs="^..?$"``, which will fail 1-2 +character-long names. (Ensure you enable ``disallowed-name``.) + +- If you would prefer an ``invalid-name`` message to be emitted, or would prefer +finer-grained control over the circumstances in which messages are emitted +(classes vs. methods, etc.), then avail yourself of the regex options described +`here `_. +(In particular, take note of the commented out options in the "example configuration" given at +the bottom of the section.) The prior regexes can be found in the +`pull request `_ +that removed the length requirements. + +Closes #2018 diff --git a/pylint/checkers/base/name_checker/naming_style.py b/pylint/checkers/base/name_checker/naming_style.py index 6410f91813..0198ae7d1e 100644 --- a/pylint/checkers/base/name_checker/naming_style.py +++ b/pylint/checkers/base/name_checker/naming_style.py @@ -47,47 +47,47 @@ def get_regex(cls, name_type: str) -> Pattern[str]: class SnakeCaseStyle(NamingStyle): """Regex rules for snake_case naming style.""" - CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]+$") + CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]*$") MOD_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]*$") CONST_NAME_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]*|__.*__)$") COMP_VAR_RGX = CLASS_NAME_RGX DEFAULT_NAME_RGX = re.compile( - r"([^\W\dA-Z][^\WA-Z]{2,}|_[^\WA-Z]*|__[^\WA-Z\d_][^\WA-Z]+__)$" + r"([^\W\dA-Z][^\WA-Z]*|_[^\WA-Z]*|__[^\WA-Z\d_][^\WA-Z]+__)$" ) - CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]{2,}|__.*__)$") + CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]*|__.*__)$") class CamelCaseStyle(NamingStyle): """Regex rules for camelCase naming style.""" - CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]+$") + CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]*$") MOD_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]*$") CONST_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]*|__.*__)$") COMP_VAR_RGX = MOD_NAME_RGX - DEFAULT_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]{2,}|__[^\W\dA-Z_]\w+__)$") - CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\W_]{2,}|__.*__)$") + DEFAULT_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]*|__[^\W\dA-Z_]\w+__)$") + CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\W_]*|__.*__)$") class PascalCaseStyle(NamingStyle): """Regex rules for PascalCase naming style.""" - CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\W_]+$") + CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\W_]*$") MOD_NAME_RGX = CLASS_NAME_RGX CONST_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]*|__.*__)$") COMP_VAR_RGX = CLASS_NAME_RGX - DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]{2,}|__[^\W\dA-Z_]\w+__)$") - CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\W_]{2,}$") + DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]*|__[^\W\dA-Z_]\w+__)$") + CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\W_]*$") class UpperCaseStyle(NamingStyle): """Regex rules for UPPER_CASE naming style.""" - CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\Wa-z]+$") + CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\Wa-z]*$") MOD_NAME_RGX = CLASS_NAME_RGX CONST_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]*|__.*__)$") COMP_VAR_RGX = CLASS_NAME_RGX - DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]{2,}|__[^\W\dA-Z_]\w+__)$") - CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\Wa-z]{2,}$") + DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]*|__[^\W\dA-Z_]\w+__)$") + CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\Wa-z]*$") class AnyStyle(NamingStyle): diff --git a/tests/functional/d/disable_msg_next_line.py b/tests/functional/d/disable_msg_next_line.py index ea283a4276..961dcfa77b 100644 --- a/tests/functional/d/disable_msg_next_line.py +++ b/tests/functional/d/disable_msg_next_line.py @@ -12,7 +12,7 @@ def function_B(arg1, arg2): # pylint: disable-next=invalid-name, f-string-without-interpolation def function_C(): - x = "string" # [unused-variable, invalid-name] + X = "string" # [unused-variable, invalid-name] return f"This should be a normal string" # [f-string-without-interpolation] diff --git a/tests/functional/d/disable_msg_next_line.txt b/tests/functional/d/disable_msg_next_line.txt index 794cfbb98d..de491f5f47 100644 --- a/tests/functional/d/disable_msg_next_line.txt +++ b/tests/functional/d/disable_msg_next_line.txt @@ -1,5 +1,5 @@ -invalid-name:15:4:15:5:function_C:"Variable name ""x"" doesn't conform to snake_case naming style":HIGH -unused-variable:15:4:15:5:function_C:Unused variable 'x':UNDEFINED +invalid-name:15:4:15:5:function_C:"Variable name ""X"" doesn't conform to snake_case naming style":HIGH +unused-variable:15:4:15:5:function_C:Unused variable 'X':UNDEFINED f-string-without-interpolation:16:11:16:44:function_C:Using an f-string that does not have any interpolated variables:UNDEFINED invalid-name:19:0:19:14:function_D:"Function name ""function_D"" doesn't conform to snake_case naming style":HIGH unused-argument:19:21:19:25:function_D:Unused argument 'arg2':HIGH diff --git a/tests/functional/i/invalid/invalid_name.py b/tests/functional/i/invalid/invalid_name.py index 201d5bf9f3..66ad1e77cd 100644 --- a/tests/functional/i/invalid/invalid_name.py +++ b/tests/functional/i/invalid/invalid_name.py @@ -29,8 +29,12 @@ def test(): re = None return re -def a(): # [invalid-name] - """yo""" +def a(): + """We no longer fail 1-character names by default.""" + + +def A(): # [invalid-name] + """But we do check casing.""" def _generate_cmdline_tests(): diff --git a/tests/functional/i/invalid/invalid_name.txt b/tests/functional/i/invalid/invalid_name.txt index 12199a97e4..72f8fcd906 100644 --- a/tests/functional/i/invalid/invalid_name.txt +++ b/tests/functional/i/invalid/invalid_name.txt @@ -1,8 +1,8 @@ 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:32:0:32:5:a:"Function name ""a"" doesn't conform to snake_case naming style":HIGH -invalid-name:46:4:46:13::"Constant name ""Foocapfor"" doesn't conform to UPPER_CASE naming style":HIGH -invalid-name:62:0:62: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:70:23:70:29:FooBar.__init__:"Argument name ""fooBar"" doesn't conform to snake_case naming style":HIGH -invalid-name:76:8:76:14:FooBar.func1:"Argument name ""fooBar"" doesn't conform to snake_case naming style":HIGH -invalid-name:96:8:96:15:FooBar.test_disable_mixed:"Argument name ""fooBar2"" doesn't conform to snake_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 diff --git a/tests/functional/i/invalid/invalid_name/invalid_name_property.py b/tests/functional/i/invalid/invalid_name/invalid_name_property.py index 9de2378bdc..1e578c3383 100644 --- a/tests/functional/i/invalid/invalid_name/invalid_name_property.py +++ b/tests/functional/i/invalid/invalid_name/invalid_name_property.py @@ -6,8 +6,8 @@ import abc -def custom_prop(f): # [invalid-name] - return property(f) +def custom_prop(F): # [invalid-name] + return property(F) class FooClass: diff --git a/tests/functional/i/invalid/invalid_name/invalid_name_property.txt b/tests/functional/i/invalid/invalid_name/invalid_name_property.txt index 84d804ce04..ce3e9660c9 100644 --- a/tests/functional/i/invalid/invalid_name/invalid_name_property.txt +++ b/tests/functional/i/invalid/invalid_name/invalid_name_property.txt @@ -1,3 +1,3 @@ -invalid-name:9:16:9:17:custom_prop:"Argument name ""f"" doesn't conform to snake_case naming style":HIGH +invalid-name:9:16:9:17:custom_prop:"Argument name ""F"" doesn't conform to snake_case naming style":HIGH invalid-name:21:4:21:11:FooClass.bar:"Attribute name ""bar"" doesn't conform to '[A-Z]+' pattern":INFERENCE invalid-name:37:4:37:11:AnotherFooClass.foo:"Attribute name ""foo"" doesn't conform to '[A-Z]+' pattern":INFERENCE diff --git a/tests/functional/n/name/name_preset_snake_case.txt b/tests/functional/n/name/name_preset_snake_case.txt index 339f27bd8a..5c03f0eb91 100644 --- a/tests/functional/n/name/name_preset_snake_case.txt +++ b/tests/functional/n/name/name_preset_snake_case.txt @@ -1,5 +1,5 @@ invalid-name:6:0:6:13::"Constant name ""SOME_CONSTANT"" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]*|__.*__)$' pattern)":HIGH -invalid-name:13:0:13:13:MyClass:"Class name ""MyClass"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]+$' pattern)":HIGH -invalid-name:25:0:25:12: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)":HIGH -invalid-name:29:0:29:13:FooEnum:"Class name ""FooEnum"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]+$' pattern)":HIGH -invalid-name:34:0:34:9:Bar:"Class name ""Bar"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]+$' pattern)":HIGH +invalid-name:13:0:13:13:MyClass:"Class name ""MyClass"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]*$' pattern)":HIGH +invalid-name:25:0:25:12:sayHello:"Function name ""sayHello"" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]*|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)":HIGH +invalid-name:29:0:29:13:FooEnum:"Class name ""FooEnum"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]*$' pattern)":HIGH +invalid-name:34:0:34:9:Bar:"Class name ""Bar"" doesn't conform to snake_case naming style ('[^\\W\\dA-Z][^\\WA-Z]*$' pattern)":HIGH diff --git a/tests/functional/n/namePresetCamelCase.txt b/tests/functional/n/namePresetCamelCase.txt index 9461c6a536..900f7dceb9 100644 --- a/tests/functional/n/namePresetCamelCase.txt +++ b/tests/functional/n/namePresetCamelCase.txt @@ -1,3 +1,3 @@ invalid-name:3:0:3:13::"Constant name ""SOME_CONSTANT"" doesn't conform to camelCase naming style ('([^\\W\\dA-Z][^\\W_]*|__.*__)$' pattern)":HIGH -invalid-name:10:0:10:13:MyClass:"Class name ""MyClass"" doesn't conform to camelCase naming style ('[^\\W\\dA-Z][^\\W_]+$' pattern)":HIGH -invalid-name:22:0:22:13:say_hello:"Function name ""say_hello"" doesn't conform to camelCase naming style ('([^\\W\\dA-Z][^\\W_]{2,}|__[^\\W\\dA-Z_]\\w+__)$' pattern)":HIGH +invalid-name:10:0:10:13:MyClass:"Class name ""MyClass"" doesn't conform to camelCase naming style ('[^\\W\\dA-Z][^\\W_]*$' pattern)":HIGH +invalid-name:22:0:22:13:say_hello:"Function name ""say_hello"" doesn't conform to camelCase naming style ('([^\\W\\dA-Z][^\\W_]*|__[^\\W\\dA-Z_]\\w+__)$' pattern)":HIGH diff --git a/tests/functional/r/regression/regression_4723.py b/tests/functional/r/regression/regression_4723.py index 012825db07..51c1f7d5df 100644 --- a/tests/functional/r/regression/regression_4723.py +++ b/tests/functional/r/regression/regression_4723.py @@ -18,4 +18,4 @@ def play(): # [no-method-argument] def func(): with B().get() as b: - b.play() + b.play() # [too-many-function-args] diff --git a/tests/functional/r/regression/regression_4723.txt b/tests/functional/r/regression/regression_4723.txt index f64667e722..4f19140899 100644 --- a/tests/functional/r/regression/regression_4723.txt +++ b/tests/functional/r/regression/regression_4723.txt @@ -1 +1,2 @@ no-method-argument:15:4:15:12:B.play:Method 'play' has no argument:UNDEFINED +too-many-function-args:21:8:21:16:func:Too many positional arguments for method call:UNDEFINED