Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/ty/docs/rules.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ len([], 1)
### Type API predicates

```py
from ty_extensions import is_subtype_of, is_fully_static
from ty_extensions import is_subtype_of

# error: [missing-argument]
is_subtype_of()
Expand All @@ -326,10 +326,4 @@ is_subtype_of(int, int, int)

# error: [too-many-positional-arguments]
is_subtype_of(int, int, int, int)

# error: [missing-argument]
is_fully_static()

# error: [too-many-positional-arguments]
is_fully_static(int, int)
```
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ from typing import Any
def _(a: Any, tuple_of_any: tuple[Any]):
reveal_type(inspect.getattr_static(a, "x", "default")) # revealed: Any | Literal["default"]

# TODO: Ideally, this would just be `def index(self, value: Any, start: SupportsIndex = Literal[0], stop: SupportsIndex = int, /) -> int`
# revealed: (def index(self, value: Any, start: SupportsIndex = Literal[0], stop: SupportsIndex = int, /) -> int) | Literal["default"]
# revealed: def index(self, value: Any, start: SupportsIndex = Literal[0], stop: SupportsIndex = int, /) -> int
reveal_type(inspect.getattr_static(tuple_of_any, "index", "default"))
```

Expand Down
6 changes: 3 additions & 3 deletions crates/ty_python_semantic/resources/mdtest/call/union.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,15 @@ def _(
## Cannot use an argument as both a value and a type form

```py
from ty_extensions import is_fully_static
from ty_extensions import is_singleton

def _(flag: bool):
if flag:
f = repr
else:
f = is_fully_static
f = is_singleton
# error: [conflicting-argument-forms] "Argument is used as both a value and a type form in call"
reveal_type(f(int)) # revealed: str | Literal[True]
reveal_type(f(int)) # revealed: str | Literal[False]
```

## Size limit on unions of literals
Expand Down
3 changes: 2 additions & 1 deletion crates/ty_python_semantic/resources/mdtest/dataclasses.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,12 @@ from typing import Any

@dataclass
class C:
w: type[Any]
x: Any
y: int | Any
z: tuple[int, Any]

reveal_type(C.__init__) # revealed: (self: C, x: Any, y: int | Any, z: tuple[int, Any]) -> None
reveal_type(C.__init__) # revealed: (self: C, w: type[Any], x: Any, y: int | Any, z: tuple[int, Any]) -> None
```

Variables without annotations are ignored:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Types that "produce" data on demand are covariant in their typevar. If you expec
get from the sequence is a valid `int`.

```py
from ty_extensions import is_assignable_to, is_equivalent_to, is_gradual_equivalent_to, is_subtype_of, static_assert, Unknown
from ty_extensions import is_assignable_to, is_equivalent_to, is_subtype_of, static_assert, Unknown
from typing import Any, Generic, TypeVar

class A: ...
Expand Down Expand Up @@ -53,11 +53,13 @@ static_assert(is_assignable_to(D[Any], C[A]))
static_assert(is_assignable_to(D[Any], C[B]))

static_assert(is_subtype_of(C[B], C[A]))
static_assert(is_subtype_of(C[A], C[A]))
static_assert(not is_subtype_of(C[A], C[B]))
static_assert(not is_subtype_of(C[A], C[Any]))
static_assert(not is_subtype_of(C[B], C[Any]))
static_assert(not is_subtype_of(C[Any], C[A]))
static_assert(not is_subtype_of(C[Any], C[B]))
static_assert(not is_subtype_of(C[Any], C[Any]))

static_assert(is_subtype_of(D[B], C[A]))
static_assert(not is_subtype_of(D[A], C[B]))
Expand All @@ -84,27 +86,11 @@ static_assert(not is_equivalent_to(D[B], C[Any]))
static_assert(not is_equivalent_to(D[Any], C[A]))
static_assert(not is_equivalent_to(D[Any], C[B]))

static_assert(is_gradual_equivalent_to(C[A], C[A]))
static_assert(is_gradual_equivalent_to(C[B], C[B]))
static_assert(is_gradual_equivalent_to(C[Any], C[Any]))
static_assert(is_gradual_equivalent_to(C[Any], C[Unknown]))
static_assert(not is_gradual_equivalent_to(C[B], C[A]))
static_assert(not is_gradual_equivalent_to(C[A], C[B]))
static_assert(not is_gradual_equivalent_to(C[A], C[Any]))
static_assert(not is_gradual_equivalent_to(C[B], C[Any]))
static_assert(not is_gradual_equivalent_to(C[Any], C[A]))
static_assert(not is_gradual_equivalent_to(C[Any], C[B]))

static_assert(not is_gradual_equivalent_to(D[A], C[A]))
static_assert(not is_gradual_equivalent_to(D[B], C[B]))
static_assert(not is_gradual_equivalent_to(D[Any], C[Any]))
static_assert(not is_gradual_equivalent_to(D[Any], C[Unknown]))
static_assert(not is_gradual_equivalent_to(D[B], C[A]))
static_assert(not is_gradual_equivalent_to(D[A], C[B]))
static_assert(not is_gradual_equivalent_to(D[A], C[Any]))
static_assert(not is_gradual_equivalent_to(D[B], C[Any]))
static_assert(not is_gradual_equivalent_to(D[Any], C[A]))
static_assert(not is_gradual_equivalent_to(D[Any], C[B]))
static_assert(is_equivalent_to(C[Any], C[Any]))
static_assert(is_equivalent_to(C[Any], C[Unknown]))

static_assert(not is_equivalent_to(D[Any], C[Any]))
static_assert(not is_equivalent_to(D[Any], C[Unknown]))
```

## Contravariance
Expand All @@ -117,7 +103,7 @@ Types that "consume" data are contravariant in their typevar. If you expect a co
that you pass into the consumer is a valid `int`.

```py
from ty_extensions import is_assignable_to, is_equivalent_to, is_gradual_equivalent_to, is_subtype_of, static_assert, Unknown
from ty_extensions import is_assignable_to, is_equivalent_to, is_subtype_of, static_assert, Unknown
from typing import Any, Generic, TypeVar

class A: ...
Expand Down Expand Up @@ -178,27 +164,11 @@ static_assert(not is_equivalent_to(D[B], C[Any]))
static_assert(not is_equivalent_to(D[Any], C[A]))
static_assert(not is_equivalent_to(D[Any], C[B]))

static_assert(is_gradual_equivalent_to(C[A], C[A]))
static_assert(is_gradual_equivalent_to(C[B], C[B]))
static_assert(is_gradual_equivalent_to(C[Any], C[Any]))
static_assert(is_gradual_equivalent_to(C[Any], C[Unknown]))
static_assert(not is_gradual_equivalent_to(C[B], C[A]))
static_assert(not is_gradual_equivalent_to(C[A], C[B]))
static_assert(not is_gradual_equivalent_to(C[A], C[Any]))
static_assert(not is_gradual_equivalent_to(C[B], C[Any]))
static_assert(not is_gradual_equivalent_to(C[Any], C[A]))
static_assert(not is_gradual_equivalent_to(C[Any], C[B]))

static_assert(not is_gradual_equivalent_to(D[A], C[A]))
static_assert(not is_gradual_equivalent_to(D[B], C[B]))
static_assert(not is_gradual_equivalent_to(D[Any], C[Any]))
static_assert(not is_gradual_equivalent_to(D[Any], C[Unknown]))
static_assert(not is_gradual_equivalent_to(D[B], C[A]))
static_assert(not is_gradual_equivalent_to(D[A], C[B]))
static_assert(not is_gradual_equivalent_to(D[A], C[Any]))
static_assert(not is_gradual_equivalent_to(D[B], C[Any]))
static_assert(not is_gradual_equivalent_to(D[Any], C[A]))
static_assert(not is_gradual_equivalent_to(D[Any], C[B]))
static_assert(is_equivalent_to(C[Any], C[Any]))
static_assert(is_equivalent_to(C[Any], C[Unknown]))

static_assert(not is_equivalent_to(D[Any], C[Any]))
static_assert(not is_equivalent_to(D[Any], C[Unknown]))
```

## Invariance
Expand All @@ -224,7 +194,7 @@ In the end, if you expect a mutable list, you must always be given a list of exa
since we can't know in advance which of the allowed methods you'll want to use.

```py
from ty_extensions import is_assignable_to, is_equivalent_to, is_gradual_equivalent_to, is_subtype_of, static_assert, Unknown
from ty_extensions import is_assignable_to, is_equivalent_to, is_subtype_of, static_assert, Unknown
from typing import Any, Generic, TypeVar

class A: ...
Expand Down Expand Up @@ -287,27 +257,11 @@ static_assert(not is_equivalent_to(D[B], C[Any]))
static_assert(not is_equivalent_to(D[Any], C[A]))
static_assert(not is_equivalent_to(D[Any], C[B]))

static_assert(is_gradual_equivalent_to(C[A], C[A]))
static_assert(is_gradual_equivalent_to(C[B], C[B]))
static_assert(is_gradual_equivalent_to(C[Any], C[Any]))
static_assert(is_gradual_equivalent_to(C[Any], C[Unknown]))
static_assert(not is_gradual_equivalent_to(C[B], C[A]))
static_assert(not is_gradual_equivalent_to(C[A], C[B]))
static_assert(not is_gradual_equivalent_to(C[A], C[Any]))
static_assert(not is_gradual_equivalent_to(C[B], C[Any]))
static_assert(not is_gradual_equivalent_to(C[Any], C[A]))
static_assert(not is_gradual_equivalent_to(C[Any], C[B]))

static_assert(not is_gradual_equivalent_to(D[A], C[A]))
static_assert(not is_gradual_equivalent_to(D[B], C[B]))
static_assert(not is_gradual_equivalent_to(D[Any], C[Any]))
static_assert(not is_gradual_equivalent_to(D[Any], C[Unknown]))
static_assert(not is_gradual_equivalent_to(D[B], C[A]))
static_assert(not is_gradual_equivalent_to(D[A], C[B]))
static_assert(not is_gradual_equivalent_to(D[A], C[Any]))
static_assert(not is_gradual_equivalent_to(D[B], C[Any]))
static_assert(not is_gradual_equivalent_to(D[Any], C[A]))
static_assert(not is_gradual_equivalent_to(D[Any], C[B]))
static_assert(is_equivalent_to(C[Any], C[Any]))
static_assert(is_equivalent_to(C[Any], C[Unknown]))

static_assert(not is_equivalent_to(D[Any], C[Any]))
static_assert(not is_equivalent_to(D[Any], C[Unknown]))
```

## Bivariance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,33 +113,6 @@ class C[T]:
reveal_type(x) # revealed: T
```

## Fully static typevars

We consider a typevar to be fully static unless it has a non-fully-static bound or constraint. This
is true even though a fully static typevar might be specialized to a gradual form like `Any`. (This
is similar to how you can assign an expression whose type is not fully static to a target whose type
is.)

```py
from ty_extensions import is_fully_static, static_assert
from typing import Any

def unbounded_unconstrained[T](t: T) -> None:
static_assert(is_fully_static(T))

def bounded[T: int](t: T) -> None:
static_assert(is_fully_static(T))

def bounded_by_gradual[T: Any](t: T) -> None:
static_assert(not is_fully_static(T))

def constrained[T: (int, str)](t: T) -> None:
static_assert(is_fully_static(T))

def constrained_by_gradual[T: (int, Any)](t: T) -> None:
static_assert(not is_fully_static(T))
```

## Subtyping and assignability

(Note: for simplicity, all of the prose in this section refers to _subtyping_ involving fully static
Expand Down Expand Up @@ -372,14 +345,14 @@ def inter[T: Base, U: (Base, Unrelated)](t: T, u: U) -> None:

## Equivalence

A fully static `TypeVar` is always equivalent to itself, but never to another `TypeVar`, since there
is no guarantee that they will be specialized to the same type. (This is true even if both typevars
are bounded by the same final class, since you can specialize the typevars to `Never` in addition to
A `TypeVar` is always equivalent to itself, but never to another `TypeVar`, since there is no
guarantee that they will be specialized to the same type. (This is true even if both typevars are
bounded by the same final class, since you can specialize the typevars to `Never` in addition to
that final class.)

```py
from typing import final
from ty_extensions import is_equivalent_to, static_assert, is_gradual_equivalent_to
from ty_extensions import is_equivalent_to, static_assert

@final
class FinalClass: ...
Expand All @@ -395,28 +368,16 @@ def f[A, B, C: FinalClass, D: FinalClass, E: (FinalClass, SecondFinalClass), F:
static_assert(is_equivalent_to(E, E))
static_assert(is_equivalent_to(F, F))

static_assert(is_gradual_equivalent_to(A, A))
static_assert(is_gradual_equivalent_to(B, B))
static_assert(is_gradual_equivalent_to(C, C))
static_assert(is_gradual_equivalent_to(D, D))
static_assert(is_gradual_equivalent_to(E, E))
static_assert(is_gradual_equivalent_to(F, F))

static_assert(not is_equivalent_to(A, B))
static_assert(not is_equivalent_to(C, D))
static_assert(not is_equivalent_to(E, F))

static_assert(not is_gradual_equivalent_to(A, B))
static_assert(not is_gradual_equivalent_to(C, D))
static_assert(not is_gradual_equivalent_to(E, F))
```

TypeVars which have non-fully-static bounds or constraints do not participate in equivalence
relations, but do participate in gradual equivalence relations.
TypeVars which have non-fully-static bounds or constraints are also self-equivalent.

```py
from typing import final, Any
from ty_extensions import is_equivalent_to, static_assert, is_gradual_equivalent_to
from ty_extensions import is_equivalent_to, static_assert

# fmt: off

Expand All @@ -426,15 +387,10 @@ def f[
C: (tuple[Any], tuple[Any, Any]),
D: (tuple[Any], tuple[Any, Any])
]():
static_assert(not is_equivalent_to(A, A))
static_assert(not is_equivalent_to(B, B))
static_assert(not is_equivalent_to(C, C))
static_assert(not is_equivalent_to(D, D))

static_assert(is_gradual_equivalent_to(A, A))
static_assert(is_gradual_equivalent_to(B, B))
static_assert(is_gradual_equivalent_to(C, C))
static_assert(is_gradual_equivalent_to(D, D))
static_assert(is_equivalent_to(A, A))
static_assert(is_equivalent_to(B, B))
static_assert(is_equivalent_to(C, C))
static_assert(is_equivalent_to(D, D))

# fmt: on
```
Expand Down
Loading
Loading