Repository: PyCQA/flake8-bugbear

Issue:
B902 requires first parameter named self for __init__ of metaclass
I have a metaclass defined like this:

    class StorageMeta(type):
        def __init__(cls, name, bases, d):

Naturally B902 flagged the first parameter of `__init__`, but I'm not sure if it's intended in the case of metaclasses. I can imagine arguments for and against this behavior.

Discussion:

Patch:
diff --git a/README.rst b/README.rst
index 9f3cf5f..81382f4 100644
--- a/README.rst
+++ b/README.rst
@@ -107,9 +107,10 @@ Users coming from Python 2 may expect the old behavior which might lead
 to bugs.  Use native ``async def`` coroutines or mark intentional
 ``return x`` usage with ``# noqa`` on the same line.
 
-**B902**: Invalid first argument used for method. Use ``self`` for
-instance methods, and `cls` for class methods (which includes `__new__`
-and `__init_subclass__`).
+**B902**: Invalid first argument used for method. Use ``self`` for instance
+methods, and `cls` for class methods (which includes `__new__` and
+`__init_subclass__`) or instance methods of metaclasses. Note that this lint
+can only detect metaclasses if they directly inherit from ``type``.
 
 **B950**: Line too long. This is a pragmatic equivalent of ``pycodestyle``'s
 E501: it considers "max-line-length" but only triggers when the value has been
diff --git a/bugbear.py b/bugbear.py
index d9dc7e7..42a9077 100644
--- a/bugbear.py
+++ b/bugbear.py
@@ -319,6 +319,10 @@ class BugBearVisitor(ast.NodeVisitor):
         ):
             expected_first_args = B902.cls
             kind = 'class'
+        elif any(getattr(x, 'id', None) == 'type'
+                 for x in self.node_stack[-2].bases):
+            expected_first_args = B902.cls
+            kind = 'metaclass instance'
         else:
             expected_first_args = B902.self
             kind = 'instance'


Test patch:
diff --git a/tests/b902.py b/tests/b902.py
index 91e6bc1..7f8ffc1 100644
--- a/tests/b902.py
+++ b/tests/b902.py
@@ -47,3 +47,13 @@ class Warnings:
 
     def invalid_keyword_only(*, self):
         ...
+
+
+class Meta(type):
+    def __init__(cls, name, bases, d):
+        ...
+
+
+class OtherMeta(type):
+    def __init__(self, name, bases, d):
+        ...
diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py
index f7c89d9..c94a60e 100644
--- a/tests/test_bugbear.py
+++ b/tests/test_bugbear.py
@@ -146,6 +146,7 @@ class BugbearTestCase(unittest.TestCase):
                 B902(39, 22, vars=("*args", 'instance', 'self')),
                 B902(45, 30, vars=("**kwargs", 'instance', 'self')),
                 B902(48, 32, vars=("*, self", 'instance', 'self')),
+                B902(58, 17, vars=("'self'", 'metaclass instance', 'cls')),
             )
         )