Repository: python/mypy

Issue:
Unexpected error when inheriting a class from Popen and overriding some method in it
* Are you reporting a bug, or opening a feature request?
Bug

* Please insert below the code you are checking with mypy,
  or a mock-up repro if the source is private. We would appreciate
  if you try to simplify your case to a minimal repro.

```python
import subprocess
from typing import List, Tuple


class MyPopen(subprocess.Popen):

    def communicate(self, _input=None, _timeout=None) -> Tuple[bytes, bytes]:
        return super().communicate(_input, _timeout)

def interactive_spawn(cmd: List[str], **kwargs) -> MyPopen:
    process = MyPopen(cmd, **kwargs)
    process.communicate()
    return process
```

* What is the actual behavior/output?
```
$ mypy mypy_subprocess.py
mypy_subprocess.py:13: error: Incompatible return value type (got "Popen[Any]", expected "MyPopen")
Found 1 error in 1 file (checked 1 source file)
````

* What is the behavior/output you expect?
```
$ mypy mypy_subprocess.py
Success: no issues found in 1 source file
```

* What are the versions of mypy and Python you are using?
  Do you see the same issue after installing mypy from Git master?
```
$ mypy --version
mypy 0.730
```

```
(env) $ mypy --version
mypy 0.740+dev.583b99e5096c979043a63dcf92bf3f0d5993d03a
```

**NOTE: it's not reproducing with `0.720`**

```
$ python --version
Python 3.7.4
```

* What are the mypy flags you are using? (For example --strict-optional)
-

* If mypy crashed with a traceback, please paste
  the full traceback below.
-


Discussion:
Yeah this got introduced in 0.730. I need to spend some time tweaking the rules for `__new__` return types maybe.

Resolving patch:
diff --git a/mypy/typeops.py b/mypy/typeops.py
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -77,11 +77,20 @@ def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance,
     variables.extend(info.defn.type_vars)
     variables.extend(init_type.variables)
 
+    from mypy.subtypes import is_subtype
+
     init_ret_type = get_proper_type(init_type.ret_type)
-    if is_new and isinstance(init_ret_type, (Instance, TupleType)):
-        ret_type = init_type.ret_type  # type: Type
+    default_ret_type = fill_typevars(info)
+    if (
+        is_new
+        and isinstance(init_ret_type, (Instance, TupleType))
+        # Only use the return type from __new__ if it is actually returning
+        # a subtype of what we would return otherwise.
+        and is_subtype(init_ret_type, default_ret_type, ignore_type_params=True)
+    ):
+        ret_type = init_ret_type  # type: Type
     else:
-        ret_type = fill_typevars(info)
+        ret_type = default_ret_type
 
     callable_type = init_type.copy_modified(
         ret_type=ret_type, fallback=type_type, name=None, variables=variables,


Test patch:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -6031,7 +6031,7 @@ class A:
     def __new__(cls) -> int:  # E: Incompatible return type for "__new__" (returns "int", but must return a subtype of "A")
         pass
 
-reveal_type(A())  # N: Revealed type is 'builtins.int'
+reveal_type(A())  # N: Revealed type is '__main__.A'
 
 [case testNewReturnType4]
 from typing import TypeVar, Type
@@ -6099,6 +6099,16 @@ class X:
     def __new__(cls, x: TX) -> TX:  # E: "__new__" must return a class instance (got "TX")
         pass
 
+[case testNewReturnType9]
+class A:
+    def __new__(cls) -> A:
+        pass
+
+class B(A):
+    pass
+
+reveal_type(B())  # N: Revealed type is '__main__.B'
+
 [case testGenericOverride]
 from typing import Generic, TypeVar, Any