Repository: wemake-services/wemake-python-styleguide

Issue:
Feature: count async function as a module member
Currently we count only regular functions and classes as module members: https://github.com/wemake-services/wemake-python-styleguide/blob/master/wemake_python_styleguide/visitors/ast/complexity/counts.py#L40-L60

What needs to be done?
1. We need to count `AsyncFunctionDef` as well
2. We need to improve `ModuleMembers` type to include `AsyncFunctionDef`
3. We need to refactor how handler for each node works: there's no need to duplicate the logic, so it can just be: `visit_AsyncFunctionDef = visit_FunctionDef = visit_ClassDef`
4. We need to improve unit tests to include `async` functions as well: https://github.com/wemake-services/wemake-python-styleguide/blob/master/tests/test_visitors/test_ast/test_complexity/test_counts/test_module_counts.py




Discussion:
Hi, I will take this issue.

Resolving patch:
diff --git a/wemake_python_styleguide/types.py b/wemake_python_styleguide/types.py
--- a/wemake_python_styleguide/types.py
+++ b/wemake_python_styleguide/types.py
@@ -36,7 +36,7 @@
 CheckResult = Tuple[int, int, str, type]
 
 #: Code members that we count in a module:
-ModuleMembers = Union[ast.FunctionDef, ast.ClassDef]
+ModuleMembers = Union[ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef]
 
 
 class ConfigurationOptions(Protocol):
diff --git a/wemake_python_styleguide/visitors/ast/complexity/counts.py b/wemake_python_styleguide/visitors/ast/complexity/counts.py
--- a/wemake_python_styleguide/visitors/ast/complexity/counts.py
+++ b/wemake_python_styleguide/visitors/ast/complexity/counts.py
@@ -37,9 +37,9 @@ def _post_visit(self) -> None:
         if self._public_items_count > self.options.max_module_members:
             self.add_violation(TooManyModuleMembersViolation())
 
-    def visit_ClassDef(self, node: ast.ClassDef) -> None:
+    def visit_module_members(self, node: ModuleMembers) -> None:
         """
-        Counts the number of `class`es in a single module.
+        Counts the number of ModuleMembers in a single module.
 
         Raises:
             TooManyModuleMembersViolation
@@ -48,16 +48,8 @@ def visit_ClassDef(self, node: ast.ClassDef) -> None:
         self._check_members_count(node)
         self.generic_visit(node)
 
-    def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
-        """
-        Counts the number of functions in a single module.
-
-        Raises:
-            TooManyModuleMembersViolation
-
-        """
-        self._check_members_count(node)
-        self.generic_visit(node)
+    visit_ClassDef = visit_module_members
+    visit_AsyncFunctionDef = visit_FunctionDef = visit_module_members
 
 
 class ImportMembersVisitor(BaseNodeVisitor):


Test patch:
diff --git a/tests/test_visitors/test_ast/test_complexity/test_counts/test_module_counts.py b/tests/test_visitors/test_ast/test_complexity/test_counts/test_module_counts.py
--- a/tests/test_visitors/test_ast/test_complexity/test_counts/test_module_counts.py
+++ b/tests/test_visitors/test_ast/test_complexity/test_counts/test_module_counts.py
@@ -13,6 +13,12 @@ def first(): ...
 class Second(object): ...
 """
 
+module_with_async_function_and_class = """
+async def first(): ...
+
+class Second(object): ...
+"""
+
 module_with_methods = """
 class First(object):
     def method(self): ...
@@ -24,6 +30,7 @@ def method2(self): ...
 
 @pytest.mark.parametrize('code', [
     module_with_function_and_class,
+    module_with_async_function_and_class,
     module_with_methods,
 ])
 def test_module_counts_normal(
@@ -40,6 +47,7 @@ def test_module_counts_normal(
 
 @pytest.mark.parametrize('code', [
     module_with_function_and_class,
+    module_with_async_function_and_class,
     module_with_methods,
 ])
 def test_module_counts_violation(