Repository: python/mypy

Issue:
Argument X of "Y" incompatible with supertype "Z" - needs more detail
* Are you reporting a bug, or opening a feature request?

feature request. Presumably mypy has the type information from the supertype available and at hand. It would be extremely helpful if that information was included in the diagnostics so that I don't have to go hunting for it.

* 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.

```
import datetime
class Tzoffset ( datetime.tzinfo ):
	_delta = _tzname = None
	def __init__ ( self, delta, tzname='' ):
		# type: ( datetime.timedelta, str ) -> None
		assert isinstance ( delta, datetime.timedelta )
		self._delta = delta
		self._tzname = tzname
	def utcoffset ( self, dt ):
		# type: ( datetime.datetime ) -> datetime.timedelta
		return self._delta
	def dst ( self, dt ):
		# type: ( datetime.datetime ) -> datetime.timedelta
		return self._delta
	def tzname ( self, dt ):
		# type: ( datetime.datetime ) -> str
		return self._tzname
```

* What is the actual behavior/output?

error: Argument 1 of "tzname" incompatible with supertype "tzinfo"

* What is the behavior/output you expect?

error: Argument 1 of "tzname" incompatible with supertype "tzinfo"; expected "foo"

* What are the versions of mypy and Python you are using?
  Do you see the same issue after installing mypy from Git master?

Python 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:19:30) [MSC v.1500 32 bit (Intel)] on win32
Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32
mypy 0.600
dunno how to check git master

* What are the mypy flags you are using? (For example --strict-optional)

mypy --py2 %1 --ignore-missing-imports --disallow-untyped-defs
mypy %1 --ignore-missing-imports --disallow-untyped-defs

* If mypy crashed with a traceback, please paste
  the full traceback below.

(n/a)



Discussion:
Note there is a proposal to add more info for errors like this behind a flag https://github.com/python/mypy/issues/4835
This is what I ended up doing to shut up mypy, but it's not correct:

def tzname ( self, dt ):
# type: ( Any ) -> Optional[str] # TODO FIXME: figure out the correct type
for the 1st argument
return self._tzname.encode ( 'ascii' )




Royce Mitchell, IT Consultant
ITAS Solutions
royce3@itas-solutions.com

On Mon, May 21, 2018 at 9:19 AM, vovanz <notifications@github.com> wrote:

> @remdragon <https://github.com/remdragon> did you figured out what type
> does mypy expects as the first argument to tzname method? I've
> encountered this problem as well.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/python/mypy/issues/5025#issuecomment-390667644>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/ADka13OWJb4b6w11v4vxRYSkVqlLLFEDks5t0szWgaJpZM4T8pxH>
> .
>

Taking a look at this

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -1531,8 +1531,16 @@ def erase_override(t: Type) -> Type:
                 for i in range(len(override.arg_types)):
                     if not is_subtype(original.arg_types[i],
                                       erase_override(override.arg_types[i])):
+                        arg_type_in_super = original.arg_types[i]
                         self.msg.argument_incompatible_with_supertype(
-                            i + 1, name, type_name, name_in_super, supertype, node)
+                            i + 1,
+                            name,
+                            type_name,
+                            name_in_super,
+                            arg_type_in_super,
+                            supertype,
+                            node
+                        )
                         emitted_msg = True
 
                 if not is_subtype(erase_override(override.ret_type),
diff --git a/mypy/messages.py b/mypy/messages.py
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -797,10 +797,13 @@ def signature_incompatible_with_supertype(
 
     def argument_incompatible_with_supertype(
             self, arg_num: int, name: str, type_name: Optional[str],
-            name_in_supertype: str, supertype: str, context: Context) -> None:
+            name_in_supertype: str, arg_type_in_supertype: Type, supertype: str,
+            context: Context) -> None:
         target = self.override_target(name, name_in_supertype, supertype)
-        self.fail('Argument {} of "{}" incompatible with {}'
-                  .format(arg_num, name, target), context)
+        arg_type_in_supertype_f = self.format_bare(arg_type_in_supertype)
+        self.fail('Argument {} of "{}" is incompatible with {}; '
+                  'supertype defines the argument type as "{}"'
+                  .format(arg_num, name, target, arg_type_in_supertype_f), context)
 
         if name == "__eq__" and type_name:
             multiline_msg = self.comparison_method_example_msg(class_name=type_name)


Test patch:
diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test
--- a/test-data/unit/check-abstract.test
+++ b/test-data/unit/check-abstract.test
@@ -308,7 +308,7 @@ class A(metaclass=ABCMeta):
     def g(self, x: int) -> int: pass
 class B(A):
     def f(self, x: str) -> int: \
-            # E: Argument 1 of "f" incompatible with supertype "A"
+            # E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int"
         pass
     def g(self, x: int) -> int: pass
 [out]
@@ -324,7 +324,7 @@ class J(metaclass=ABCMeta):
     def g(self, x: str) -> str: pass
 class A(I, J):
     def f(self, x: str) -> int: pass \
-        # E: Argument 1 of "f" incompatible with supertype "I"
+        # E: Argument 1 of "f" is incompatible with supertype "I"; supertype defines the argument type as "int"
     def g(self, x: str) -> int: pass \
         # E: Return type of "g" incompatible with supertype "J"
     def h(self) -> int: pass # Not related to any base class
@@ -339,7 +339,7 @@ class J(metaclass=ABCMeta):
 class I(J): pass
 class A(I):
     def f(self, x: str) -> int: pass \
-        # E: Argument 1 of "f" incompatible with supertype "J"
+        # E: Argument 1 of "f" is incompatible with supertype "J"; supertype defines the argument type as "int"
 [out]
 
 [case testInvalidOverridingAbstractMethod]
@@ -350,7 +350,7 @@ class J(metaclass=ABCMeta):
     def f(self, x: 'J') -> None: pass
 class I(J):
     @abstractmethod
-    def f(self, x: 'I') -> None: pass # E: Argument 1 of "f" incompatible with supertype "J"
+    def f(self, x: 'I') -> None: pass # E: Argument 1 of "f" is incompatible with supertype "J"; supertype defines the argument type as "J"
 [out]
 
 [case testAbstractClassCoAndContraVariance]
@@ -371,7 +371,7 @@ class A(I):
     def g(self, a: 'A') -> 'A':
         pass
 [out]
-main:11: error: Argument 1 of "h" incompatible with supertype "I"
+main:11: error: Argument 1 of "h" is incompatible with supertype "I"; supertype defines the argument type as "I"
 main:11: error: Return type of "h" incompatible with supertype "I"
 
 
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
@@ -280,7 +280,7 @@ class B(A):
   def g(self, x: A, y: A) -> A: pass
   def h(self, x: A, y: 'B') -> object: pass  # Fail
 [out]
-main:7: error: Argument 1 of "f" incompatible with supertype "A"
+main:7: error: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "A"
 main:9: error: Return type of "h" incompatible with supertype "A"
 
 [case testEqMethodsOverridingWithNonObjects]
@@ -288,7 +288,7 @@ class A:
   def __eq__(self, other: A) -> bool: pass  # Fail
 [builtins fixtures/attr.pyi]
 [out]
-main:2: error: Argument 1 of "__eq__" incompatible with supertype "object"
+main:2: error: Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object"
 main:2: note: It is recommended for "__eq__" to work with arbitrary objects, for example:
 main:2: note:     def __eq__(self, other: object) -> bool:
 main:2: note:         if not isinstance(other, A):
@@ -316,7 +316,7 @@ class C(B): # with gap in implementations
     def f(self, x: 'C') -> None:  # Fail
         pass
 [out]
-main:6: error: Argument 1 of "f" incompatible with supertype "A"
+main:6: error: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "B"
 
 [case testMethodOverridingAcrossDeepInheritanceHierarchy2]
 import typing
@@ -458,7 +458,7 @@ class B(A):
     @staticmethod
     def f(x: int, y: str) -> None: pass
     @staticmethod
-    def g(x: str, y: str) -> None: pass # E: Argument 1 of "g" incompatible with supertype "A"
+    def g(x: str, y: str) -> None: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "int"
 [builtins fixtures/classmethod.pyi]
 
 [case testOverrideClassMethodWithClassMethod]
@@ -472,7 +472,7 @@ class B(A):
     @classmethod
     def f(cls, x: int, y: str) -> None: pass
     @classmethod
-    def g(cls, x: str, y: str) -> None: pass # E: Argument 1 of "g" incompatible with supertype "A"
+    def g(cls, x: str, y: str) -> None: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "int"
 [builtins fixtures/classmethod.pyi]
 
 [case testOverrideClassMethodWithStaticMethod]
@@ -488,7 +488,7 @@ class B(A):
     @staticmethod
     def f(x: int) -> None: pass
     @staticmethod
-    def g(x: str) -> int: pass # E: Argument 1 of "g" incompatible with supertype "A"
+    def g(x: str) -> int: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "int"
     @staticmethod
     def h() -> int: pass
 [builtins fixtures/classmethod.pyi]
@@ -506,7 +506,7 @@ class B(A):
     @classmethod
     def f(cls, x: int) -> None: pass
     @classmethod
-    def g(cls, x: int) -> int: pass # E: Argument 1 of "g" incompatible with supertype "A"
+    def g(cls, x: int) -> int: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "str"
     @classmethod
     def h(cls) -> int: pass
 [builtins fixtures/classmethod.pyi]
@@ -2452,7 +2452,7 @@ class D(A):
     def __iadd__(self, x: 'A') -> 'B': pass
 [out]
 main:6: error: Return type of "__iadd__" incompatible with "__add__" of supertype "A"
-main:8: error: Argument 1 of "__iadd__" incompatible with "__add__" of supertype "A"
+main:8: error: Argument 1 of "__iadd__" is incompatible with "__add__" of supertype "A"; supertype defines the argument type as "A"
 main:8: error: Signatures of "__iadd__" and "__add__" are incompatible
 
 [case testGetattribute]
diff --git a/test-data/unit/check-dynamic-typing.test b/test-data/unit/check-dynamic-typing.test
--- a/test-data/unit/check-dynamic-typing.test
+++ b/test-data/unit/check-dynamic-typing.test
@@ -698,7 +698,7 @@ class C:
 class B(C):
     def f(self, a): pass
 class A(B):
-    def f(self, a: 'D') -> None: # E: Argument 1 of "f" incompatible with supertype "C"
+    def f(self, a: 'D') -> None: # E: Argument 1 of "f" is incompatible with supertype "C"; supertype defines the argument type as "A"
         pass
 class D: pass
 [out]
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -45,7 +45,7 @@ class A(object):
     def f(self, a: int, b: str) -> None: pass
 
 class B(A):
-    def f(self, b: str, a: int) -> None: pass # E: Argument 1 of "f" incompatible with supertype "A" # E: Argument 2 of "f" incompatible with supertype "A"
+    def f(self, b: str, a: int) -> None: pass # E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int" # E: Argument 2 of "f" is incompatible with supertype "A"; supertype defines the argument type as "str"
 
 class C(A):
     def f(self, foo: int, bar: str) -> None: pass
diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test
--- a/test-data/unit/check-generic-subtyping.test
+++ b/test-data/unit/check-generic-subtyping.test
@@ -186,7 +186,7 @@ class C: pass
 class D: pass
 class A(B[C]):
     def f(self, a: D) -> None: pass \
-        # E: Argument 1 of "f" incompatible with supertype "B"
+        # E: Argument 1 of "f" is incompatible with supertype "B"; supertype defines the argument type as "C"
     def g(self, a: C) -> None: pass
 [out]
 
@@ -199,7 +199,7 @@ class B:
     def g(self, a: C) -> None: pass
 class A(B, Generic[T]):
     def f(self, a: T) -> None: pass \
-        # E: Argument 1 of "f" incompatible with supertype "B"
+        # E: Argument 1 of "f" is incompatible with supertype "B"; supertype defines the argument type as "C"
     def g(self, a: 'C') -> None: pass
 [out]
 
@@ -212,7 +212,7 @@ class B(Generic[T]):
     def g(self, a: T) -> None: pass
 class A(B[S], Generic[T, S]):
     def f(self, a: T) -> None: pass \
-        # E: Argument 1 of "f" incompatible with supertype "B"
+        # E: Argument 1 of "f" is incompatible with supertype "B"; supertype defines the argument type as "S"
     def g(self, a: S) -> None: pass
 [out]
 
@@ -230,7 +230,7 @@ class C(Generic[T, U, V]):
 class B(C[D, D, T], Generic[T]): pass
 class A(B[S], Generic[T, S]):
     def f(self, a: T) -> None: pass \
-        # E: Argument 1 of "f" incompatible with supertype "C"
+        # E: Argument 1 of "f" is incompatible with supertype "C"; supertype defines the argument type as "S"
     def g(self, a: S) -> None: pass
 [out]
 
@@ -279,7 +279,7 @@ class C(A):
 class D(A):
     def f(self, x: T1, y: S) -> None: pass # TODO: This error could be more specific.
 [out]
-main:12: error: Argument 2 of "f" incompatible with supertype "A"
+main:12: error: Argument 2 of "f" is incompatible with supertype "A"; supertype defines the argument type as "S"
 main:14: error: Signature of "f" incompatible with supertype "A"
 
 
@@ -533,7 +533,7 @@ class I(Generic[T]):
     def g(self, a: T) -> None: pass
 class A(I[C]):
     def f(self, a: 'D') -> None: pass \
-        # E: Argument 1 of "f" incompatible with supertype "I"
+        # E: Argument 1 of "f" is incompatible with supertype "I"; supertype defines the argument type as "C"
     def g(self, a: 'C') -> None: pass
 class C: pass
 class D: pass
@@ -565,7 +565,7 @@ class B(I[C]):
     def g(self, a: 'C', b: Any) -> None: pass
 class A(B):
     def g(self, a: 'C', b: 'C') -> None: pass \
-        # E: Argument 2 of "g" incompatible with supertype "I"
+        # E: Argument 2 of "g" is incompatible with supertype "I"; supertype defines the argument type as "D"
     def f(self, a: 'C', b: 'C') -> None: pass
 class C: pass
 class D: pass
@@ -581,7 +581,7 @@ class B(I[C]):
     def f(self, a: 'C', b: Any) -> None: pass
 class A(B):
     def f(self, a: 'C', b: 'D') -> None: pass \
-        # E: Argument 2 of "f" incompatible with supertype "I"
+        # E: Argument 2 of "f" is incompatible with supertype "I"; supertype defines the argument type as "C"
 class C: pass
 class D: pass
 [out]
diff --git a/test-data/unit/check-multiple-inheritance.test b/test-data/unit/check-multiple-inheritance.test
--- a/test-data/unit/check-multiple-inheritance.test
+++ b/test-data/unit/check-multiple-inheritance.test
@@ -538,7 +538,7 @@ class Flag:
 class IntFlag(int, Flag):
     def __or__(self: _T, other: str) -> _T: ...
 [out]
-main:8: error: Argument 1 of "__or__" incompatible with supertype "Flag"
+main:8: error: Argument 1 of "__or__" is incompatible with supertype "Flag"; supertype defines the argument type as "IntFlag"
 
 [case testMultipleInheritance_MethodDefinitionsCompatibleNoOverride]
 from typing import TypeVar, Union