ruff/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP046_0.py
Brent Westbrook ce8110332c
[pyupgrade] Handle multiple base classes for PEP 695 generics (UP046) (#15659)
## Summary

Addresses the second follow up to #15565 in #15642. This was easier than
expected by using this cool destructuring syntax I hadn't used before,
and by assuming
[PYI059](https://docs.astral.sh/ruff/rules/generic-not-last-base-class/)
(`generic-not-last-base-class`).

## Test Plan

Using an existing test, plus two new tests combining multiple base
classes and multiple generics. It looks like I deleted a relevant test,
which I did, but I meant to rename this in #15565. It looks like instead
I copied it and renamed the copy.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-22 20:19:13 -05:00

136 lines
2.7 KiB
Python

from typing import Any, AnyStr, Generic, ParamSpec, TypeVar, TypeVarTuple
from somewhere import SupportsRichComparisonT
S = TypeVar("S", str, bytes) # constrained type variable
T = TypeVar("T", bound=float)
Ts = TypeVarTuple("Ts")
P = ParamSpec("P")
class A(Generic[T]):
# Comments in a class body are preserved
var: T
class B(Generic[*Ts]):
var: tuple[*Ts]
class C(Generic[P]):
var: P
class Constrained(Generic[S]):
var: S
# This case gets a diagnostic but not a fix because we can't look up the bounds
# or constraints on the TypeVar imported from another module
class ExternalType(Generic[T, SupportsRichComparisonT]):
var: T
compare: SupportsRichComparisonT
# typing.AnyStr is a common external type variable, so treat it specially as a
# known TypeVar
class MyStr(Generic[AnyStr]):
s: AnyStr
class MultipleGenerics(Generic[S, T, *Ts, P]):
var: S
typ: T
tup: tuple[*Ts]
pep: P
class MultipleBaseClasses(list, Generic[T]):
var: T
# these are just for the MoreBaseClasses and MultipleBaseAndGenerics cases
class Base1: ...
class Base2: ...
class Base3: ...
class MoreBaseClasses(Base1, Base2, Base3, Generic[T]):
var: T
class MultipleBaseAndGenerics(Base1, Base2, Base3, Generic[S, T, *Ts, P]):
var: S
typ: T
tup: tuple[*Ts]
pep: P
class A(Generic[T]): ...
class B(A[S], Generic[S]):
var: S
class C(A[S], Generic[S, T]):
var: tuple[S, T]
class D(A[int], Generic[T]):
var: T
class NotLast(Generic[T], Base1):
var: T
class Sandwich(Base1, Generic[T], Base2):
var: T
# runtime `TypeError` to inherit from `Generic` multiple times, but we still
# emit a diagnostic
class TooManyGenerics(Generic[T], Generic[S]):
var: T
var: S
# These cases are not handled
class D(Generic[T, T]): # duplicate generic variable, runtime error
pass
# TODO(brent) we should also apply the fix to methods, but it will need a
# little more work. these should be left alone for now but be fixed eventually.
class NotGeneric:
# -> generic_method[T: float](t: T)
def generic_method(t: T) -> T:
return t
# This one is strange in particular because of the mix of old- and new-style
# generics, but according to the PEP, this is okay "if the class, function, or
# type alias does not use the new syntax." `more_generic` doesn't use the new
# syntax, so it can use T from the module and U from the class scope.
class MixedGenerics[U]:
def more_generic(u: U, t: T) -> tuple[U, T]:
return (u, t)
# TODO(brent) default requires 3.13
V = TypeVar("V", default=Any, bound=str)
class DefaultTypeVar(Generic[V]): # -> [V: str = Any]
var: V
# nested classes and functions are skipped
class Outer:
class Inner(Generic[T]):
var: T