Skip to content

Unable to overload __new__ method with TypeVar input  #15220

@spacether

Description

@spacether

Bug Report

Unable to overload new method with generic input

To Reproduce
How can I type hint my __new__ implementation using a generic input?

So I have a class that can store str or decimal.Decimal instances, and it subclasses the input str or decimal.Decimal class.

This code works in mypy and python:

from __future__ import annotations
import typing
import typing_extensions
import decimal
import random

T = typing.TypeVar('T', str, decimal.Decimal)

class StrOrDecimal(typing.Generic[T]):
    @typing.overload
    def __new__(cls, arg: decimal.Decimal) -> StrOrDecimalTyped.Decimal.StrOrDecimal[decimal.Decimal]:
        ...
    @typing.overload
    def __new__(cls, arg: str) -> StrOrDecimalTyped.Str.StrOrDecimal[str]:
        ...
    # def __new__(cls, arg: typing.Union[str, decimal.Decimal]): does not work
    def __new__(cls, arg):  # works
        if isinstance(arg, decimal.Decimal):
            return super(cls, StrOrDecimalTyped.Decimal.StrOrDecimal).__new__(StrOrDecimalTyped.Decimal.StrOrDecimal, arg)
        if isinstance(arg, str):
            return super(cls, StrOrDecimalTyped.Str.StrOrDecimal).__new__(StrOrDecimalTyped.Str.StrOrDecimal, arg)
        raise ValueError('Invalid value')

_StrOrDecimal = StrOrDecimal
# needed for mypy
class StrOrDecimalTyped:
    class Str:
        class StrOrDecimal(_StrOrDecimal[T], str):
            pass
    class Decimal:
        class StrOrDecimal(_StrOrDecimal[T], decimal.Decimal):
            pass

a_val = 'a'
a = StrOrDecimal(a_val)
print(a)

b_val = decimal.Decimal('1.2')
b = StrOrDecimal(b_val)
print(b)

choices: typing.List[typing.Union[str, decimal.Decimal]] = [a_val, b_val]
c_val = random.choice(choices)
c = StrOrDecimal(c_val)

try:
    reveal_type(a)
    reveal_type(b)
    reveal_type(c)
except NameError:
    typing_extensions.reveal_type(a)
    typing_extensions.reveal_type(b)
    typing_extensions.reveal_type(c)

But if I use either of these signatures:

def __new__(cls, arg: typing.Union[str, decimal.Decimal]):
def __new__(cls, arg: T):

I get this error:

generic.py:19: error: Argument 1 to "__new__" of "StrOrDecimal" has incompatible type "Type[generic.StrOrDecimalTyped.Decimal.StrOrDecimal[Any]]"; expected "Type[generic.StrOrDecimal[T]]"

How can I fix it?

Expected Behavior

I expect to be able to use T as the input type

Actual Behavior

Mypy throws the above error

Your Environment

  • Mypy version used:
  • Mypy command-line flags:
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used:

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