Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lenient TypeVarTuple checking when number of dimensions is unknown #18665

Open
JukkaL opened this issue Feb 12, 2025 · 2 comments
Open

Lenient TypeVarTuple checking when number of dimensions is unknown #18665

JukkaL opened this issue Feb 12, 2025 · 2 comments
Labels
feature needs discussion topic-pep-646 PEP 646 (TypeVarTuple, Unpack)

Comments

@JukkaL
Copy link
Collaborator

JukkaL commented Feb 12, 2025

Currently this generates an error:

from typing import Any

class C[*Ts, S]:
    pass

a = C[int, str]()
a = C[*tuple[Any, ...], str]()  # Error

This is the output:

error: Incompatible types in assignment (expression has type "C[*tuple[Any, ...], str]", variable has type "C[int, str]")

However, this doesn't generate an error:

from typing import Any

class C[*Ts, S]:
    pass

a = C[int, str]()
a = C[*tuple[Any, ...]]()  # No error

I'd argue that the first example shouldn't generate an error either. One possible rule would be to allow this as long as some substitution of the *tuple[Any, ...] part would match the target type.

We could possibly also generalize the matching of unknown-length TypeVarTuple type arguments when the unknown-length part has a non-Any tuple item type. This could reduce apparent false positives. Example:

from typing import Any

class C[*Ts, S]:
    pass

a = C[int, int]()
a = C[*tuple[int, ...]]()  # No error?

This would only change the behavior of variadic generics. Variable-length tuples would continue to behave as they behave currently, i.e. tuple[int, ...] wouldn't be assignable to tuple[int, int]. The tuple behavior has been around for a very long time, so it doesn't make sense to change it, but variadic generics are a relatively new and untested feature.

The primary motivation is help with NumPy and libraries that provide multidimensional array-like types.

Proper subtype checks should continue to work as they work currently, since we can't safely simplify say C[int] | C[*tuple[Any, ...]].

cc @ilevkivskyi who I chatted about this recently

@jorenham
Copy link

The primary motivation is help with NumPy and libraries that provide multidimensional array-like types.

I can see how this could indeed help with shape-typing in NumPy. But the main reason that we're not using TypeVarTuple for this, is because it doesn't support setting a bound. So in order for NumPy to be able to benefit from this proposed change, it would also require a PEP that would add the bound support to TypeVarTuple (since it requires a syntax change).

So until then, we'll be using an old-fashioned ShapeT: tuple[int, ...] to type the shapes of our arrays. But it's far from ideal, and leads to verbose annotations and spaghetti error messages.

Anyway, I guess I'm trying to say that this change won't be enough for NumPy to be able to use TypeVarTuple, but it's certainly helpful!

@ilevkivskyi
Copy link
Member

The case with Any should be easy to implement, and I totally agree with it. The case with non-Any types is harder because we will need to special case instances vs tuples. I don't have a strong opinion on the latter, may do this later, if it is helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature needs discussion topic-pep-646 PEP 646 (TypeVarTuple, Unpack)
Projects
None yet
Development

No branches or pull requests

3 participants