Repository: pylint-dev/pylint

Issue:
[feature request] New warning: too many positional arguments
### Current problem

With R0913 too-many-arguments now including keyword arguments (#8667), the reasonable setting for `max-args` is now larger, since it's not really a problem to have a dozen keyword-only arguments.

However, having a dozen positional arguments remains a poor design.

### Desired solution

A new warning for too many positional arguments, with a separately configurable maximum. This would warn when there are more than a very small number of positional arguments (including positional-only arguments).

This would be in addition to the existing too-many-arguments warning.

### Additional context

This has been previously suggested in comments on #8667


Discussion:
+1
As for specification would this proposed checker be for emitting a message where a function or class is called? (as opposed to `too-many-arguments` which is emitted for the function or class _definition_. It sounds similar to the existing https://github.com/pylint-dev/pylint/issues/385.


As I'm hearing the request, it's just on the definition (and that's what I would suggest, too).
Yeah, I intended this to be on the definition, just like the too-many-arguments warning (but excluding keyword-only arguments from the count and with a separate maximum)
I think the idea behind `too-many-arguments` is that, regardless of the argument being positional or keyword or positional-only (etc.), the function is complex by having too many of them (subjective I know :D) and doing too many things. At definition time this is possible because we have all the information we need (the number of params).

Would the new checker raise for this situation (where the function call has explicitly passed the names of the parameters)?
```python
def tomato(one, two, three, four, five, six):
    ...

tomato(one=1, two=2, three=3, four=4, five=5, six=6)
```

Perhaps if it were to raise for positional-only, then raising at definition time is ok:
```python
def tomato(one, two, three, four, five, six, /):
    ...

tomato(1, 2, 3, 4, 5, 6)
```
Yeah; the idea was that the new checker would raise for:
```python3
def tomato(one, two, three, four, five, six):
    ...
```
and also raise for:
```python3
def tomato(one, two, three, four, five, six, /):
    ...
```

It would be OK with
```python3
def tomato(one, two, *, three, four, five, six):
    ...
```
and OK with
```python3
def tomato(*, one, two, three, four, five, six):
    ...
```

In any case, I was thinking of definition time; the design of the function encourages calls with a confusing undifferentiated argument list, regardless of whether or not it's called that way (or called at all).

Call time would be more of a variant of #385, function actually being called with a confusing undifferentiated argument list, which is not what I was thinking of here...
I think https://github.com/pylint-dev/pylint/issues/8667 was a mistake. “too many total arguments” could be a separate rule, but it’s much less useful than restricting the number of positional arguments.
I'm happy that 8667 sparked the conversation and that ambiguity will be removed with the introduction of the new checker.
I still feel that the spirit of [too-many-arguments](https://pylint.pycqa.org/en/stable/user_guide/messages/refactor/too-many-arguments.html) was to draw attention to a function that accepts a large number of arguments (irrespective of type) and to consider a re-design/refactor.
Unfortunately, IMHO, significantly limiting number of keyword-only arguments only encourages bad practices, like:

- using `**kwargs` or a dict argument and losing useful checks for typos in args, required args, arg type checking with mypy etc;
- replacing arguments with a `dataclasses.dataclass` or `typing.NamedTuple`, but then soon be forced to disable `too-many-instance-attributes`;
- over-engineering with some special classes like `SomeFunctionArgumentGenerator` - no way it is simpler and less error prone.

There are other checks in pylint that discourage too complicated code, like too-many-branches and too-many-locals etc. which should be enough.

With no obvious refactoring path for reasonable number of positional-only arguments, the change in #8667 made a useful check into a forced bunch of `pylint: disable` comments. Please revert #8667 until too-many-positional with a reasonable default is added (maybe 15, like too-many-locals).
far too late to go with your suggestion of reverting this, but a new code for it has been reserved: https://pylint.pycqa.org/en/latest/user_guide/messages/refactor/too-many-positional.html

you could either implement it here or switch to / add Ruff to your project



Patch:
diff --git a/doc/data/messages/t/too-many-positional-arguments/bad.py b/doc/data/messages/t/too-many-positional-arguments/bad.py
new file mode 100644
index 0000000000..2a2c5b4cac
--- /dev/null
+++ b/doc/data/messages/t/too-many-positional-arguments/bad.py
@@ -0,0 +1,5 @@
+class FiveArgumentMethods:
+    """The max positional arguments default is 5."""
+
+    def take_five_args(self, a, b, c, d, e):  # [too-many-positional-arguments]
+        pass
diff --git a/doc/data/messages/t/too-many-positional-arguments/details.rst b/doc/data/messages/t/too-many-positional-arguments/details.rst
new file mode 100644
index 0000000000..bed5bfb806
--- /dev/null
+++ b/doc/data/messages/t/too-many-positional-arguments/details.rst
@@ -0,0 +1,10 @@
+Positional arguments work well for cases where the the use cases are
+self-evident, such as unittest's ``assertEqual(first, second, msg=None)``.
+Comprehensibility suffers beyond a handful of arguments, though, so for
+functions that take more inputs, require that additional arguments be
+passed by *keyword only* by preceding them with ``*``:
+
+.. code-block:: python
+
+    def make_noise(self, volume, *, color=noise.PINK, debug=True):
+        ...
diff --git a/doc/data/messages/t/too-many-positional-arguments/good.py b/doc/data/messages/t/too-many-positional-arguments/good.py
new file mode 100644
index 0000000000..ab4a9f2735
--- /dev/null
+++ b/doc/data/messages/t/too-many-positional-arguments/good.py
@@ -0,0 +1,5 @@
+class FiveArgumentMethods:
+    """The max positional arguments default is 5."""
+
+    def take_five_args(self, a, b, c, d, *, e=False):
+        pass
diff --git a/doc/data/messages/t/too-many-positional-arguments/pylintrc b/doc/data/messages/t/too-many-positional-arguments/pylintrc
new file mode 100644
index 0000000000..271f58965b
--- /dev/null
+++ b/doc/data/messages/t/too-many-positional-arguments/pylintrc
@@ -0,0 +1,2 @@
+[MESSAGES CONTROL]
+disable=too-many-arguments
diff --git a/doc/data/messages/t/too-many-positional/related.rst b/doc/data/messages/t/too-many-positional-arguments/related.rst
similarity index 100%
rename from doc/data/messages/t/too-many-positional/related.rst
rename to doc/data/messages/t/too-many-positional-arguments/related.rst
diff --git a/doc/data/messages/t/too-many-positional/details.rst b/doc/data/messages/t/too-many-positional/details.rst
deleted file mode 100644
index b9acd905f0..0000000000
--- a/doc/data/messages/t/too-many-positional/details.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-Reserved message name, not yet implemented.
diff --git a/doc/user_guide/checkers/features.rst b/doc/user_guide/checkers/features.rst
index a6434333f7..e83312b8a3 100644
--- a/doc/user_guide/checkers/features.rst
+++ b/doc/user_guide/checkers/features.rst
@@ -437,10 +437,8 @@ Design checker Messages
   simpler (and so easier to use) class.
 :too-many-locals (R0914): *Too many local variables (%s/%s)*
   Used when a function or method has too many local variables.
-:too-many-positional (R0917): *Too many positional arguments in a function call.*
-  Will be implemented in https://github.com/pylint-
-  dev/pylint/issues/9099,msgid/symbol pair reserved for compatibility with
-  ruff, see https://github.com/astral-sh/ruff/issues/8946.
+:too-many-positional-arguments (R0917): *Too many positional arguments (%s/%s)*
+  Used when a function has too many positional arguments.
 :too-many-public-methods (R0904): *Too many public methods (%s/%s)*
   Used when class has too many public methods, try to reduce this to get a
   simpler (and so easier to use) class.
diff --git a/doc/user_guide/configuration/all-options.rst b/doc/user_guide/configuration/all-options.rst
index eb7d72f2a3..568cdde85c 100644
--- a/doc/user_guide/configuration/all-options.rst
+++ b/doc/user_guide/configuration/all-options.rst
@@ -765,6 +765,13 @@ Standard Checkers
 **Default:**  ``7``
 
 
+--max-positional-arguments
+""""""""""""""""""""""""""
+*Maximum number of positional arguments for function / method.*
+
+**Default:**  ``5``
+
+
 --max-public-methods
 """"""""""""""""""""
 *Maximum number of public methods for a class (see R0904).*
@@ -822,6 +829,8 @@ Standard Checkers
 
    max-parents = 7
 
+   max-positional-arguments = 5
+
    max-public-methods = 20
 
    max-returns = 6
diff --git a/doc/user_guide/messages/messages_overview.rst b/doc/user_guide/messages/messages_overview.rst
index 18570b0fdc..6ad50562f8 100644
--- a/doc/user_guide/messages/messages_overview.rst
+++ b/doc/user_guide/messages/messages_overview.rst
@@ -539,7 +539,7 @@ All messages in the refactor category:
    refactor/too-many-instance-attributes
    refactor/too-many-locals
    refactor/too-many-nested-blocks
-   refactor/too-many-positional
+   refactor/too-many-positional-arguments
    refactor/too-many-public-methods
    refactor/too-many-return-statements
    refactor/too-many-statements
diff --git a/doc/whatsnew/fragments/9099.new_check b/doc/whatsnew/fragments/9099.new_check
new file mode 100644
index 0000000000..4c5744bb54
--- /dev/null
+++ b/doc/whatsnew/fragments/9099.new_check
@@ -0,0 +1,8 @@
+Added `too-many-positional-arguments` to allow distinguishing the configuration for too many
+total arguments (with keyword-only params specified after `*`) from the configuration
+for too many positional-or-keyword or positional-only arguments.
+
+As part of evaluating whether this check makes sense for your project, ensure you
+adjust the value of `--max-positional-arguments`.
+
+Closes #9099
diff --git a/examples/pylintrc b/examples/pylintrc
index 24d91bd4e9..645bf7f441 100644
--- a/examples/pylintrc
+++ b/examples/pylintrc
@@ -319,6 +319,8 @@ max-statements=50
 # Minimum number of public methods for a class (see R0903).
 min-public-methods=2
 
+# Minimum number of public methods for a class (see R0903).
+max-positional-arguments=5
 
 [EXCEPTIONS]
 
diff --git a/examples/pyproject.toml b/examples/pyproject.toml
index 8b3c7b29f7..bae7233081 100644
--- a/examples/pyproject.toml
+++ b/examples/pyproject.toml
@@ -281,6 +281,9 @@ max-statements = 50
 # Minimum number of public methods for a class (see R0903).
 min-public-methods = 2
 
+# Maximum number of positional arguments (see R0917).
+max-positional-arguments = 5
+
 [tool.pylint.exceptions]
 # Exceptions that will emit a warning when caught.
 overgeneral-exceptions = ["builtins.BaseException", "builtins.Exception"]
diff --git a/pylint/checkers/design_analysis.py b/pylint/checkers/design_analysis.py
index 78378e92c9..5c1adbc888 100644
--- a/pylint/checkers/design_analysis.py
+++ b/pylint/checkers/design_analysis.py
@@ -16,6 +16,7 @@
 
 from pylint.checkers import BaseChecker
 from pylint.checkers.utils import is_enum, only_required_for_messages
+from pylint.interfaces import HIGH
 from pylint.typing import MessageDefinitionTuple
 
 if TYPE_CHECKING:
@@ -81,11 +82,9 @@
             "Used when an if statement contains too many boolean expressions.",
         ),
         "R0917": (
-            "Too many positional arguments in a function call.",
-            "too-many-positional",
-            "Will be implemented in https://github.com/pylint-dev/pylint/issues/9099,"
-            "msgid/symbol pair reserved for compatibility with ruff, "
-            "see https://github.com/astral-sh/ruff/issues/8946.",
+            "Too many positional arguments (%s/%s)",
+            "too-many-positional-arguments",
+            "Used when a function has too many positional arguments.",
         ),
     }
 )
@@ -311,6 +310,15 @@ class MisdesignChecker(BaseChecker):
                 "help": "Maximum number of arguments for function / method.",
             },
         ),
+        (
+            "max-positional-arguments",
+            {
+                "default": 5,
+                "type": "int",
+                "metavar": "<int>",
+                "help": "Maximum number of positional arguments for function / method.",
+            },
+        ),
         (
             "max-locals",
             {
@@ -525,6 +533,7 @@ def leave_classdef(self, node: nodes.ClassDef) -> None:
         "too-many-branches",
         "too-many-arguments",
         "too-many-locals",
+        "too-many-positional-arguments",
         "too-many-statements",
         "keyword-arg-before-vararg",
     )
@@ -536,13 +545,20 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None:
         self._returns.append(0)
         # check number of arguments
         args = node.args.args + node.args.posonlyargs + node.args.kwonlyargs
+        pos_args = node.args.args + node.args.posonlyargs
         ignored_argument_names = self.linter.config.ignored_argument_names
         if args is not None:
             ignored_args_num = 0
             if ignored_argument_names:
-                ignored_args_num = sum(
-                    1 for arg in args if ignored_argument_names.match(arg.name)
+                ignored_pos_args_num = sum(
+                    1 for arg in pos_args if ignored_argument_names.match(arg.name)
+                )
+                ignored_kwonly_args_num = sum(
+                    1
+                    for arg in node.args.kwonlyargs
+                    if ignored_argument_names.match(arg.name)
                 )
+                ignored_args_num = ignored_pos_args_num + ignored_kwonly_args_num
 
             argnum = len(args) - ignored_args_num
             if argnum > self.linter.config.max_args:
@@ -551,6 +567,16 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None:
                     node=node,
                     args=(len(args), self.linter.config.max_args),
                 )
+            pos_args_count = (
+                len(args) - len(node.args.kwonlyargs) - ignored_pos_args_num
+            )
+            if pos_args_count > self.linter.config.max_positional_arguments:
+                self.add_message(
+                    "too-many-positional-arguments",
+                    node=node,
+                    args=(pos_args_count, self.linter.config.max_positional_arguments),
+                    confidence=HIGH,
+                )
         else:
             ignored_args_num = 0
         # check number of local variables
diff --git a/pylintrc b/pylintrc
index 8989a90a77..c1c70c0731 100644
--- a/pylintrc
+++ b/pylintrc
@@ -433,6 +433,9 @@ max-attributes=11
 # Maximum number of statements in a try-block
 max-try-statements = 7
 
+# Maximum number of positional arguments (see R0917).
+max-positional-arguments = 12
+
 [CLASSES]
 
 # List of method names used to declare (i.e. assign) instance attributes.


Test patch:
diff --git a/pylint/testutils/pyreverse.py b/pylint/testutils/pyreverse.py
index c621f9e7a9..115eda416a 100644
--- a/pylint/testutils/pyreverse.py
+++ b/pylint/testutils/pyreverse.py
@@ -25,6 +25,7 @@ class PyreverseConfig(
 
     def __init__(
         self,
+        *,
         mode: str = "PUB_ONLY",
         classes: list[str] | None = None,
         show_ancestors: int | None = None,
diff --git a/tests/functional/a/async_functions.py b/tests/functional/a/async_functions.py
index 75fa684dff..5852dd2438 100644
--- a/tests/functional/a/async_functions.py
+++ b/tests/functional/a/async_functions.py
@@ -22,7 +22,8 @@ async def some_method(self):
         super(OtherClass, self).test() # [bad-super-call]
 
 
-# +1: [too-many-arguments,too-many-return-statements, too-many-branches]
+# +1: [line-too-long]
+# +1: [too-many-arguments, too-many-positional-arguments, too-many-return-statements, too-many-branches]
 async def complex_function(this, function, has, more, arguments, than,
                            one, _, should, have):
     if 1:
diff --git a/tests/functional/a/async_functions.txt b/tests/functional/a/async_functions.txt
index bfb9e52021..33052f8773 100644
--- a/tests/functional/a/async_functions.txt
+++ b/tests/functional/a/async_functions.txt
@@ -1,10 +1,12 @@
 redefined-builtin:5:0:5:14:next:Redefining built-in 'next':UNDEFINED
 unused-argument:8:30:8:34:some_function:Unused argument 'arg2':HIGH
 bad-super-call:22:8:22:31:Class.some_method:Bad first argument 'OtherClass' given to super():UNDEFINED
-too-many-arguments:26:0:26:26:complex_function:Too many arguments (10/5):UNDEFINED
-too-many-branches:26:0:26:26:complex_function:Too many branches (13/12):UNDEFINED
-too-many-return-statements:26:0:26:26:complex_function:Too many return statements (10/6):UNDEFINED
-dangerous-default-value:59:0:59:14:func:Dangerous default value [] as argument:UNDEFINED
-duplicate-argument-name:59:18:59:19:func:Duplicate argument name 'a' in function definition:HIGH
-disallowed-name:64:0:64:13:foo:"Disallowed name ""foo""":HIGH
-empty-docstring:64:0:64:13:foo:Empty function docstring:HIGH
+line-too-long:26:0:None:None::Line too long (104/100):UNDEFINED
+too-many-arguments:27:0:27:26:complex_function:Too many arguments (10/5):UNDEFINED
+too-many-branches:27:0:27:26:complex_function:Too many branches (13/12):UNDEFINED
+too-many-positional-arguments:27:0:27:26:complex_function:Too many positional arguments (9/5):HIGH
+too-many-return-statements:27:0:27:26:complex_function:Too many return statements (10/6):UNDEFINED
+dangerous-default-value:60:0:60:14:func:Dangerous default value [] as argument:UNDEFINED
+duplicate-argument-name:60:18:60:19:func:Duplicate argument name 'a' in function definition:HIGH
+disallowed-name:65:0:65:13:foo:"Disallowed name ""foo""":HIGH
+empty-docstring:65:0:65:13:foo:Empty function docstring:HIGH
diff --git a/tests/functional/ext/docparams/parameter/missing_param_doc_required_Google.py b/tests/functional/ext/docparams/parameter/missing_param_doc_required_Google.py
index 0fd82307fb..05db0cd500 100644
--- a/tests/functional/ext/docparams/parameter/missing_param_doc_required_Google.py
+++ b/tests/functional/ext/docparams/parameter/missing_param_doc_required_Google.py
@@ -383,7 +383,7 @@ def test_finds_args_with_xref_type_google(named_arg, **kwargs):
 
 def test_ignores_optional_specifier_google(
     param1, param2, param3=(), param4=[], param5=[], param6=True
-):
+):  # pylint: disable=too-many-positional-arguments
     """Do something.
 
     Args:
@@ -411,7 +411,7 @@ def test_finds_multiple_complex_types_google(
     named_arg_eight,
     named_arg_nine,
     named_arg_ten,
-):
+):  # pylint: disable=too-many-positional-arguments
     """The google docstring
 
     Args:
diff --git a/tests/functional/ext/docparams/parameter/missing_param_doc_required_Numpy.py b/tests/functional/ext/docparams/parameter/missing_param_doc_required_Numpy.py
index 83fa9700d3..3d84fdef24 100644
--- a/tests/functional/ext/docparams/parameter/missing_param_doc_required_Numpy.py
+++ b/tests/functional/ext/docparams/parameter/missing_param_doc_required_Numpy.py
@@ -344,7 +344,7 @@ def my_func(
     named_arg_six,
     named_arg_seven,
     named_arg_eight,
-):
+):  # pylint: disable=too-many-positional-arguments
     """The docstring
 
     Args
diff --git a/tests/functional/t/too/too_many_arguments.py b/tests/functional/t/too/too_many_arguments.py
index 9c7f3ab087..0d427c7e49 100644
--- a/tests/functional/t/too/too_many_arguments.py
+++ b/tests/functional/t/too/too_many_arguments.py
@@ -1,6 +1,7 @@
 # pylint: disable=missing-docstring,wrong-import-position,unnecessary-dunder-call
 
-def stupid_function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9): # [too-many-arguments]
+# +1: [too-many-arguments, too-many-positional-arguments]
+def stupid_function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9):
     return arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9
 
 
diff --git a/tests/functional/t/too/too_many_arguments.txt b/tests/functional/t/too/too_many_arguments.txt
index 6d727813ea..b996058196 100644
--- a/tests/functional/t/too/too_many_arguments.txt
+++ b/tests/functional/t/too/too_many_arguments.txt
@@ -1,2 +1,3 @@
-too-many-arguments:3:0:3:19:stupid_function:Too many arguments (9/5):UNDEFINED
-too-many-arguments:36:0:36:9:name1:Too many arguments (6/5):UNDEFINED
+too-many-arguments:4:0:4:19:stupid_function:Too many arguments (9/5):UNDEFINED
+too-many-positional-arguments:4:0:4:19:stupid_function:Too many positional arguments (9/5):HIGH
+too-many-arguments:37:0:37:9:name1:Too many arguments (6/5):UNDEFINED
diff --git a/tests/functional/t/too/too_many_locals.py b/tests/functional/t/too/too_many_locals.py
index 34395871d7..8b141039f8 100644
--- a/tests/functional/t/too/too_many_locals.py
+++ b/tests/functional/t/too/too_many_locals.py
@@ -29,7 +29,8 @@ def too_many_locals_function(): # [too-many-locals]
     args15 = args14 * 15
     return args15
 
-def too_many_arguments_function(arga, argu, argi, arge, argt, args): # [too-many-arguments]
+# +1: [too-many-arguments, too-many-positional-arguments]
+def too_many_arguments_function(arga, argu, argi, arge, argt, args):
     """pylint will complain about too many arguments."""
     arga = argu
     arga += argi
diff --git a/tests/functional/t/too/too_many_locals.txt b/tests/functional/t/too/too_many_locals.txt
index 19272626f1..6617661e3c 100644
--- a/tests/functional/t/too/too_many_locals.txt
+++ b/tests/functional/t/too/too_many_locals.txt
@@ -1,3 +1,4 @@
 too-many-locals:4:0:4:12:function:Too many local variables (16/15):UNDEFINED
 too-many-locals:12:0:12:28:too_many_locals_function:Too many local variables (16/15):UNDEFINED
-too-many-arguments:32:0:32:31:too_many_arguments_function:Too many arguments (6/5):UNDEFINED
+too-many-arguments:33:0:33:31:too_many_arguments_function:Too many arguments (6/5):UNDEFINED
+too-many-positional-arguments:33:0:33:31:too_many_arguments_function:Too many positional arguments (6/5):HIGH
diff --git a/tests/functional/t/too/too_many_positional_arguments.py b/tests/functional/t/too/too_many_positional_arguments.py
new file mode 100644
index 0000000000..e373324818
--- /dev/null
+++ b/tests/functional/t/too/too_many_positional_arguments.py
@@ -0,0 +1,9 @@
+# pylint: disable=missing-function-docstring, missing-module-docstring
+class FiveArgumentMethods:
+    """The max positional arguments default is 5."""
+    def fail1(self, a, b, c, d, e):  # [too-many-arguments, too-many-positional-arguments]
+        pass
+    def fail2(self, a, b, c, d, /, e):  # [too-many-arguments, too-many-positional-arguments]
+        pass
+    def okay1(self, a, b, c, d, *, e=True):  # [too-many-arguments]
+        pass
diff --git a/tests/functional/t/too/too_many_positional_arguments.txt b/tests/functional/t/too/too_many_positional_arguments.txt
new file mode 100644
index 0000000000..80591cd371
--- /dev/null
+++ b/tests/functional/t/too/too_many_positional_arguments.txt
@@ -0,0 +1,5 @@
+too-many-arguments:4:4:4:13:FiveArgumentMethods.fail1:Too many arguments (6/5):UNDEFINED
+too-many-positional-arguments:4:4:4:13:FiveArgumentMethods.fail1:Too many positional arguments (6/5):HIGH
+too-many-arguments:6:4:6:13:FiveArgumentMethods.fail2:Too many arguments (6/5):UNDEFINED
+too-many-positional-arguments:6:4:6:13:FiveArgumentMethods.fail2:Too many positional arguments (6/5):HIGH
+too-many-arguments:8:4:8:13:FiveArgumentMethods.okay1:Too many arguments (6/5):UNDEFINED
diff --git a/tests/functional/u/unexpected_special_method_signature.py b/tests/functional/u/unexpected_special_method_signature.py
index 146308b035..31ea161faf 100644
--- a/tests/functional/u/unexpected_special_method_signature.py
+++ b/tests/functional/u/unexpected_special_method_signature.py
@@ -74,6 +74,7 @@ class Valid:
     def __new__(cls, test, multiple, args):
         pass
 
+    # pylint: disable-next=too-many-positional-arguments
     def __init__(self, this, can, have, multiple, args, as_well):
         pass
 
diff --git a/tests/functional/w/wrong_import_order.txt b/tests/functional/w/wrong_import_order.txt
index 068d2140d8..9f143c292d 100644
--- a/tests/functional/w/wrong_import_order.txt
+++ b/tests/functional/w/wrong_import_order.txt
@@ -1,16 +1,16 @@
-wrong-import-order:12:0:12:14::"standard import ""os.path"" should be placed before third party import ""six""":UNDEFINED
-wrong-import-order:14:0:14:10::"standard import ""sys"" should be placed before third party imports ""six"", ""astroid.are_exclusive""":UNDEFINED
-wrong-import-order:15:0:15:15::"standard import ""datetime"" should be placed before third party imports ""six"", ""astroid.are_exclusive""":UNDEFINED
-wrong-import-order:18:0:18:22::"third party import ""totally_missing"" should be placed before local import ""package.Class""":UNDEFINED
-wrong-import-order:20:0:20:14::"third party import ""astroid"" should be placed before local imports ""package.Class"", "".package""":UNDEFINED
-wrong-import-order:22:0:22:22::"first party import ""pylint.checkers"" should be placed before local imports ""package.Class"", "".package"", "".package2""":UNDEFINED
-wrong-import-order:23:0:23:25::"first party import ""pylint.config"" should be placed before local imports ""package.Class"", "".package"", "".package2""":UNDEFINED
-wrong-import-order:24:0:24:17::"first party import ""pylint.sys"" should be placed before local imports ""package.Class"", "".package"", "".package2""":UNDEFINED
-wrong-import-order:25:0:25:28::"first party import ""pylint.pyreverse"" should be placed before local imports ""package.Class"", "".package"", "".package2""":UNDEFINED
-wrong-import-order:30:0:30:40::"third party import ""six.moves.urllib.parse.quote"" should be placed before first party imports ""pylint.checkers"", ""pylint.config"", ""pylint.sys"", ""pylint.pyreverse"" and local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
-wrong-import-order:31:0:31:23::"first party import ""pylint.constants"" should be placed before local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
-wrong-import-order:32:0:32:19::"standard import ""re"" should be placed before third party imports ""six"", ""astroid.are_exclusive"", ""unused_import"", ""totally_missing"", ""astroid"", ""six.moves.urllib.parse.quote"", first party imports ""pylint.checkers"", ""pylint.config"", ""pylint.sys"", ""pylint.pyreverse"", ""pylint.constants"", and local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
-wrong-import-order:32:0:32:19::"third party import ""requests"" should be placed before first party imports ""pylint.checkers"", ""pylint.config"", ""pylint.sys"", ""pylint.pyreverse"", ""pylint.constants"" and local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
-wrong-import-order:33:0:33:24::"first party import ""pylint.exceptions"" should be placed before local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
-wrong-import-order:34:0:34:21::"first party import ""pylint.message"" should be placed before local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
-wrong-import-order:35:0:35:11::"standard import ""time"" should be placed before third party imports ""six"", ""astroid.are_exclusive"", ""unused_import"" (...) ""astroid"", ""six.moves.urllib.parse.quote"", ""requests"", first party imports ""pylint.checkers"", ""pylint.config"", ""pylint.sys"" (...) ""pylint.constants"", ""pylint.exceptions"", ""pylint.message"", and local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
+wrong-import-order:12:0:12:14::"standard import ""os.path"" should be placed before third party import ""six""":UNDEFINED
+wrong-import-order:14:0:14:10::"standard import ""sys"" should be placed before third party imports ""six"", ""astroid.are_exclusive""":UNDEFINED
+wrong-import-order:15:0:15:15::"standard import ""datetime"" should be placed before third party imports ""six"", ""astroid.are_exclusive""":UNDEFINED
+wrong-import-order:18:0:18:22::"third party import ""totally_missing"" should be placed before local import ""package.Class""":UNDEFINED
+wrong-import-order:20:0:20:14::"third party import ""astroid"" should be placed before local imports ""package.Class"", "".package""":UNDEFINED
+wrong-import-order:22:0:22:22::"first party import ""pylint.checkers"" should be placed before local imports ""package.Class"", "".package"", "".package2""":UNDEFINED
+wrong-import-order:23:0:23:25::"first party import ""pylint.config"" should be placed before local imports ""package.Class"", "".package"", "".package2""":UNDEFINED
+wrong-import-order:24:0:24:17::"first party import ""pylint.sys"" should be placed before local imports ""package.Class"", "".package"", "".package2""":UNDEFINED
+wrong-import-order:25:0:25:28::"first party import ""pylint.pyreverse"" should be placed before local imports ""package.Class"", "".package"", "".package2""":UNDEFINED
+wrong-import-order:30:0:30:40::"third party import ""six.moves.urllib.parse.quote"" should be placed before first party imports ""pylint.checkers"", ""pylint.config"", ""pylint.sys"", ""pylint.pyreverse"" and local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
+wrong-import-order:31:0:31:23::"first party import ""pylint.constants"" should be placed before local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
+wrong-import-order:32:0:32:19::"standard import ""re"" should be placed before third party imports ""six"", ""astroid.are_exclusive"", ""unused_import"", ""totally_missing"", ""astroid"", ""six.moves.urllib.parse.quote"", first party imports ""pylint.checkers"", ""pylint.config"", ""pylint.sys"", ""pylint.pyreverse"", ""pylint.constants"", and local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
+wrong-import-order:32:0:32:19::"third party import ""requests"" should be placed before first party imports ""pylint.checkers"", ""pylint.config"", ""pylint.sys"", ""pylint.pyreverse"", ""pylint.constants"" and local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
+wrong-import-order:33:0:33:24::"first party import ""pylint.exceptions"" should be placed before local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
+wrong-import-order:34:0:34:21::"first party import ""pylint.message"" should be placed before local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED
+wrong-import-order:35:0:35:11::"standard import ""time"" should be placed before third party imports ""six"", ""astroid.are_exclusive"", ""unused_import"" (...) ""astroid"", ""six.moves.urllib.parse.quote"", ""requests"", first party imports ""pylint.checkers"", ""pylint.config"", ""pylint.sys"" (...) ""pylint.constants"", ""pylint.exceptions"", ""pylint.message"", and local imports ""package.Class"", "".package"", "".package2"" (...) ""package3.Class3"", "".package4"", ""package4.Class4""":UNDEFINED