Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Protocols are too "eager" to match an overload #3656

Closed
ilevkivskyi opened this issue Jul 4, 2017 · 3 comments
Closed

Protocols are too "eager" to match an overload #3656

ilevkivskyi opened this issue Jul 4, 2017 · 3 comments

Comments

@ilevkivskyi
Copy link
Member

This is a follow-up for #3132 (this does not happen on master).

Consider this example found by @JukkaL in #3132:

from typing import Mapping, Any
import sqlite3

class dict_like_row(sqlite3.Row, Mapping[str, Any]):
    def f(self) -> None:
        d = dict(self)  # Need type annotation for variable   <--- wasn't required before

It looks like this problem appears because sqlite3.Row has an untyped __iter__ and it is therefore considered a subtype of Iterable protocol and therefore matches another overload of dict constructor (annotating Row.__iter__ fixes the problem).

It is not clear what is the right way here, maybe we can somehow tweak overload similarity in case of an unsuccessful inference (here e.g. Tuple[KT, VT] never matches Any).

@Michael0x2a
Copy link
Collaborator

@ilevkivskyi -- I wasn't able to reproduce the error on master (using both sqlite3's Row and a custom Row class w/ an untyped __iter__ method). Maybe this was fixed? It's also entirely possible that I'm not understanding the example (protocols are black magic to me tbh).

Can you check and see if this is still an issue whenever you have time?

@ilevkivskyi
Copy link
Member Author

protocols are black magic to me tbh

Why??? They are so nice :-)

Just to give a bit more details the problem was that dict has an overloaded constructor that includes these two overloads:

    @overload
    def __init__(self, map: Mapping[KT, VT], **kwargs: VT) -> None: ...
    @overload
    def __init__(self, iterable: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None: ...

before protocols, the class in example only matched the first overload, but after Iterable became structural, the class is also a structural subtype of Iterable[Any], so it matches the second one too. Moreover, it was matched preferably, because IIRC structural matches were given higher similarity for other reasons. Then type inference failed when trying to find any constraints on variables from Any <: Tuple[KT, VT] causing this error. Since you removed the similarity concept, the problem has gone, now the first match is chosen. So yes, the problem is fixed.

@Michael0x2a
Copy link
Collaborator

protocols are black magic to me tbh

Why??? They are so nice :-)

I mean the implementation :)

I have no idea how you got recursive Protocols working, for example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants