[ty] Experiment: half-baked typing.TypeAlias support

This commit is contained in:
David Peter 2025-05-20 09:43:33 +02:00
parent f9ca6eb63e
commit 9220598fc8
13 changed files with 48 additions and 28 deletions

View file

@ -16,7 +16,8 @@ Alias: TypeAlias = int
def f(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]:
reveal_type(args) # revealed: tuple[@Todo(`Unpack[]` special form), ...]
reveal_type(Alias) # revealed: @Todo(Support for `typing.TypeAlias`)
# TODO: this is not correct. At runtime, this is `type` (`Alias` is just `<class 'int'>`).
reveal_type(Alias) # revealed: typing.TypeAliasType
def g() -> TypeGuard[int]: ...
def h() -> TypeIs[int]: ...

View file

@ -1736,9 +1736,9 @@ reveal_type(False.real) # revealed: Literal[0]
All attribute access on literal `bytes` types is currently delegated to `builtins.bytes`:
```py
# revealed: bound method Literal[b"foo"].join(iterable_of_bytes: Iterable[@Todo(Support for `typing.TypeAlias`)], /) -> bytes
# revealed: bound method Literal[b"foo"].join(iterable_of_bytes: Iterable[Buffer], /) -> bytes
reveal_type(b"foo".join)
# 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
# revealed: bound method Literal[b"foo"].endswith(suffix: Buffer | tuple[Buffer, ...], start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool
reveal_type(b"foo".endswith)
```

View file

@ -313,8 +313,7 @@ reveal_type(A() + "foo") # revealed: A
reveal_type("foo" + A()) # revealed: A
reveal_type(A() + b"foo") # revealed: A
# TODO should be `A` since `bytes.__add__` doesn't support `A` instances
reveal_type(b"foo" + A()) # revealed: bytes
reveal_type(b"foo" + A()) # revealed: A
reveal_type(A() + ()) # revealed: A
reveal_type(() + A()) # revealed: A

View file

@ -54,10 +54,8 @@ reveal_type(2**largest_u32) # revealed: int
def variable(x: int):
reveal_type(x**2) # revealed: int
# TODO: should be `Any` (overload 5 on `__pow__`), requires correct overload matching
reveal_type(2**x) # revealed: int
# TODO: should be `Any` (overload 5 on `__pow__`), requires correct overload matching
reveal_type(x**x) # revealed: int
reveal_type(2**x) # revealed: Any
reveal_type(x**x) # revealed: Any
```
If the second argument is \<0, a `float` is returned at runtime. If the first argument is \<0 but

View file

@ -146,13 +146,11 @@ def _(flag: bool):
def _(flag: bool):
x = 1 if flag else "a"
# TODO: this should cause us to emit a diagnostic during
# type checking
# error: [invalid-argument-type]
if isinstance(x, "a"):
reveal_type(x) # revealed: Literal[1, "a"]
# TODO: this should cause us to emit a diagnostic during
# type checking
# error: [invalid-argument-type]
if isinstance(x, "int"):
reveal_type(x) # revealed: Literal[1, "a"]
```

View file

@ -214,8 +214,7 @@ def flag() -> bool:
t = int if flag() else str
# TODO: this should cause us to emit a diagnostic during
# type checking
# error: [invalid-argument-type]
if issubclass(t, "str"):
reveal_type(t) # revealed: <class 'int'> | <class 'str'>

View file

@ -28,7 +28,7 @@ def f() -> None:
```py
type IntOrStr = int | str
reveal_type(IntOrStr.__value__) # revealed: @Todo(Support for `typing.TypeAlias`)
reveal_type(IntOrStr.__value__) # revealed: Any
```
## Invalid assignment

View file

@ -243,7 +243,8 @@ def f(
Nonetheless, `Protocol` can still be used as the second argument to `issubclass()` at runtime:
```py
# Could also be `Literal[True]`, but `bool` is fine:
# TODO: Should be `Literal[True]`, but `bool` is also fine
# error: [invalid-argument-type]
reveal_type(issubclass(MyProtocol, Protocol)) # revealed: bool
```

View file

@ -122,7 +122,7 @@ properties on instance types:
```py
reveal_type(sys.version_info.micro) # revealed: int
reveal_type(sys.version_info.releaselevel) # revealed: @Todo(Support for `typing.TypeAlias`)
reveal_type(sys.version_info.releaselevel) # revealed: Literal["alpha", "beta", "candidate", "final"]
reveal_type(sys.version_info.serial) # revealed: int
```

View file

@ -1,4 +1,5 @@
Tanjun # hangs
alerta # too many iterations
antidote # hangs / slow
artigraph # cycle panics (value_type_)
core # cycle panics (value_type_)
@ -6,21 +7,29 @@ cpython # access to field whilst being initialized, too many cycle iterations
discord.py # some kind of hang, only when multi-threaded?
freqtrade # hangs
hydpy # too many iterations
hydra-zen # too many iterations
ibis # too many iterations
isort # too many iterations
jax # too many iterations
meson # too many iterations
mypy # too many iterations
packaging # too many iterations
pandas # slow
pandas-stubs # hangs/slow, or else https://github.com/salsa-rs/salsa/issues/831
pandera # stack overflow
pip # vendors packaging, see above
prefect # slow
psycopg2 # too many iterations
pydantic # too many iterations
pylint # cycle panics (self-recursive type alias)
pyodide # too many cycle iterations
pywin32 # bad use-def map (binding with definitely-visible unbound)
schemathesis # https://github.com/salsa-rs/salsa/issues/831
scikit-learn # success, but mypy-primer hangs processing the output
scipy # slow / hangs
setuptools # vendors packaging, see above
spack # success, but mypy-primer hangs processing the output
spark # too many iterations
steam.py # hangs
strawberry # too many iterations
xarray # too many iterations

View file

@ -8,7 +8,6 @@ aiohttp-devtools
aioredis
aiortc
alectryon
alerta
altair
anyio
apprise
@ -41,10 +40,8 @@ flake8-pyi
git-revise
graphql-core
httpx-caching
hydra-zen
ignite
imagehash
isort
itsdangerous
janus
jinja
@ -53,13 +50,11 @@ kopf
kornia
manticore
materialize
meson
mitmproxy
mkdocs
mkosi
mongo-python-driver
more-itertools
mypy
mypy-protobuf
mypy_primer
nionutils
@ -74,11 +69,9 @@ pegen
poetry
porcupine
ppb-vector
psycopg
pwndbg
pybind11
pycryptodome
pydantic
pyinstrument
pyjwt
pylox
@ -93,7 +86,6 @@ rclip
rich
rotki
schema_salad
scipy
scrapy
sockeye
speedrun.com_global_scoreboard_webapp
@ -101,7 +93,6 @@ sphinx
starlette
static-frame
stone
strawberry
streamlit
sympy
tornado

View file

@ -4922,7 +4922,9 @@ impl<'db> Type<'db> {
TypeVarKind::Legacy,
)))
}
KnownInstanceType::TypeAlias => Ok(todo_type!("Support for `typing.TypeAlias`")),
KnownInstanceType::TypeAlias => {
Ok(Type::KnownInstance(KnownInstanceType::TypeAlias))
}
KnownInstanceType::TypedDict => Ok(todo_type!("Support for `typing.TypedDict`")),
KnownInstanceType::Protocol(_) => Err(InvalidTypeExpressionError {

View file

@ -3575,7 +3575,29 @@ impl<'db> TypeInferenceBuilder<'db> {
debug_assert!(target.is_name_expr());
if let Some(value) = value {
if declared_ty.inner_type() == Type::KnownInstance(KnownInstanceType::TypeAlias) {
let value_ty = self.infer_type_expression(value);
let type_alias_ty = Type::KnownInstance(KnownInstanceType::TypeAliasType(
TypeAliasType::Bare(BareTypeAliasType::new(
self.db(),
ast::name::Name::new("dummy"),
Some(definition),
value_ty,
)),
));
self.add_declaration_with_binding(
target.into(),
definition,
&DeclaredAndInferredType::AreTheSame(type_alias_ty),
);
self.store_expression_type(target, type_alias_ty);
return;
}
let inferred_ty = self.infer_expression(value);
let inferred_ty = if target
.as_name_expr()
.is_some_and(|name| &name.id == "TYPE_CHECKING")