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

Edge case of invalid TypeVar inference with Generic #16075

Closed
francis-clairicia opened this issue Sep 9, 2023 · 1 comment
Closed

Edge case of invalid TypeVar inference with Generic #16075

francis-clairicia opened this issue Sep 9, 2023 · 1 comment
Labels
bug mypy got something wrong

Comments

@francis-clairicia
Copy link

francis-clairicia commented Sep 9, 2023

Bug Report

It is not possible to infer the type of a TypeVar declared in a Generic with another TypeVar in __init__ by adding a type hint to self.

To Reproduce

from typing import Any, Generic, TypeVar, overload

DTOPacketT = TypeVar("DTOPacketT")

SentPacketT = TypeVar("SentPacketT")
ReceivedPacketT = TypeVar("ReceivedPacketT")

class Serializer(Generic[DTOPacketT]):
    ...
    

class Converter(Generic[SentPacketT, ReceivedPacketT, DTOPacketT]):
    ...
    

class Protocol(Generic[SentPacketT, ReceivedPacketT]):
    @overload
    def __init__(
        self: Protocol[DTOPacketT, DTOPacketT],
        serializer: Serializer[DTOPacketT],
        converter: None = ...,
    ) -> None:
        ...

    @overload
    def __init__(
        self,
        serializer: Serializer[DTOPacketT],
        converter: Converter[SentPacketT, ReceivedPacketT, DTOPacketT],
    ) -> None:
        ...

    def __init__(
        self,
        serializer: Serializer[Any],
        converter: Converter[SentPacketT, ReceivedPacketT, Any] | None = None,
    ) -> None:
        ...

# This case works
serializer: Serializer[str] = Serializer()
converter: Converter[dict[str, Any], dict[str, Any], str] = Converter()

protocol_with_converter = Protocol(serializer, converter=converter)
reveal_type(protocol_with_converter)  # Revealed type is "__main__.Protocol[builtins.dict[builtins.str, Any], builtins.dict[builtins.str, Any]]"

# This case fails
protocol_without_converter = Protocol(serializer, converter=None)
reveal_type(protocol_without_converter)

There is a mypy-play available here:

https://mypy-play.net/?mypy=latest&python=3.11&gist=282865693449e3786dde94d2f45bfb30

Expected Behavior
protocol_without_converter should be inferred as Protocol[str, str]

Actual Behavior

main.py:49: error: Argument 1 to "Protocol" has incompatible type "Serializer[str]"; expected "Serializer[<nothing>]"  [arg-type]
main.py:50: note: Revealed type is "__main__.Protocol[DTOPacketT`-1, DTOPacketT`-1]"

EDIT: A workaround to get this to work is to do the following:

    @overload
    def __init__(
        self,
        serializer: Serializer[SentPacketT | ReceivedPacketT],
        converter: None = ...,
    ) -> None:
        ...

Mypy playground: https://mypy-play.net/?mypy=latest&python=3.11&gist=77ee74bfd5e80a91e3c0ff654a502d1f

Your Environment

  • Mypy version used: 1.5.1
  • Mypy command-line flags: (none)
  • Mypy configuration options from mypy.ini (and other config files): (none)
  • Python version used: 3.11.2
@francis-clairicia francis-clairicia added the bug mypy got something wrong label Sep 9, 2023
@A5rocks
Copy link
Contributor

A5rocks commented Jun 16, 2024

This works on master as of #17348

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

2 participants