Repository: python/mypy

Issue:
`stubgen` removes `Protocol` from base classes list
**Bug Report**

Consider the following input:
```python
from typing import Protocol

class Interface(Protocol):
    def dummy(self, arg: str) -> str:
        ...
```
`stubgen` produces:
```python
class Interface:
    def dummy(self, arg: str) -> str: ...
```

Note that the `Protocol` class is removed from the list of base classes.

Obviously this causes problems when performing type checking against users of that generated typestub:

```python
from interface import Interface

class InterfaceImpl:
    def dummy(self, arg: str) -> str:
        return arg


interf: Interface = InterfaceImpl()
```
mypy:
```
provider/__init__.py:8: error: Incompatible types in assignment (expression has type "InterfaceImpl", variable has type "Interface")
```

**Expected Behavior**

`Protocol` base class is not removed.

**Actual Behavior**

`Protocol` base class is removed.


**Your Environment**

- Mypy version used: mypy 0.931
- Python version used: Python 3.9.7
- Operating system and version: macOS



Discussion:
This works as a fix:
```diff
diff --git a/mypy/stubgen.py b/mypy/stubgen.py
index b0a2c4e64..4e1a0fe08 100755
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -849,6 +849,9 @@ class StubGenerator(mypy.traverser.TraverserVisitor):
             base_types.append('metaclass=abc.ABCMeta')
             self.import_tracker.add_import('abc')
             self.import_tracker.require_name('abc')
+        elif self.analyzed and o.info.is_protocol:
+            base_types.append('Protocol')
+            self.add_typing_import('Protocol')
         if base_types:
             self.add('(%s)' % ', '.join(base_types))
         self.add(':\n')
```

If you think this is the way to go, I can submit it as a PR.

Resolving patch:
diff --git a/mypy/stubgen.py b/mypy/stubgen.py
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -851,6 +851,12 @@ def visit_class_def(self, o: ClassDef) -> None:
             base_types.append('metaclass=abc.ABCMeta')
             self.import_tracker.add_import('abc')
             self.import_tracker.require_name('abc')
+        elif self.analyzed and o.info.is_protocol:
+            type_str = 'Protocol'
+            if o.info.type_vars:
+                type_str += f'[{", ".join(o.info.type_vars)}]'
+            base_types.append(type_str)
+            self.add_typing_import('Protocol')
         if base_types:
             self.add('(%s)' % ', '.join(base_types))
         self.add(':\n')


Test patch:
diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test
--- a/test-data/unit/stubgen.test
+++ b/test-data/unit/stubgen.test
@@ -2581,6 +2581,30 @@ def f(x: int, y: int) -> int: ...
 @t.overload
 def f(x: t.Tuple[int, int]) -> int: ...
 
+[case testProtocol_semanal]
+from typing import Protocol, TypeVar
+
+class P(Protocol):
+    def f(self, x: int, y: int) -> str:
+        ...
+
+T = TypeVar('T')
+T2 = TypeVar('T2')
+class PT(Protocol[T, T2]):
+    def f(self, x: T) -> T2:
+        ...
+
+[out]
+from typing import Protocol, TypeVar
+
+class P(Protocol):
+    def f(self, x: int, y: int) -> str: ...
+T = TypeVar('T')
+T2 = TypeVar('T2')
+
+class PT(Protocol[T, T2]):
+    def f(self, x: T) -> T2: ...
+
 [case testNonDefaultKeywordOnlyArgAfterAsterisk]
 def func(*, non_default_kwarg: bool, default_kwarg: bool = True): ...
 [out]