Repository: python/mypy

Issue:
__class_getitem__ is not assumed to be a classmethod
mypy (version 0.720) gives confusing errors like this:

    error: The erased type of self "Type[_Type]" is not a supertype of its class "_Type"

for the following method:

```python
_TypeT = TypeVar("_TypeT", bound="_Type")
class _Type:
    def __class_getitem__(cls: Type[_TypeT], item: Any) -> _TypeT:
        raise NotImplementedError
```

even though this is [apparently the correct way](https://github.com/python/typing/issues/58#issuecomment-326240794) to type-hint factory functions.

Adding the `@classmethod` decorator makes the error go away, but [according to PEP 560](https://www.python.org/dev/peps/pep-0560/#class-getitem), `__class_getitem__` is automatically a class method, so the decorator shouln't be required.


Discussion:
Yes, we should recognise it as a class method automatically, this is however quite low priority IMO, because it is a rarely used method.
On the other hand it should be easy to fix, so please go ahead if you would like to make a PR.
I think we have different definitions of "easy" :)

I pushed [a few commits](https://github.com/python/mypy/compare/master...anowlcalledjosh:class-getitem-is-classmethod) to [my fork](https://github.com/anowlcalledjosh/mypy/tree/class-getitem-is-classmethod), but I get some errors in the tests I added, which I have no idea how to fix, since I basically just copied bits of what is special-cased for `__init_subclass__` (which is also automatically a classmethod), ignoring the parts about setting the return type to None; I probably don't have the brainspace to work out how to fix this in the near future, sorry.
> I think we have different definitions of "easy" :)

I have only one scale, it is much easier than any other mypy issue.

Btw a quick fix for your issue will be to tweak `shared_parse.special_function_elide_names()` so that it will preserve `cls` argument name for `__class_getitem__`. Or maybe a better solution would be to always keep the name of first argument.
I think something similar to https://github.com/python/mypy/pull/7355 would work for this.

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -616,7 +616,7 @@ def prepare_method_signature(self, func: FuncDef, info: TypeInfo) -> None:
         # Only non-static methods are special.
         functype = func.type
         if not func.is_static:
-            if func.name() == '__init_subclass__':
+            if func.name() in ['__init_subclass__', '__class_getitem__']:
                 func.is_class = True
             if not func.arguments:
                 self.fail('Method must have at least one argument', func)


Test patch:
diff --git a/test-data/unit/check-super.test b/test-data/unit/check-super.test
--- a/test-data/unit/check-super.test
+++ b/test-data/unit/check-super.test
@@ -258,6 +258,17 @@ class A:
         super().__init_subclass__()
 [builtins fixtures/__init_subclass__.pyi]
 
+[case testSuperClassGetItem]
+from typing import TypeVar, Type, Any
+
+T = TypeVar("T", bound="B")
+
+class A:
+    def __class_getitem__(cls, item) -> None: pass
+
+class B(A):
+    def __class_getitem__(cls: Type[T], item: Any) -> None:
+        super(B, cls).__class_getitem__(item)
 
 -- Invalid uses of super()
 -- -----------------------