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
113 changes: 20 additions & 93 deletions crates/ty_python_semantic/resources/mdtest/assignment/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,87 +229,6 @@ a: list[str] = [1, 2, 3]
b: set[int] = {1, 2, "3"}
```

## Literal annnotations are respected

```toml
[environment]
python-version = "3.12"
```

```py
from enum import Enum
from typing_extensions import Literal, LiteralString

a: list[Literal[1]] = [1]
reveal_type(a) # revealed: list[Literal[1]]

b: list[Literal[True]] = [True]
reveal_type(b) # revealed: list[Literal[True]]

c: list[Literal["a"]] = ["a"]
reveal_type(c) # revealed: list[Literal["a"]]

d: list[LiteralString] = ["a", "b", "c"]
reveal_type(d) # revealed: list[LiteralString]

e: list[list[Literal[1]]] = [[1]]
reveal_type(e) # revealed: list[list[Literal[1]]]

class Color(Enum):
RED = "red"

f: dict[list[Literal[1]], list[Literal[Color.RED]]] = {[1]: [Color.RED, Color.RED]}
reveal_type(f) # revealed: dict[list[Literal[1]], list[Color]]

class X[T]:
def __init__(self, value: T): ...

g: X[Literal[1]] = X(1)
reveal_type(g) # revealed: X[Literal[1]]

h: X[int] = X(1)
reveal_type(h) # revealed: X[int]

i: dict[list[X[Literal[1]]], set[Literal[b"a"]]] = {[X(1)]: {b"a"}}
reveal_type(i) # revealed: dict[list[X[Literal[1]]], set[Literal[b"a"]]]

j: list[Literal[1, 2, 3]] = [1, 2, 3]
reveal_type(j) # revealed: list[Literal[1, 2, 3]]

k: list[Literal[1] | Literal[2] | Literal[3]] = [1, 2, 3]
reveal_type(k) # revealed: list[Literal[1, 2, 3]]

type Y[T] = list[T]

l: Y[Y[Literal[1]]] = [[1]]
reveal_type(l) # revealed: list[Y[Literal[1]]]

m: list[tuple[Literal[1], Literal[2], Literal[3]]] = [(1, 2, 3)]
reveal_type(m) # revealed: list[tuple[Literal[1], Literal[2], Literal[3]]]

n: list[tuple[int, str, int]] = [(1, "2", 3), (4, "5", 6)]
reveal_type(n) # revealed: list[tuple[int, str, int]]

o: list[tuple[Literal[1], ...]] = [(1, 1, 1)]
reveal_type(o) # revealed: list[tuple[Literal[1], ...]]

p: list[tuple[int, ...]] = [(1, 1, 1)]
reveal_type(p) # revealed: list[tuple[int, ...]]

# literal promotion occurs based on assignability, an exact match is not required
q: list[int | Literal[1]] = [1]
reveal_type(q) # revealed: list[int]

r: list[Literal[1, 2, 3, 4]] = [1, 2]
reveal_type(r) # revealed: list[Literal[1, 2, 3, 4]]

s: list[Literal[1]]
s = [1]
reveal_type(s) # revealed: list[Literal[1]]
(s := [1])
reveal_type(s) # revealed: list[Literal[1]]
```

## Generic constructor annotations are understood

```toml
Expand Down Expand Up @@ -352,17 +271,25 @@ from dataclasses import dataclass
class Y[T]:
value: T

y1: Y[Any] = Y(value=1)
reveal_type(y1) # revealed: Y[Any]
y1 = Y(value=1)
reveal_type(y1) # revealed: Y[int]

y2: Y[Any] = Y(value=1)
reveal_type(y2) # revealed: Y[Any]
```

```py
class Z[T]:
value: T

def __new__(cls, value: T):
return super().__new__(cls)

z1: Z[Any] = Z(1)
reveal_type(z1) # revealed: Z[Any]
z1 = Z(1)
reveal_type(z1) # revealed: Z[int]

z2: Z[Any] = Z(1)
reveal_type(z2) # revealed: Z[Any]
```

## PEP-604 annotations are supported
Expand Down Expand Up @@ -481,7 +408,7 @@ def f[T](x: T) -> list[T]:
return [x]

a = f("a")
reveal_type(a) # revealed: list[Literal["a"]]
reveal_type(a) # revealed: list[str]

b: list[int | Literal["a"]] = f("a")
reveal_type(b) # revealed: list[int | Literal["a"]]
Expand All @@ -495,10 +422,10 @@ reveal_type(d) # revealed: list[int | tuple[int, int]]
e: list[int] = f(True)
reveal_type(e) # revealed: list[int]

# error: [invalid-assignment] "Object of type `list[Literal["a"]]` is not assignable to `list[int]`"
# error: [invalid-assignment] "Object of type `list[str]` is not assignable to `list[int]`"
g: list[int] = f("a")

# error: [invalid-assignment] "Object of type `list[Literal["a"]]` is not assignable to `tuple[int]`"
# error: [invalid-assignment] "Object of type `list[str]` is not assignable to `tuple[int]`"
h: tuple[int] = f("a")

def f2[T: int](x: T) -> T:
Expand Down Expand Up @@ -603,7 +530,7 @@ def f3[T](x: T) -> list[T] | dict[T, T]:
return [x]

a = f(1)
reveal_type(a) # revealed: list[Literal[1]]
reveal_type(a) # revealed: list[int]

b: list[Any] = f(1)
reveal_type(b) # revealed: list[Any]
Expand All @@ -619,11 +546,11 @@ reveal_type(e) # revealed: list[Any]

f: list[Any] | None = f2(1)
# TODO: Better constraint solver.
reveal_type(f) # revealed: list[Literal[1]] | None
reveal_type(f) # revealed: list[int] | None

g: list[Any] | dict[Any, Any] = f3(1)
# TODO: Better constraint solver.
reveal_type(g) # revealed: list[Literal[1]] | dict[Literal[1], Literal[1]]
reveal_type(g) # revealed: list[int] | dict[int, int]
```

We currently prefer the generic declared type regardless of its variance:
Expand Down Expand Up @@ -662,8 +589,8 @@ x4 = invariant(1)

reveal_type(x1) # revealed: Bivariant[Literal[1]]
reveal_type(x2) # revealed: Covariant[Literal[1]]
reveal_type(x3) # revealed: Contravariant[Literal[1]]
reveal_type(x4) # revealed: Invariant[Literal[1]]
reveal_type(x3) # revealed: Contravariant[int]
reveal_type(x4) # revealed: Invariant[int]

x5: Bivariant[Any] = bivariant(1)
x6: Covariant[Any] = covariant(1)
Expand Down
30 changes: 11 additions & 19 deletions crates/ty_python_semantic/resources/mdtest/bidirectional.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,19 @@ python-version = "3.12"
```

```py
from typing import Literal

def list1[T](x: T) -> list[T]:
return [x]

l1 = list1(1)
l1: list[Literal[1]] = list1(1)
reveal_type(l1) # revealed: list[Literal[1]]
l2: list[int] = list1(1)
reveal_type(l2) # revealed: list[int]

# `list[Literal[1]]` and `list[int]` are incompatible, since `list[T]` is invariant in `T`.
# error: [invalid-assignment] "Object of type `list[Literal[1]]` is not assignable to `list[int]`"
l2 = l1

intermediate = list1(1)
# TODO: the error will not occur if we can infer the type of `intermediate` to be `list[int]`
# error: [invalid-assignment] "Object of type `list[Literal[1]]` is not assignable to `list[int]`"
l3: list[int] = intermediate
# TODO: it would be nice if this were `list[int]`
reveal_type(intermediate) # revealed: list[Literal[1]]
reveal_type(l3) # revealed: list[int]
l2 = list1(1)
reveal_type(l2) # revealed: list[int]

l4: list[int | str] | None = list1(1)
reveal_type(l4) # revealed: list[int | str]
l3: list[int | str] | None = list1(1)
reveal_type(l3) # revealed: list[int | str]

def _(l: list[int] | None = None):
l1 = l or list()
Expand All @@ -50,8 +41,6 @@ def _(l: list[int] | None = None):
def f[T](x: T, cond: bool) -> T | list[T]:
return x if cond else [x]

# TODO: Better constraint solver.
# error: [invalid-assignment]
l5: int | list[int] = f(1, True)
```

Expand Down Expand Up @@ -233,6 +222,9 @@ def _(flag: bool):

def _(c: C):
c.x = lst(1)

# TODO: Use the parameter type of `__set__` as type context to avoid this error.
# error: [invalid-assignment]
C.x = lst(1)
```

Expand Down Expand Up @@ -296,7 +288,7 @@ def f[T](x: T) -> list[T]:

def _(flag: bool):
x1 = f(1) if flag else f(2)
reveal_type(x1) # revealed: list[Literal[1]] | list[Literal[2]]
reveal_type(x1) # revealed: list[int]

x2: list[int | None] = f(1) if flag else f(2)
reveal_type(x2) # revealed: list[int | None]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -978,10 +978,10 @@ reveal_type(ParentDataclass.__init__)
reveal_type(ChildOfParentDataclass.__init__)

result_int = uses_dataclass(42)
reveal_type(result_int) # revealed: ChildOfParentDataclass[Literal[42]]
reveal_type(result_int) # revealed: ChildOfParentDataclass[int]

result_str = uses_dataclass("hello")
reveal_type(result_str) # revealed: ChildOfParentDataclass[Literal["hello"]]
reveal_type(result_str) # revealed: ChildOfParentDataclass[str]
```

## Descriptor-typed fields
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ class C[T, U]:
class D[V](C[V, int]):
def __init__(self, x: V) -> None: ...

reveal_type(D(1)) # revealed: D[int]
reveal_type(D(1)) # revealed: D[Literal[1]]
```

### Generic class inherits `__init__` from generic base class
Expand All @@ -334,8 +334,8 @@ class C[T, U]:
class D[T, U](C[T, U]):
pass

reveal_type(C(1, "str")) # revealed: C[int, str]
reveal_type(D(1, "str")) # revealed: D[int, str]
reveal_type(C(1, "str")) # revealed: C[Literal[1], Literal["str"]]
reveal_type(D(1, "str")) # revealed: D[Literal[1], Literal["str"]]
```

### Generic class inherits `__init__` from `dict`
Expand All @@ -358,7 +358,7 @@ context. But from the user's point of view, this is another example of the above
```py
class C[T, U](tuple[T, U]): ...

reveal_type(C((1, 2))) # revealed: C[int, int]
reveal_type(C((1, 2))) # revealed: C[Literal[1], Literal[2]]
```

### Upcasting a `tuple` to its `Sequence` supertype
Expand Down Expand Up @@ -442,9 +442,9 @@ class D[T, U]:
def __init__(self, t: T, u: U) -> None: ...
def __init__(self, *args) -> None: ...

reveal_type(D("string")) # revealed: D[str, str]
reveal_type(D(1)) # revealed: D[str, int]
reveal_type(D(1, "string")) # revealed: D[int, str]
reveal_type(D("string")) # revealed: D[str, Literal["string"]]
reveal_type(D(1)) # revealed: D[str, Literal[1]]
reveal_type(D(1, "string")) # revealed: D[Literal[1], Literal["string"]]
```

### Synthesized methods with dataclasses
Expand Down
Loading
Loading