Skip to content

Generics in self types should not be bound in different ways #14729

Closed as not planned
@davidhalter

Description

@davidhalter

Bug Report

In the example below you can see that you can have a self type like self: C[Tuple[T, ...]] where T is a type variable of C. This should IMO not be allowed and raise an error. I raised this here first: python/typing#1340 and @hauntsaninja agreed with me:

I agree that this mypy behaviour seems is fragile, if not outright buggy. It would certainly be clearer to use a different type variable if you wanted a different binding.

and further comments about Pyright:

Pyright interprets this as doing something recursive, which looks more correct than what mypy does, but I'm not convinced pyright's behaviour is fully correct either.

He then goes on to write an example that would not be type safe in either mypy or pyright: python/typing#1340 (comment).

To Reproduce

Just run Mypy's test suite (the test testSelfTypeRestrictedMethodOverloadInit of check-selftype.test.

class C(Generic[T]):
    @overload
    def __init__(self: C[T], item: T, use_tuple: Literal[False]) -> None: ...
    @overload
    def __init__(self: C[Tuple[T, ...]], item: T, use_tuple: Literal[True]) -> None: ...

reveal_type(C(0, use_tuple=False))  # N: Revealed type is "lib.C[builtins.int]"
reveal_type(C(0, use_tuple=True))  # N: Revealed type is "lib.C[builtins.tuple[builtins.int, ...]]"

Expected Behavior

I feel like Mypy should just generate an error in this case, because the generics are essentially bound twice.

I feel like it's debatable in the above example if even C[T] should be used. C[Tuple[T, ...]] should however definitely not be allowed, because that kind of contradicts what T is.

However I'm not saying that generics should not be allowed in these positions. I feel like this should definitely be possible, because the type variable is bound to the function, not the class:

U = TypeVar("U")
class C(Generic[T]):
    @overload
    def __init__(self: C[U], item: U, use_tuple: Literal[False]) -> None: ...
    @overload
    def __init__(self: C[Tuple[U, ...]], item: U, use_tuple: Literal[True]) -> None: ...

Your Environment

The test is part of Mypy's test suite here and my environment should therefore not matter:

https://github.com/python/mypy/blob/master/test-data/unit/check-selftype.test#L672-L705

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions