python / python/mypy

Error messages when Type[X] is not a subtype of Type[SomeProtocol] are not very descriptive

Open
#5,390 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

priority-1-normal topic-error-reporting topic-protocols topic-usability
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Consider the following code, which contains a type error. We expect a Type[MyProtocol], but we pass in a Type[SomeClass] instead, where SomeClass is incompatible with MyProtocol:

from typing import *

class MyProtocol(Protocol):
    def foo(self) -> int: pass

class SomeClass:
    def foo(self) -> str: pass

def test(x: Type[MyProtocol]) -> None: pass

test(SomeClass)

The error message we get is just Argument 1 to "test" has incompatible type "Type[SomeClass]"; expected "Type[MyProtocol]".

It would be nice if we got the more detailed error message we usually get when misusing protocols instead. For example, if we modify the code to:

def test(x: MyProtocol) -> None: pass
test(SomeClass())

...we get:

../probe/test2.py:15: error: Argument 1 to "test" has incompatible type "SomeClass"; expected "MyProtocol"
../probe/test2.py:15: note: Following member(s) of "SomeClass" have conflicts:
../probe/test2.py:15: note:     Expected:
../probe/test2.py:15: note:         def foo(self) -> int
../probe/test2.py:15: note:     Got:
../probe/test2.py:15: note:         def foo(self) -> str

I think this is probably just an oversight having specifically to do with Type -- doing stuff like:

T = TypeVar('T')
class Wrapper(Generic[T]):
    def __init__(self, x: T) -> None: pass

def test(x: Wrapper[MyProtocol]) -> None: pass
test(Wrapper(SomeClass()))

...results in the same nice error message, as expected.

(I would just go ahead and fix this myself, but I'm busy with other things atm, so filing this here so I don't forget about it/in case somebody else has some spare time.)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running mypy on the two protocol examples in the issue and comparing their diagnostics. Trace the Type[SomeProtocol] compatibility path against the ordinary protocol path, then identify where the member-conflict details are omitted. Done means the Type[...] case reports the conflicting members and their expected and actual signatures.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.