Repository: wemake-services/wemake-python-styleguide

Issue:
Add `__init_subclass__` to the order of methods in WPS338
# Rule request

<!-- Thanks for coming up with a proposal! -->

## Thesis

`__init_subclass__` ([PEP 487](https://peps.python.org/pep-0487/)) should be added to `WPS338` order. The resulting list would look like this:
- `__init_subclass__`
- `__new__`
- `__init__`
- `__call__`
- `__await__`
- public and magic methods
- protected methods
- private methods (we discourage using them)

## Reasoning
In current order I see that construction and initialisation methods are given priority and ordered from instance construction to instance initialisation.

Chronologically, before all that come class construction and its initialisation. First happens in meta class, second can also be done in `__init_subclass__`. Therefore it naturally should come before class `__new__` and `__init__` methods.


## Second thoughts
It might be worth considering doing the same with `__class_getitem__`, since it mostly used for generics construction. But since it might not be even that interesting from user perspective, I'm hesitant at proposing it.

It also should be possible to ignore specific methods by placing #noqa WPS338 at the line of their definition. But that means that reporting will also be done per method instead of current per class reporting, which can be both good (you can see what method is causing the error) and bad (there may be too many errors per class)

<!--
Why do you think this is a good idea?

Please, include some strong points and considerations.
-->

<!--

  Love wemake-python-styleguide? ❤️
  Do you want your features to be implemented faster?

  Please consider supporting our collective:
👉  https://opencollective.com/wemake-python-styleguide/donate

-->



Discussion:
I agree, please, send a PR ;)

Resolving patch:
diff --git a/wemake_python_styleguide/violations/consistency.py b/wemake_python_styleguide/violations/consistency.py
--- a/wemake_python_styleguide/violations/consistency.py
+++ b/wemake_python_styleguide/violations/consistency.py
@@ -1564,6 +1564,7 @@ class WrongMethodOrderViolation(ASTViolation):
 
     We follow the same ordering:
 
+    - ``__init_subclass__``
     - ``__new__``
     - ``__init__``
     - ``__call__``
diff --git a/wemake_python_styleguide/visitors/ast/classes.py b/wemake_python_styleguide/visitors/ast/classes.py
--- a/wemake_python_styleguide/visitors/ast/classes.py
+++ b/wemake_python_styleguide/visitors/ast/classes.py
@@ -395,7 +395,8 @@ def _check_method_order(self, node: ast.ClassDef) -> None:
 
     def _ideal_order(self, first: str) -> int:
         base_methods_order = {
-            '__new__': 6,  # highest priority
+            '__init_subclass__': 7,  # highest priority
+            '__new__': 6,
             '__init__': 5,
             '__call__': 4,
             '__await__': 3,


Test patch:
diff --git a/tests/test_visitors/test_ast/test_classes/test_method_order.py b/tests/test_visitors/test_ast/test_classes/test_method_order.py
--- a/tests/test_visitors/test_ast/test_classes/test_method_order.py
+++ b/tests/test_visitors/test_ast/test_classes/test_method_order.py
@@ -9,6 +9,9 @@
 
 correct_method_order = """
 class Test(object):
+    def __init_subclass__(cls):
+        ...
+
     def __new__(self):
         ...
 
@@ -82,21 +85,25 @@ def test_correct_method_order(
 
 
 @pytest.mark.parametrize(('first', 'second'), [
+    ('__new__', '__init_subclass__'),
     ('__init__', '__new__'),
     ('__call__', '__init__'),
     ('__call__', '__new__'),
     ('__await__', '__call__'),
 
+    ('public', '__init_subclass__'),
     ('public', '__new__'),
     ('public', '__init__'),
     ('public', '__call__'),
     ('public', '__await__'),
 
+    ('__magic__', '__init_subclass__'),
     ('__magic__', '__new__'),
     ('__magic__', '__init__'),
     ('__magic__', '__call__'),
     ('__magic__', '__await__'),
 
+    ('_protected', '__init_subclass__'),
     ('_protected', '__new__'),
     ('_protected', '__init__'),
     ('_protected', '__call__'),
@@ -104,6 +111,7 @@ def test_correct_method_order(
     ('_protected', 'public'),
     ('_protected', '__magic__'),
 
+    ('__private', '__init_subclass__'),
     ('__private', '__new__'),
     ('__private', '__init__'),
     ('__private', '__call__'),