Skip to content

Respect explicit return type of __new__()#21441

Open
ilevkivskyi wants to merge 7 commits intopython:masterfrom
ilevkivskyi:new-new
Open

Respect explicit return type of __new__()#21441
ilevkivskyi wants to merge 7 commits intopython:masterfrom
ilevkivskyi:new-new

Conversation

@ilevkivskyi
Copy link
Copy Markdown
Member

With this PR will still give an error if the explicit return __new__() is not a subtype of current class, but now we will actually use it. There are two exceptions (to preserve backwards compatibility):

  • If the return type is Any with still use current class as the return type.
  • If the explicit return type comes from a superclass and is a supertype of implicit return type.
class A:
    def __new__(cls): ...
reveal_type(A())  # still __main__.A

class B:
    def __new__(cls) -> B:
        return cls()
class C(B): ...
reveal_type(C())  # still __main__.C

This uses a more principled implementation than some earlier attempts: adding a new dedicated attribute to CallableType for this purpose. Some comments:

  • This PR has a bit for boilerplate, but this is expected. When adding a new attribute to a type, one needs to update most visitors.
  • While doing the above I noticed that CallableType.type_guard and CallableType.type_is were not handled in dependency visitors (neither coarse-grained nor fine-grained). IMO they definitely should be handled there, so I now handle them.
  • I try to reduce the size of CallableType a bit to compensate for new attribute by removing (rarely used) min_args attribute. I also use compact flags serialization.
  • I skimmed the code base and updated all places where we "casually" use CallableType.ret_type for various type object edge cases.
  • Note that I don't assert that instance_type is set when is_type_obj() returns True. This is mostly to avoid breaking 3rd party plugins.
  • I didn't add a test case for each edge case, but I added (improved) test cases from Believe more __new__ return types #16020 plus some more. Suggestions for more test cases are welcome.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

Diff from mypy_primer, showing the effect of this PR on open source code:

zope.interface (https://github.com/zopefoundation/zope.interface)
- src/zope/interface/common/tests/test_collections.py:88: error: Incompatible types in assignment (expression has type "set[type[array[int]]]", base class "VerifyClassMixin" defined the type as "tuple[()]")  [assignment]
+ src/zope/interface/common/tests/test_collections.py:88: error: Incompatible types in assignment (expression has type "set[type[array[_T]]]", base class "VerifyClassMixin" defined the type as "tuple[()]")  [assignment]

comtypes (https://github.com/enthought/comtypes)
- comtypes/server/register.py:226: error: Incompatible types in assignment (expression has type "reversed[tuple[int, str]]", variable has type "list[tuple[int, str]]")  [assignment]
+ comtypes/server/register.py:226: error: Incompatible types in assignment (expression has type "Iterator[tuple[int, str]]", variable has type "list[tuple[int, str]]")  [assignment]

discord.py (https://github.com/Rapptz/discord.py)
- discord/abc.py:1815: error: Incompatible types in assignment (expression has type "reversed[MessagePin]", variable has type "list[MessagePin]")  [assignment]
+ discord/abc.py:1815: error: Incompatible types in assignment (expression has type "Iterator[MessagePin]", variable has type "list[MessagePin]")  [assignment]

pandera (https://github.com/pandera-dev/pandera)
+ tests/pandas/test_model.py:1797: error: Argument 1 to "from_records" of "DataFrame" has incompatible type "type[Schema]"; expected "type[DataFrameBase[Schema]]"  [arg-type]

pandas-stubs (https://github.com/pandas-dev/pandas-stubs)
+ tests/arrays/test_numpy_.py:60: error: Expression is of type "Any", not "NumpyExtensionArray"  [assert-type]

sympy (https://github.com/sympy/sympy)
+ sympy/core/evalf.py:997: error: Argument 1 to "evalf_add" has incompatible type "Expr"; expected "Add"  [arg-type]
+ sympy/core/function.py:449: error: Unused "type: ignore" comment  [unused-ignore]
+ sympy/core/function.py:959: error: Argument 1 to "pickle" has incompatible type "type[UndefinedFunction]"; expected "type[type[AppliedUndef]]"  [arg-type]
+ sympy/solvers/ode/single.py:1985: error: "Expr" has no attribute "name"  [attr-defined]
+ sympy/printing/smtlib.py:188: error: Argument 1 to "_print_Unequality" of "SMTLibPrinter" has incompatible type "Unequality | BooleanFalse | BooleanTrue"; expected "Unequality"  [arg-type]
+ sympy/printing/smtlib.py:567: error: Item "BooleanFalse" of "Equality | BooleanFalse | BooleanTrue" has no attribute "lhs"  [union-attr]
+ sympy/printing/smtlib.py:567: error: Item "BooleanTrue" of "Equality | BooleanFalse | BooleanTrue" has no attribute "lhs"  [union-attr]
+ sympy/printing/smtlib.py:567: error: Item "BooleanFalse" of "Equality | BooleanFalse | BooleanTrue" has no attribute "rhs"  [union-attr]
+ sympy/printing/smtlib.py:567: error: Item "BooleanTrue" of "Equality | BooleanFalse | BooleanTrue" has no attribute "rhs"  [union-attr]
+ sympy/printing/smtlib.py:569: error: Item "BooleanFalse" of "Equality | BooleanFalse | BooleanTrue" has no attribute "rhs"  [union-attr]
+ sympy/printing/smtlib.py:569: error: Item "BooleanTrue" of "Equality | BooleanFalse | BooleanTrue" has no attribute "rhs"  [union-attr]
+ sympy/printing/smtlib.py:569: error: Item "BooleanFalse" of "Equality | BooleanFalse | BooleanTrue" has no attribute "lhs"  [union-attr]
+ sympy/printing/smtlib.py:569: error: Item "BooleanTrue" of "Equality | BooleanFalse | BooleanTrue" has no attribute "lhs"  [union-attr]

pandas (https://github.com/pandas-dev/pandas)
+ pandas/tseries/frequencies.py:298: error: Incompatible return value type (got "Timestamp | NaTType", expected "Timestamp")  [return-value]
- pandas/core/dtypes/cast.py:698: error: Non-overlapping identity check (left operand type: "Timestamp", right operand type: "NaTType")  [comparison-overlap]
- pandas/core/dtypes/cast.py:711: error: Non-overlapping identity check (left operand type: "Timedelta", right operand type: "NaTType")  [comparison-overlap]
+ pandas/core/dtypes/cast.py:159: error: Incompatible types in assignment (expression has type "Timestamp | NaTType", variable has type "str | bytes | date | datetime | timedelta | <7 more items> | Interval[Any] | complex | integer[Any] | floating[Any] | complexfloating[Any, Any]")  [assignment]
+ pandas/core/dtypes/cast.py:161: error: Incompatible types in assignment (expression has type "Timedelta | NaTType", variable has type "str | bytes | date | datetime | timedelta | <7 more items> | Interval[Any] | complex | integer[Any] | floating[Any] | complexfloating[Any, Any]")  [assignment]
+ pandas/core/arrays/_ranges.py:147: error: Incompatible types in assignment (expression has type "Timestamp | NaTType", variable has type "Timestamp | None")  [assignment]
+ pandas/core/arrays/datetimes.py:832: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "unit"  [union-attr]
+ pandas/core/arrays/datetimes.py:3174: error: Incompatible types in assignment (expression has type "Timestamp | NaTType", variable has type "Timestamp | None")  [assignment]
+ pandas/core/arrays/datetimes.py:3174: note: Error code "assignment" not covered by "type: ignore[arg-type]" comment
- pandas/core/arrays/datetimes.py:3175: error: Non-overlapping identity check (left operand type: "Timestamp", right operand type: "NaTType")  [comparison-overlap]
+ pandas/core/arrays/datetimes.py:3175: error: Non-overlapping identity check (left operand type: "Timestamp | None", right operand type: "NaTType")  [comparison-overlap]
+ pandas/core/arrays/datetimes.py:3176: error: Item "None" of "Timestamp | None" has no attribute "as_unit"  [union-attr]
+ pandas/core/arrays/datetimes.py:3182: error: Incompatible types in assignment (expression has type "Timestamp | NaTType", variable has type "Timestamp | None")  [assignment]
+ pandas/core/arrays/datetimes.py:3182: note: Error code "assignment" not covered by "type: ignore[arg-type]" comment
- pandas/core/arrays/datetimes.py:3183: error: Non-overlapping identity check (left operand type: "Timestamp", right operand type: "NaTType")  [comparison-overlap]
+ pandas/core/arrays/datetimes.py:3183: error: Non-overlapping identity check (left operand type: "Timestamp | None", right operand type: "NaTType")  [comparison-overlap]
+ pandas/core/arrays/datetimes.py:3184: error: Item "None" of "Timestamp | None" has no attribute "as_unit"  [union-attr]
+ pandas/core/arrays/period.py:385: error: Incompatible return value type (got "Period | NaTType", expected "Period")  [return-value]
+ pandas/core/arrays/period.py:1227: error: Argument 1 to "_add_timedelta_arraylike" of "PeriodArray" has incompatible type "ndarray[tuple[Any, ...], dtype[generic[date | int | timedelta | None]]]"; expected "TimedeltaArray | ndarray[tuple[Any, ...], dtype[timedelta64[timedelta | int | None]]]"  [arg-type]
+ pandas/core/arrays/period.py:1342: error: Argument 1 to "delta_to_tick" has incompatible type "Timedelta | NaTType"; expected "timedelta"  [arg-type]
+ pandas/core/window/rolling.py:2023: error: Incompatible types in assignment (expression has type "int | signedinteger[_64Bit]", variable has type "int | None")  [assignment]
+ pandas/core/tools/datetimes.py:608: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_julian_date"  [union-attr]
+ pandas/core/tools/datetimes.py:654: error: No overload variant of "__floordiv__" of "Timedelta" matches argument type "NaTType"  [operator]
+ pandas/core/tools/datetimes.py:654: note: Possible overload variants:
+ pandas/core/tools/datetimes.py:654: note:     def __floordiv__(self, timedelta, /) -> int
+ pandas/core/tools/datetimes.py:654: note:     def __floordiv__(self, float, /) -> Timedelta
+ pandas/core/tools/datetimes.py:654: note:     def __floordiv__(self, ndarray[tuple[Any, ...], dtype[timedelta64[timedelta | int | None]]], /) -> ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit | _64Bit]]]
+ pandas/core/tools/datetimes.py:654: note:     def __floordiv__(self, ndarray[tuple[Any, ...], dtype[number[Any, int | float | complex]]], /) -> ndarray[tuple[Any, ...], dtype[timedelta64[timedelta | int | None]]] | Timedelta
+ pandas/core/tools/datetimes.py:654: note: Both left and right operands are unions
+ pandas/core/indexes/timedeltas.py:324: error: Argument 1 to "to_offset" has incompatible type "Timedelta | NaTType"; expected "timedelta | str | PeriodDtypeBase"  [arg-type]
+ pandas/core/indexes/timedeltas.py:324: note: Error code "arg-type" not covered by "type: ignore[operator]" comment
+ pandas/core/indexes/period.py:673: error: Incompatible return value type (got "Period | NaTType", expected "Period")  [return-value]
+ pandas/core/indexes/period.py:700: error: Item "NaTType" of "Period | NaTType" has no attribute "asfreq"  [union-attr]
+ pandas/core/indexes/datetimes.py:943: error: Unsupported left operand type for % ("NaTType")  [operator]
+ pandas/core/indexes/datetimes.py:943: note: Left operand is of type "Timedelta | NaTType"
+ pandas/core/indexes/datetimes.py:1120: error: Item "NaTType" of "Period | NaTType" has no attribute "start_time"  [union-attr]
+ pandas/core/indexes/datetimes.py:1123: error: Unsupported operand types for + ("NaTType" and "int")  [operator]
+ pandas/core/indexes/datetimes.py:1123: note: Left operand is of type "Period | NaTType"
+ pandas/core/indexes/datetimes.py:1148: error: Incompatible types in assignment (expression has type "Timestamp | NaTType", variable has type "datetime")  [assignment]
+ pandas/core/indexes/datetimes.py:1154: error: "datetime" has no attribute "tz_localize"  [attr-defined]
+ pandas/core/indexes/datetimes.py:1156: error: Incompatible return value type (got "tuple[datetime, Resolution]", expected "tuple[Timestamp, Resolution]")  [return-value]
+ pandas/core/indexes/datetimes.py:1735: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "unit"  [union-attr]
+ pandas/core/indexes/datetimes.py:1736: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "unit"  [union-attr]
+ pandas/core/indexes/datetimelike.py:1107: error: Argument 1 to "to_offset" has incompatible type "Timedelta | NaTType"; expected "timedelta | str | PeriodDtypeBase"  [arg-type]
+ pandas/core/resample.py:2524: error: Incompatible types in assignment (expression has type "Timestamp | NaTType", variable has type "Literal['epoch', 'start', 'start_day', 'end', 'end_day'] | Timestamp")  [assignment]
+ pandas/core/resample.py:2648: error: Argument "offset" to "_get_timestamp_range_edges" has incompatible type "Timedelta | NaTType | None"; expected "Timedelta | None"  [arg-type]
+ pandas/core/resample.py:2840: error: Argument "offset" to "_get_period_range_edges" has incompatible type "Timedelta | NaTType | None"; expected "Timedelta | None"  [arg-type]
+ pandas/core/resample.py:2959: error: Incompatible types in assignment (expression has type "Timestamp | NaTType", variable has type "Literal['epoch', 'start', 'start_day', 'end', 'end_day'] | Timestamp")  [assignment]
+ pandas/core/resample.py:2975: error: Incompatible types in assignment (expression has type "Timestamp | NaTType", variable has type "Timestamp")  [assignment]
+ pandas/core/resample.py:2977: error: Incompatible types in assignment (expression has type "Timestamp | NaTType", variable has type "Timestamp")  [assignment]
+ pandas/core/resample.py:2979: error: Incompatible types in assignment (expression has type "Timestamp | NaTType", variable has type "Timestamp")  [assignment]
+ pandas/core/resample.py:3091: error: No overload variant of "__mul__" of "signedinteger" matches argument type "Tick"  [operator]
+ pandas/core/resample.py:3091: note: Possible overload variants:
+ pandas/core/resample.py:3091: note:     def __mul__(self, int | signedinteger[_8Bit] | numpy.bool[builtins.bool] | signedinteger[_64Bit], /) -> signedinteger[_64Bit]
+ pandas/core/resample.py:3091: note:     def __mul__(self, float, /) -> float64
+ pandas/core/resample.py:3091: note:     def __mul__(self, complex, /) -> complex128
+ pandas/core/resample.py:3091: note:     def __mul__(self, signedinteger[Any], /) -> signedinteger[Any]
+ pandas/core/resample.py:3091: note:     def __mul__(self, integer[Any], /) -> Any
+ pandas/core/resample.py:3091: note: Left operand is of type "int | signedinteger[_64Bit]"
+ pandas/core/resample.py:3139: error: Incompatible return value type (got "tuple[Timestamp | NaTType, Timestamp | NaTType]", expected "tuple[Timestamp, Timestamp]")  [return-value]
+ pandas/io/excel/_odfreader.py:220: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "time"  [union-attr]
+ pandas/tseries/holiday.py:262: error: Incompatible types in assignment (expression has type "Timestamp | NaTType | None", variable has type "Timestamp | None")  [assignment]
+ pandas/tseries/holiday.py:264: error: Incompatible types in assignment (expression has type "Timestamp | NaTType | None", variable has type "Timestamp | None")  [assignment]
+ pandas/tests/tslibs/test_timedeltas.py:24: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_pytimedelta"  [union-attr]
+ pandas/tests/tseries/offsets/test_custom_business_hour.py:277: error: Unsupported operand types for - ("NaTType" and "Nano")  [operator]
+ pandas/tests/tseries/offsets/test_custom_business_hour.py:277: note: Left operand is of type "Timestamp | NaTType"
+ pandas/tests/tseries/offsets/test_custom_business_hour.py:278: error: Unsupported operand types for - ("NaTType" and "Nano")  [operator]
+ pandas/tests/tseries/offsets/test_custom_business_hour.py:278: note: Left operand is of type "Timestamp | NaTType"
+ pandas/tests/tseries/offsets/test_custom_business_hour.py:288: error: Unsupported operand types for - ("NaTType" and "Nano")  [operator]
+ pandas/tests/tseries/offsets/test_custom_business_hour.py:288: note: Left operand is of type "Timestamp | NaTType"
+ pandas/tests/tseries/offsets/test_custom_business_hour.py:289: error: Unsupported operand types for - ("NaTType" and "Nano")  [operator]
+ pandas/tests/tseries/offsets/test_custom_business_hour.py:289: note: Left operand is of type "Timestamp | NaTType"
+ pandas/tests/tseries/offsets/test_business_day.py:78: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_pytimedelta"  [union-attr]
+ pandas/tests/tseries/offsets/test_business_day.py:79: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_timedelta64"  [union-attr]
+ pandas/tests/series/indexing/test_setitem.py:512: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_timedelta64"  [union-attr]
+ pandas/tests/series/indexing/test_setitem.py:513: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_pytimedelta"  [union-attr]
+ pandas/tests/scalar/test_nat.py:557: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_pytimedelta"  [union-attr]
+ pandas/tests/scalar/test_nat.py:558: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_timedelta64"  [union-attr]
+ pandas/tests/scalar/timedelta/test_timedelta.py:266: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "max"  [union-attr]
+ pandas/tests/scalar/interval/test_arithmetic.py:116: error: Value of type variable "_OrderableT" of "Interval" cannot be "Timestamp | NaTType"  [type-var]
+ pandas/tests/scalar/interval/test_arithmetic.py:119: error: Value of type variable "_OrderableT" of "Interval" cannot be "Timedelta | NaTType"  [type-var]
+ pandas/tests/indexes/test_engines.py:62: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_pytimedelta"  [union-attr]
+ pandas/tests/indexes/test_engines.py:63: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_timedelta64"  [union-attr]
+ pandas/tests/groupby/methods/test_rank.py:72: error: List item 0 has incompatible type "list[Timestamp | NaTType]"; expected "ndarray[tuple[Any, ...], dtype[Any]]"  [list-item]
+ pandas/tests/groupby/methods/test_rank.py:79: error: List item 1 has incompatible type "list[Timestamp | NaTType]"; expected "ndarray[tuple[Any, ...], dtype[Any]]"  [list-item]
+ pandas/tests/groupby/methods/test_rank.py:86: error: List item 2 has incompatible type "list[Timedelta | NaTType]"; expected "ndarray[tuple[Any, ...], dtype[Any]]"  [list-item]
+ pandas/tests/groupby/methods/test_rank.py:93: error: List item 3 has incompatible type "list[Period | Any]"; expected "ndarray[tuple[Any, ...], dtype[Any]]"  [list-item]
+ pandas/tests/groupby/methods/test_rank.py:94: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/groupby/methods/test_rank.py:95: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/groupby/methods/test_rank.py:96: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/groupby/methods/test_rank.py:97: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/groupby/methods/test_rank.py:98: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/groupby/methods/test_rank.py:202: error: List item 0 has incompatible type "list[float | Timestamp | NaTType]"; expected "ndarray[tuple[Any, ...], dtype[Any]]"  [list-item]
+ pandas/tests/groupby/methods/test_rank.py:212: error: List item 1 has incompatible type "list[float | Timestamp | NaTType]"; expected "ndarray[tuple[Any, ...], dtype[Any]]"  [list-item]
+ pandas/tests/groupby/methods/test_rank.py:222: error: List item 2 has incompatible type "list[float | Timedelta | NaTType]"; expected "ndarray[tuple[Any, ...], dtype[Any]]"  [list-item]
+ pandas/tests/groupby/methods/test_rank.py:232: error: List item 3 has incompatible type "list[float | Period | Any]"; expected "ndarray[tuple[Any, ...], dtype[Any]]"  [list-item]
+ pandas/tests/groupby/methods/test_rank.py:233: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/groupby/methods/test_rank.py:234: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/groupby/methods/test_rank.py:236: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/groupby/methods/test_rank.py:237: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/groupby/methods/test_rank.py:238: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/arrays/test_timedeltas.py:215: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_timedelta64"  [union-attr]
+ pandas/tests/arrays/test_timedeltas.py:216: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_pytimedelta"  [union-attr]
+ pandas/tests/arrays/test_timedeltas.py:238: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/arrays/test_datetimes.py:669: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "to_period"  [union-attr]
+ pandas/tests/arrays/test_datetimelike.py:187: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "time"  [union-attr]
+ pandas/tests/arithmetic/conftest.py:70: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_pytimedelta"  [union-attr]
+ pandas/tests/arithmetic/conftest.py:72: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_timedelta64"  [union-attr]
+ pandas/tests/arithmetic/conftest.py:87: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_pytimedelta"  [union-attr]
+ pandas/tests/arithmetic/conftest.py:106: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_pytimedelta"  [union-attr]
+ pandas/tests/arithmetic/conftest.py:131: error: Item "NaTType" of "Timedelta | NaTType" has no attribute "to_pytimedelta"  [union-attr]
+ pandas/plotting/_matplotlib/converter.py:153: error: Item "NaTType" of "Timestamp | NaTType" has no attribute "time"  [union-attr]

... (truncated 15 lines) ...

starlette (https://github.com/encode/starlette)
+ starlette/testclient.py:129: error: Incompatible types in assignment (expression has type "tuple[MemoryObjectSendStream[MutableMapping[str, Any]], MemoryObjectReceiveStream[MutableMapping[str, Any]]]", variable has type "create_memory_object_stream[MutableMapping[str, Any]]")  [assignment]
+ starlette/testclient.py:131: error: Incompatible types in assignment (expression has type "tuple[MemoryObjectSendStream[MutableMapping[str, Any]], MemoryObjectReceiveStream[MutableMapping[str, Any]]]", variable has type "create_memory_object_stream[MutableMapping[str, Any]]")  [assignment]
+ starlette/testclient.py:678: error: Incompatible types in assignment (expression has type "tuple[MemoryObjectSendStream[MutableMapping[str, Any] | None], MemoryObjectReceiveStream[MutableMapping[str, Any] | None]]", variable has type "create_memory_object_stream[MutableMapping[str, Any] | None]")  [assignment]
+ starlette/testclient.py:680: error: Incompatible types in assignment (expression has type "tuple[MemoryObjectSendStream[MutableMapping[str, Any]], MemoryObjectReceiveStream[MutableMapping[str, Any]]]", variable has type "create_memory_object_stream[MutableMapping[str, Any]]")  [assignment]
+ starlette/middleware/base.py:189: error: Incompatible types in assignment (expression has type "tuple[MemoryObjectSendStream[MutableMapping[str, Any]], MemoryObjectReceiveStream[MutableMapping[str, Any]]]", variable has type "create_memory_object_stream[MutableMapping[str, Any]]")  [assignment]

@ilevkivskyi ilevkivskyi requested review from JukkaL and hauntsaninja May 8, 2026 14:44
@ilevkivskyi
Copy link
Copy Markdown
Member Author

mypy_primer looks sad, but I guess this is what people want. I spot-checked the new errors, and they all look correct. The only one suspicious thing in one new Any error in pandas-stubs.

cc @Dr-Irv for all the new errors in pandas. IIUC these are because of things like this https://github.com/pandas-dev/pandas/blob/47f6ccdbec98998bd675324fb6f049794ad03acf/pandas/_libs/tslibs/timestamps.pyi#L44-L60

class Timestamp(datetime):
    ...
    def __new__(  # type: ignore[misc]
        cls: type[Self],
        # ...
    ) -> Self | NaTType: ...

@Dr-Irv
Copy link
Copy Markdown

Dr-Irv commented May 8, 2026

mypy_primer looks sad, but I guess this is what people want. I spot-checked the new errors, and they all look correct. The only one suspicious thing in one new Any error in pandas-stubs.

Yes, I agree that is suspicious.

cc @Dr-Irv for all the new errors in pandas. IIUC these are because of things like this https://github.com/pandas-dev/pandas/blob/47f6ccdbec98998bd675324fb6f049794ad03acf/pandas/_libs/tslibs/timestamps.pyi#L44-L60

class Timestamp(datetime):
    ...
    def __new__(  # type: ignore[misc]
        cls: type[Self],
        # ...
    ) -> Self | NaTType: ...

We should probably copy the PYI for Timestamp and Timedelta and NAType from pandas-stubs into the pandas source at some point to address this.

I have on my TODO list to first make the pandas source compatible with the latest release of mypy. So when these changes in this PR get released, I'll do a similar task then.

@bzoracler
Copy link
Copy Markdown
Contributor

bzoracler commented May 8, 2026

  • If the return type is Any with still use current class as the return type.
  • If the explicit return type comes from a superclass and is a supertype of implicit return type.

Would the following alternatives also preserve backwards compatibility just as well, but reflect explicit intention better?

  • If the return type is unannotated (i.e. an explicit Any is not used):

    class A:
        def __new__(cls, arg: object): ...
    
    class B:
        def __new__(cls, arg: object) -> Any: ...
    
    class C:
        def __new__(cls, arg: object) -> "bad + annotation": ...
    
    reveal_type(A())  # `__main__.A`
    reveal_type(B())  # `Any`
    reveal_type(C())  # `Any`
  • If the explicit return type comes from a superclass and the superclass's __new__'s return type is the same as the class:

    class A:
        def __new__(cls) -> A: ...
    class B(A):
        def __new__(cls) -> A: ...
    
    class C(B): ...
    class D(A): ...
    
    reveal_type(C())  # Would it be possible for this to be `__main__.A`?
    reveal_type(D())  # `__main__.D`

    This pattern came up when I was trying to write stubs for symengine.py a while back (maybe it affects SymPy too), where they have a root Expr class, and most of Expr's subclasses have a __new__ that frequently returns something that is not an instance of the subclass.

    I'm hoping that "__new__'s return type being the same as a class" would handle the vast majority of backwards compatibility cases because it was common before typing.Self was introduced.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants