Fix overload resolution for type aliases to Any#21345
Fix overload resolution for type aliases to Any#21345pablogsal wants to merge 4 commits intopython:masterfrom
Conversation
8b882e3 to
398d70e
Compare
This comment has been minimized.
This comment has been minimized.
Aliases like `Incomplete: TypeAlias = Any` were being downgraded to TypeOfAny.special_form, the same flavor used for NewType-style higher-kinded types. has_any_type ignores special_form Anys when checking for overload ambiguity, which meant a 2-arg call to os.environ.get with an alias-Any default would silently match the `default: None = None` overload and return `str | None` instead of Any. Add a dedicated TypeOfAny.from_alias_target flavor that suppresses --disallow-any-explicit at use sites (the original reason for the downgrade) but still counts as a real Any everywhere else. Stats reporting and the Literal[...] arg check are extended to keep their existing behavior for the new flavor.
for more information, see https://pre-commit.ci
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Thank you! This makes some amount of sense and I dislike how mypy's current behaviour seemingly breaks referential transparency pretty badly
But this is a lot of primer diff and would be disruptive for users, so I think we might have to explore a few other options or additional changes
I don't have a strong opinion here so happy to work on whatever direction you think makes sense :) |
Hnasar
left a comment
There was a problem hiding this comment.
Thanks for this fix -- it seems more correct! And I agree, much of the primer diff looks reasonable, like the trio changes, and the new unused-ignores, but I'm concerned with the numpy and pandas regressions which seem like they could be related to Any-in-Generics?
E.g.
static-frame (https://github.com/static-frame/static-frame)
+ static_frame/core/util.py:1807: error: Returning Any from function declared to return "ndarray[Any, Any]" [no-any-return]Looking https://github.com/static-frame/static-frame/blob/master/static_frame/core/util.py#L1807 is returning values.T where values: TNDArrayAny and TNDArrayAny = np.ndarray[tp.Any, tp.Any].
And here's the (reasonable) definition of ndarray.T
So I'm not sure where this regression is coming from. Perhaps we need to update some usage of TypeOfAny.special_form to account for the new TypeOfAny.from_alias_target?
(Perhaps the regression in this numpy/pandas usage could be demonstrated in a test to narrow (ha!) it down?)
| @@ -483,7 +483,7 @@ def is_complex(t: Type) -> bool: | |||
|
|
|||
|
|
|||
| def is_special_form_any(t: AnyType) -> bool: | |||
There was a problem hiding this comment.
nit: name is now incongruous with body. suggest: is_special_form_or_alias_any
|
I was playing around with a simpler diff that looked like: Seemed pretty similar to this PR. In particular, it also hit |
Thanks for checking! I can investigate more tomorrow indeed |
|
I investigated the pandas-stubs / numpy-ish primer regressions. I think the issue is that this change affects aliases involving For pandas-stubs operator overloads, that means calls that used to infer A small reduced version is: from typing import Any, Generic, TypeAlias, TypeVar, overload, Sequence
from typing_extensions import Never
S = TypeVar("S")
Sh = TypeVar("Sh")
A: TypeAlias = Any
class Array(Generic[Sh, S]): pass
Shape: TypeAlias = tuple[Any, ...]
ArrBool: TypeAlias = Array[Shape, bool]
class Series(Generic[S]):
@overload
def __add__(self: Series[Never], other: complex | Sequence[Any]) -> Series[Any]: ...
@overload
def __add__(self: Series[S], other: Array[Any, S]) -> Series[S]: ...
@overload
def __add__(self: Series[S], other: Array[Any, bool]) -> Series[S]: ...
@overload
def __add__(self: Series[bool], other: Array[Any, bool]) -> Series[bool]: ...
@overload
def __add__(self: Series[str], other: Array[Any, bool]) -> Never: ...
def __add__(self, other): ...
def f(x: Series[A], y: ArrBool):
reveal_type(x + y)On master, this reveals So I think the fix direction is to preserve the new “real Any” behavior only for aliases whose target is exactly Any / another alias to exactly Any, while keeping nested explicit Anys inside larger alias targets as special_form for the old overload-ambiguity behavior. |
This comment has been minimized.
This comment has been minimized.
|
Will take a look at the primer later to see what's going on with these other changes |
for more information, see https://pre-commit.ci
|
Diff from mypy_primer, showing the effect of this PR on open source code: colour (https://github.com/colour-science/colour)
- colour/utilities/metrics.py:88: error: Incompatible return value type (got "floating[_16Bit] | floating[_32Bit] | float64", expected "ndarray[tuple[Any, ...], dtype[floating[_16Bit] | floating[_32Bit] | float64]]") [return-value]
|
This looks like a legitimate error as Your call @hauntsaninja |
Aliases like
Incomplete: TypeAlias = Anywere being downgraded toTypeOfAny.special_form, the same flavor used forNewType-style higher-kinded types.has_any_typeignoresspecial_formAnys when checking for overload ambiguity, which meant a 2-arg call toos.environ.getwith an alias-Anydefault would silently match thedefault: None = Noneoverload and returnstr | Noneinstead ofAny. No buenoAdd a dedicated
TypeOfAny.from_alias_targetflavor that suppresses--disallow-any-explicitat use sites (the original reason for the downgrade) but still counts as a realAnyeverywhere else. Stats reporting and theLiteral[...]arg check are extended to keep their existing behavior for the new flavor.Fixes #21344