title: Classmethods in Generic Protocols with self-types, mypy type checking failure
body: <p>A little background, I essentially need to define an <code>int</code> wrapper type, say <code>MyInt</code> (among some other classes), and another generic <code>Interval</code> type which can accept <code>MyInt</code> objects as well as other types of objects. Since the types acceptable by the <code>Interval</code> do not fall into a neat hierarchy, I thought this would be a perfect use-case for the experimental <code>Protocol</code>, which in my case would require a couple of methods and a couple of <code>@classmethod</code>s. All the methods return a "self-type", i.e., <code>MyInt.my_method</code> returns a <code>MyInt</code>. Here is a MCVE:</p>
  
  <pre><code>from dataclasses import dataclass
  from typing import Union, ClassVar, TypeVar, Generic, Type
  
  from typing_extensions import Protocol
  
  
  _P = TypeVar('_P', bound='PType')
  class PType(Protocol):
      @classmethod
      def maximum_type_value(cls: Type[_P]) -&gt; _P:
          ...
      @classmethod
      def minimum_type_value(cls: Type[_P]) -&gt; _P:
          ...
      def predecessor(self: _P) -&gt; _P:
          ...
      def successor(self: _P) -&gt; _P:
          ...
  
  @dataclass
  class MyInteger:
      value: int
      _MAX: ClassVar[int] = 42
      _MIN: ClassVar[int] = -42
      def __post_init__(self) -&gt; None:
          if not (self._MIN &lt;= self.value &lt;= self._MAX):
              msg = f"Integers must be in range [{self._MIN}, {self._MAX}]"
              raise ValueError(msg)
      @classmethod
      def maximum_type_value(cls) -&gt; MyInteger:
          return MyInteger(cls._MAX)
      @classmethod
      def minimum_type_value(cls) -&gt; MyInteger:
          return MyInteger(cls._MIN)
      def predecessor(self) -&gt; MyInteger:
          return MyInteger(self.value - 1)
      def successor(self) -&gt; MyInteger:
          return MyInteger(self.value + 1)
  
  
  @dataclass
  class Interval(Generic[_P]):
      low: _P
      high: _P
  
  interval = Interval(MyInteger(1), MyInteger(2))
  def foo(x: PType) -&gt; PType:
      return x
  foo(MyInteger(42))
  </code></pre>
  
  <p>However, mypy complains:</p>
  
  <pre><code>(py37) Juans-MacBook-Pro: juan$ mypy mcve.py
  mcve.py:46: error: Value of type variable "_P" of "Interval" cannot be "MyInteger"
  mcve.py:49: error: Argument 1 to "foo" has incompatible type "MyInteger"; expected "PType"
  mcve.py:49: note: Following member(s) of "MyInteger" have conflicts:
  mcve.py:49: note:     Expected:
  mcve.py:49: note:         def maximum_type_value(cls) -&gt; &lt;nothing&gt;
  mcve.py:49: note:     Got:
  mcve.py:49: note:         def maximum_type_value(cls) -&gt; MyInteger
  mcve.py:49: note:     Expected:
  mcve.py:49: note:         def minimum_type_value(cls) -&gt; &lt;nothing&gt;
  mcve.py:49: note:     Got:
  mcve.py:49: note:         def minimum_type_value(cls) -&gt; MyInteger
  </code></pre>
  
  <p>Which to me is hard to understand. Why is return-type expecting <code>&lt;nothing&gt;</code>? I tried simply not annotating <code>cls</code> in the protocol:</p>
  
  <pre><code>_P = TypeVar('_P', bound='PType')
  class PType(Protocol):
      @classmethod
      def maximum_type_value(cls) -&gt; _P:
          ...
      @classmethod
      def minimum_type_value(cls) -&gt; _P:
          ...
      def predecessor(self: _P) -&gt; _P:
          ...
      def successor(self: _P) -&gt; _P:
          ...
  </code></pre>
  
  <p>However, mypy complains with a similar error message:</p>
  
  <pre><code>mcve.py:46: error: Value of type variable "_P" of "Interval" cannot be "MyInteger"
  mcve.py:49: error: Argument 1 to "foo" has incompatible type "MyInteger"; expected "PType"
  mcve.py:49: note: Following member(s) of "MyInteger" have conflicts:
  mcve.py:49: note:     Expected:
  mcve.py:49: note:         def [_P &lt;: PType] maximum_type_value(cls) -&gt; _P
  mcve.py:49: note:     Got:
  mcve.py:49: note:         def maximum_type_value(cls) -&gt; MyInteger
  mcve.py:49: note:     Expected:
  mcve.py:49: note:         def [_P &lt;: PType] minimum_type_value(cls) -&gt; _P
  mcve.py:49: note:     Got:
  mcve.py:49: note:         def minimum_type_value(cls) -&gt; MyInteger
  </code></pre>
  
  <p>Which to me, makes even less sense. Note, if I make these instance methods:</p>
  
  <pre><code>_P = TypeVar('_P', bound='PType')
  class PType(Protocol):
      def maximum_type_value(self: _P) -&gt; _P:
          ...
      def minimum_type_value(self: _P) -&gt; _P:
          ...
      def predecessor(self: _P) -&gt; _P:
          ...
      def successor(self: _P) -&gt; _P:
          ...
  
  @dataclass
  class MyInteger:
      value: int
      _MAX: ClassVar[int] = 42
      _MIN: ClassVar[int] = -42
      def __post_init__(self) -&gt; None:
          if not (self._MIN &lt;= self.value &lt;= self._MAX):
              msg = f"Integers must be in range [{self._MIN}, {self._MAX}]"
              raise ValueError(msg)
      def maximum_type_value(self) -&gt; MyInteger:
          return MyInteger(self._MAX)
      def minimum_type_value(self) -&gt; MyInteger:
          return MyInteger(self._MIN)
      def predecessor(self) -&gt; MyInteger:
          return MyInteger(self.value - 1)
      def successor(self) -&gt; MyInteger:
          return MyInteger(self.value + 1)
  </code></pre>
  
  <p>Then, <code>mypy</code> doesn't complain at all:</p>
  
  <p>I've read about <a href="https://www.python.org/dev/peps/pep-0544/#self-types-in-protocols" rel="noreferrer">self-types in protocols in PEP 544</a>, where it gives the following example:</p>
  
  <pre><code>C = TypeVar('C', bound='Copyable')
  class Copyable(Protocol):
      def copy(self: C) -&gt; C:
  
  class One:
      def copy(self) -&gt; 'One':
          ...
  
  T = TypeVar('T', bound='Other')
  class Other:
      def copy(self: T) -&gt; T:
          ...
  
  c: Copyable
  c = One()  # OK
  c = Other()  # Also OK
  </code></pre>
  
  <p>Furthermore, <a href="https://www.python.org/dev/peps/pep-0484/#annotating-instance-and-class-methods" rel="noreferrer">in PEP484, regarding typing classmethods</a>, we see this example:</p>
  
  <pre><code>T = TypeVar('T', bound='C')
  class C:
      @classmethod
      def factory(cls: Type[T]) -&gt; T:
          # make a new instance of cls
  
  class D(C): ...
  d = D.factory()  # type here should be D
  </code></pre>
  
  <p>What is wrong with my <code>Protocol</code> / class definition? Am I missing something obvious? I would appreciate any specific answers about <em>why this is failing</em>, or any work-around. But note, I need these attributes to be accessible on the class. </p>
  
  <p>Note, I've tried using a <code>ClassVar</code>, but that introduced other issues... namely, <code>ClassVar</code> does not accept type-variables <a href="https://github.com/python/mypy/issues/3172#issuecomment-294112713" rel="noreferrer">as far as I can tell <code>ClassVar</code>'s cannot be generic</a>. And ideally, it would be a <code>@classmethod</code> since I might have to rely on other meta-data I would want to shove in the class.</p>