mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 06:11:43 +00:00

## Summary This PR extends our [PEP 695](https://peps.python.org/pep-0695) handling from the type aliases handled by `UP040` to generic function and class parameters, as suggested in the latter two examples from #4617: ```python # Input T = TypeVar("T", bound=float) class A(Generic[T]): ... def f(t: T): ... # Output class A[T: float]: ... def f[T: float](t: T): ... ``` I first implemented this as part of `UP040`, but based on a brief discussion during a very helpful pairing session with @AlexWaygood, I opted to split them into rules separate from `UP040` and then also separate from each other. From a quick look, and based on [this issue](https://github.com/asottile/pyupgrade/issues/836), I'm pretty sure neither of these rules is currently in pyupgrade, so I just took the next available codes, `UP046` and `UP047`. The last main TODO, noted in the rule file and in the fixture, is to handle generic method parameters not included in the class itself, `S` in this case: ```python T = TypeVar("T") S = TypeVar("S") class Foo(Generic[T]): def bar(self, x: T, y: S) -> S: ... ``` but Alex mentioned that that might be okay to leave for a follow-up PR. I also left a TODO about handling multiple subclasses instead of bailing out when more than one is present. I'm not sure how common that would be, but I can still handle it here, or follow up on that too. I think this is unrelated to the PR, but when I ran `cargo dev generate-all`, it removed the rule code `PLW0101` from `ruff.schema.json`. It seemed unrelated, so I left that out, but I wanted to mention it just in case. ## Test Plan New test fixture, `cargo nextest run` Closes #4617, closes #12542 --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
59 lines
1.3 KiB
Python
59 lines
1.3 KiB
Python
from collections.abc import Callable
|
|
from typing import Any, AnyStr, ParamSpec, TypeVar, TypeVarTuple
|
|
|
|
from somewhere import Something
|
|
|
|
S = TypeVar("S", str, bytes) # constrained type variable
|
|
T = TypeVar("T", bound=float)
|
|
Ts = TypeVarTuple("Ts")
|
|
P = ParamSpec("P")
|
|
|
|
|
|
def f(t: T) -> T:
|
|
return t
|
|
|
|
|
|
def g(ts: tuple[*Ts]) -> tuple[*Ts]:
|
|
return ts
|
|
|
|
|
|
def h(
|
|
p: Callable[P, T],
|
|
# Comment in the middle of a parameter list should be preserved
|
|
another_param,
|
|
and_another,
|
|
) -> Callable[P, T]:
|
|
return p
|
|
|
|
|
|
def i(s: S) -> S:
|
|
return s
|
|
|
|
|
|
# NOTE this case is the reason the fix is marked unsafe. If we can't confirm
|
|
# that one of the type parameters (`Something` in this case) is a TypeVar,
|
|
# which we can't do across module boundaries, we will not convert it to a
|
|
# generic type parameter. This leads to code that mixes old-style standalone
|
|
# TypeVars with the new-style generic syntax and will be rejected by type
|
|
# checkers
|
|
def broken_fix(okay: T, bad: Something) -> tuple[T, Something]:
|
|
return (okay, bad)
|
|
|
|
|
|
def any_str_param(s: AnyStr) -> AnyStr:
|
|
return s
|
|
|
|
|
|
# these cases are not handled
|
|
|
|
# TODO(brent) default requires 3.13
|
|
V = TypeVar("V", default=Any, bound=str)
|
|
|
|
|
|
def default_var(v: V) -> V:
|
|
return v
|
|
|
|
|
|
def outer():
|
|
def inner(t: T) -> T:
|
|
return t
|