Skip to content

Recent change to tuple constructor breaks subclasses of tuples #8275

Closed

Description

@erictraut

A recent change to the tuple.__new__ method in builtins.pyi introduced an overload:

    @overload
    def __new__(cls) -> tuple[()]: ...

This overload is problematic for classes that derive from specialized tuples.

class MyTuple(tuple[int, str, str]):
    ...

x = MyTuple()
reveal_type(x) # tuple[()] !!!!

Because of this overload, the wrong type is evaluated.

I think this overload should return Self, not tuple[()]. Admittedly, that doesn't produce the ideal type evaluation for tuple(). It produces tuple[Any, ...] rather than tuple[()].

Activity

  1. AlexWaygood commented on Jul 12, 2022

    @AlexWaygood
    Member
    class MyTuple(tuple[int, str, str]):
        ...
    
    x = MyTuple()
    reveal_type(x) # tuple[()] !!!!

    I feel like maybe a type checker should disallow this snippet. This is what happens at runtime:

    >>> class MyTuple(tuple[int, str, str]): ...
    ...
    >>> MyTuple()
    ()

    Perhaps type checkers should disallow calls to subclasses of specialised tuples unless __new__ has been overridden?

  2. JelleZijlstra commented on Jul 12, 2022

    @JelleZijlstra
    Member

    @AlexWaygood the runtime type of MyTuple() is in fact MyTuple. I don't see why type checkers should disallow that.

    This change is from #7454. I already brought up this possible issue, but for whatever reason mypy doesn't infer the wrong type for subclasses in this case. Given that it causes a regression for pyright, I'm fine with reverting it.

  3. AlexWaygood commented on Jul 12, 2022

    @AlexWaygood
    Member

    @AlexWaygood the runtime type of MyTuple() is in fact MyTuple. I don't see why type checkers should disallow that.

    The runtime type is MyTuple, yes, but it's not a subtype of tuple[int, str, str]. My concern is about situations like this:

    class MyTuple(tuple[int, str, str]):
        ...
    
    x = MyTuple()  # type checker is happy with this, infers type of x as MyTuple
    
    # type checker is happy with this due to x having inferred type of MyTuple,
    # and due to MyTuple being a subtype of tuple[int, str, str].
    # However, this fails at runtime.
    a, b, c = x

    That feels pretty unsafe to me.

    But I'm fine with reverting #7454 for now; I agree that it doesn't bring much benefit.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions