Back to Ruff

PEP 695 rules (`UP040`, `UP046`, `UP047`)

crates/ruff_linter/resources/mdtest/pyupgrade/pep-695.md

0.16.14.0 KB
Original Source

PEP 695 rules (UP040, UP046, UP047)

toml
target-version = "py313"

[lint]
preview = true
select = [
    "non-pep695-type-alias",
    "non-pep695-generic-class",
    "non-pep695-generic-function",
]

Defaulted TypeVar before a non-defaulted one

In a PEP 695 type parameter list, a non-defaulted type parameter cannot follow a defaulted one, and reordering the parameters would change how positional arguments bind when subscripting the alias, class, or function. There is no valid, equivalent type parameter list in this case, so no diagnostic is emitted (see #27021).

non-pep695-type-alias (UP040)

No diagnostic for a TypeAlias annotation

py
from typing import TypeAlias, TypeVar

T = TypeVar("T", default=int)
S = TypeVar("S")

Pair: TypeAlias = tuple[T, S]

No diagnostic for a TypeAliasType call

py
from typing import TypeAliasType, TypeVar

T = TypeVar("T", default=int)
S = TypeVar("S")

Pair = TypeAliasType("Pair", tuple[T, S], type_params=(T, S))

The fix is still offered when the non-defaulted TypeVar comes first

py
from typing import TypeAlias, TypeVar

T = TypeVar("T", default=int)
S = TypeVar("S")

Pair: TypeAlias = tuple[S, T]  # snapshot: non-pep695-type-alias
snapshot
error[UP040]: Type alias `Pair` uses `TypeAlias` annotation instead of the `type` keyword
 --> src/mdtest_snippet.py:6:1
  |
6 | Pair: TypeAlias = tuple[S, T]  # snapshot: non-pep695-type-alias
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: Use the `type` keyword
  |
5 |
  - Pair: TypeAlias = tuple[S, T]  # snapshot: non-pep695-type-alias
6 + type Pair[S, T = int] = tuple[S, T]  # snapshot: non-pep695-type-alias
  |
note: This is an unsafe fix and may change runtime behavior

The fix is still offered when all of the type variables have defaults

py
from typing import TypeAlias, TypeVar

T = TypeVar("T", default=int)
S = TypeVar("S", default=str)

Pair: TypeAlias = tuple[T, S]  # error: [non-pep695-type-alias]

non-pep695-generic-class (UP046)

No diagnostic when a defaulted TypeVar precedes a non-defaulted one

py
from typing import Generic, TypeVar

T = TypeVar("T", default=int)
S = TypeVar("S")

class Pair(Generic[T, S]):
    t: T
    s: S

The fix is still offered when the non-defaulted TypeVar comes first

py
from typing import Generic, TypeVar

T = TypeVar("T", default=int)
S = TypeVar("S")

class Pair(Generic[S, T]):  # snapshot: non-pep695-generic-class
    t: T
    s: S
snapshot
error[UP046]: Generic class `Pair` uses `Generic` subclass instead of type parameters
 --> src/mdtest_snippet.py:6:12
  |
6 | class Pair(Generic[S, T]):  # snapshot: non-pep695-generic-class
  |            ^^^^^^^^^^^^^
help: Use type parameters
  |
5 |
  - class Pair(Generic[S, T]):  # snapshot: non-pep695-generic-class
6 + class Pair[S, T = int]:  # snapshot: non-pep695-generic-class
7 |     t: T
  |
note: This is an unsafe fix and may change runtime behavior

non-pep695-generic-function (UP047)

No diagnostic when a defaulted TypeVar precedes a non-defaulted one

py
from typing import TypeVar

T = TypeVar("T", default=int)
S = TypeVar("S")

def pair(t: T, s: S) -> tuple[T, S]:
    return (t, s)

The fix is still offered when the non-defaulted TypeVar comes first

py
from typing import TypeVar

T = TypeVar("T", default=int)
S = TypeVar("S")

def pair(s: S, t: T) -> tuple[S, T]:  # snapshot: non-pep695-generic-function
    return (s, t)
snapshot
error[UP047]: Generic function `pair` should use type parameters
 --> src/mdtest_snippet.py:6:5
  |
6 | def pair(s: S, t: T) -> tuple[S, T]:  # snapshot: non-pep695-generic-function
  |     ^^^^^^^^^^^^^^^^
help: Use type parameters
  |
5 |
  - def pair(s: S, t: T) -> tuple[S, T]:  # snapshot: non-pep695-generic-function
6 + def pair[S, T = int](s: S, t: T) -> tuple[S, T]:  # snapshot: non-pep695-generic-function
7 |     return (s, t)
  |
note: This is an unsafe fix and may change runtime behavior