Skip to content

Commit 797b195

Browse files
committed
[ty] Experiment: half-baked typing.TypeAlias support
1 parent f9ca6eb commit 797b195

File tree

13 files changed

+47
-27
lines changed

13 files changed

+47
-27
lines changed

crates/ty_python_semantic/resources/mdtest/annotations/unsupported_special_forms.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ Alias: TypeAlias = int
1616

1717
def f(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]:
1818
reveal_type(args) # revealed: tuple[@Todo(`Unpack[]` special form), ...]
19-
reveal_type(Alias) # revealed: @Todo(Support for `typing.TypeAlias`)
19+
# TODO: this is not correct. At runtime, this is `type` (`Alias` is just `<class 'int'>`).
20+
reveal_type(Alias) # revealed: typing.TypeAliasType
2021

2122
def g() -> TypeGuard[int]: ...
2223
def h() -> TypeIs[int]: ...

crates/ty_python_semantic/resources/mdtest/attributes.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,9 +1736,9 @@ reveal_type(False.real) # revealed: Literal[0]
17361736
All attribute access on literal `bytes` types is currently delegated to `builtins.bytes`:
17371737

17381738
```py
1739-
# revealed: bound method Literal[b"foo"].join(iterable_of_bytes: Iterable[@Todo(Support for `typing.TypeAlias`)], /) -> bytes
1739+
# revealed: bound method Literal[b"foo"].join(iterable_of_bytes: Iterable[Buffer], /) -> bytes
17401740
reveal_type(b"foo".join)
1741-
# revealed: bound method Literal[b"foo"].endswith(suffix: @Todo(Support for `typing.TypeAlias`) | tuple[@Todo(Support for `typing.TypeAlias`), ...], start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool
1741+
# revealed: bound method Literal[b"foo"].endswith(suffix: Buffer | tuple[Buffer, ...], start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool
17421742
reveal_type(b"foo".endswith)
17431743
```
17441744

crates/ty_python_semantic/resources/mdtest/binary/instances.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,7 @@ reveal_type(A() + "foo") # revealed: A
313313
reveal_type("foo" + A()) # revealed: A
314314

315315
reveal_type(A() + b"foo") # revealed: A
316-
# TODO should be `A` since `bytes.__add__` doesn't support `A` instances
317-
reveal_type(b"foo" + A()) # revealed: bytes
316+
reveal_type(b"foo" + A()) # revealed: A
318317

319318
reveal_type(A() + ()) # revealed: A
320319
reveal_type(() + A()) # revealed: A

crates/ty_python_semantic/resources/mdtest/binary/integers.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,8 @@ reveal_type(2**largest_u32) # revealed: int
5454

5555
def variable(x: int):
5656
reveal_type(x**2) # revealed: int
57-
# TODO: should be `Any` (overload 5 on `__pow__`), requires correct overload matching
58-
reveal_type(2**x) # revealed: int
59-
# TODO: should be `Any` (overload 5 on `__pow__`), requires correct overload matching
60-
reveal_type(x**x) # revealed: int
57+
reveal_type(2**x) # revealed: Any
58+
reveal_type(x**x) # revealed: Any
6159
```
6260

6361
If the second argument is \<0, a `float` is returned at runtime. If the first argument is \<0 but

crates/ty_python_semantic/resources/mdtest/narrow/isinstance.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,11 @@ def _(flag: bool):
146146
def _(flag: bool):
147147
x = 1 if flag else "a"
148148

149-
# TODO: this should cause us to emit a diagnostic during
150-
# type checking
149+
# error: [invalid-argument-type]
151150
if isinstance(x, "a"):
152151
reveal_type(x) # revealed: Literal[1, "a"]
153152

154-
# TODO: this should cause us to emit a diagnostic during
155-
# type checking
153+
# error: [invalid-argument-type]
156154
if isinstance(x, "int"):
157155
reveal_type(x) # revealed: Literal[1, "a"]
158156
```

crates/ty_python_semantic/resources/mdtest/narrow/issubclass.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,7 @@ def flag() -> bool:
214214

215215
t = int if flag() else str
216216

217-
# TODO: this should cause us to emit a diagnostic during
218-
# type checking
217+
# error: [invalid-argument-type]
219218
if issubclass(t, "str"):
220219
reveal_type(t) # revealed: <class 'int'> | <class 'str'>
221220

crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def f() -> None:
2828
```py
2929
type IntOrStr = int | str
3030

31-
reveal_type(IntOrStr.__value__) # revealed: @Todo(Support for `typing.TypeAlias`)
31+
reveal_type(IntOrStr.__value__) # revealed: Any
3232
```
3333

3434
## Invalid assignment

crates/ty_python_semantic/resources/mdtest/protocols.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,8 @@ def f(
243243
Nonetheless, `Protocol` can still be used as the second argument to `issubclass()` at runtime:
244244

245245
```py
246-
# Could also be `Literal[True]`, but `bool` is fine:
246+
# TODO: Should be `Literal[True]`, but `bool` is also fine
247+
# error: [invalid-argument-type]
247248
reveal_type(issubclass(MyProtocol, Protocol)) # revealed: bool
248249
```
249250

crates/ty_python_semantic/resources/mdtest/sys_version_info.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ properties on instance types:
122122

123123
```py
124124
reveal_type(sys.version_info.micro) # revealed: int
125-
reveal_type(sys.version_info.releaselevel) # revealed: @Todo(Support for `typing.TypeAlias`)
125+
reveal_type(sys.version_info.releaselevel) # revealed: Literal["alpha", "beta", "candidate", "final"]
126126
reveal_type(sys.version_info.serial) # revealed: int
127127
```
128128

crates/ty_python_semantic/resources/primer/bad.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
Tanjun # hangs
2+
alerta # too many iterations
23
antidote # hangs / slow
34
artigraph # cycle panics (value_type_)
45
core # cycle panics (value_type_)
56
cpython # access to field whilst being initialized, too many cycle iterations
67
discord.py # some kind of hang, only when multi-threaded?
78
freqtrade # hangs
89
hydpy # too many iterations
10+
hydra-zen # too many iterations
911
ibis # too many iterations
12+
isort # too many iterations
1013
jax # too many iterations
14+
meson # too many iterations
15+
mypy # too many iterations
1116
packaging # too many iterations
1217
pandas # slow
1318
pandas-stubs # hangs/slow, or else https://github.com/salsa-rs/salsa/issues/831
1419
pandera # stack overflow
1520
pip # vendors packaging, see above
1621
prefect # slow
22+
psycopg2 # too many iterations
23+
pydantic # too many iterations
1724
pylint # cycle panics (self-recursive type alias)
1825
pyodide # too many cycle iterations
1926
pywin32 # bad use-def map (binding with definitely-visible unbound)
@@ -23,4 +30,5 @@ setuptools # vendors packaging, see above
2330
spack # success, but mypy-primer hangs processing the output
2431
spark # too many iterations
2532
steam.py # hangs
33+
strawberry # too many iterations
2634
xarray # too many iterations

crates/ty_python_semantic/resources/primer/good.txt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ aiohttp-devtools
88
aioredis
99
aiortc
1010
alectryon
11-
alerta
1211
altair
1312
anyio
1413
apprise
@@ -41,10 +40,8 @@ flake8-pyi
4140
git-revise
4241
graphql-core
4342
httpx-caching
44-
hydra-zen
4543
ignite
4644
imagehash
47-
isort
4845
itsdangerous
4946
janus
5047
jinja
@@ -53,13 +50,11 @@ kopf
5350
kornia
5451
manticore
5552
materialize
56-
meson
5753
mitmproxy
5854
mkdocs
5955
mkosi
6056
mongo-python-driver
6157
more-itertools
62-
mypy
6358
mypy-protobuf
6459
mypy_primer
6560
nionutils
@@ -74,11 +69,9 @@ pegen
7469
poetry
7570
porcupine
7671
ppb-vector
77-
psycopg
7872
pwndbg
7973
pybind11
8074
pycryptodome
81-
pydantic
8275
pyinstrument
8376
pyjwt
8477
pylox
@@ -101,7 +94,6 @@ sphinx
10194
starlette
10295
static-frame
10396
stone
104-
strawberry
10597
streamlit
10698
sympy
10799
tornado

crates/ty_python_semantic/src/types.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4922,7 +4922,9 @@ impl<'db> Type<'db> {
49224922
TypeVarKind::Legacy,
49234923
)))
49244924
}
4925-
KnownInstanceType::TypeAlias => Ok(todo_type!("Support for `typing.TypeAlias`")),
4925+
KnownInstanceType::TypeAlias => {
4926+
Ok(Type::KnownInstance(KnownInstanceType::TypeAlias))
4927+
}
49264928
KnownInstanceType::TypedDict => Ok(todo_type!("Support for `typing.TypedDict`")),
49274929

49284930
KnownInstanceType::Protocol(_) => Err(InvalidTypeExpressionError {

crates/ty_python_semantic/src/types/infer.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3575,7 +3575,29 @@ impl<'db> TypeInferenceBuilder<'db> {
35753575
debug_assert!(target.is_name_expr());
35763576

35773577
if let Some(value) = value {
3578+
if declared_ty.inner_type() == Type::KnownInstance(KnownInstanceType::TypeAlias) {
3579+
let value_ty = self.infer_type_expression(value);
3580+
3581+
let type_alias_ty = Type::KnownInstance(KnownInstanceType::TypeAliasType(
3582+
TypeAliasType::Bare(BareTypeAliasType::new(
3583+
self.db(),
3584+
ast::name::Name::new("dummy"),
3585+
Some(definition),
3586+
value_ty,
3587+
)),
3588+
));
3589+
3590+
self.add_declaration_with_binding(
3591+
target.into(),
3592+
definition,
3593+
&DeclaredAndInferredType::AreTheSame(type_alias_ty),
3594+
);
3595+
self.store_expression_type(target, type_alias_ty);
3596+
return;
3597+
}
3598+
35783599
let inferred_ty = self.infer_expression(value);
3600+
35793601
let inferred_ty = if target
35803602
.as_name_expr()
35813603
.is_some_and(|name| &name.id == "TYPE_CHECKING")

0 commit comments

Comments
 (0)