mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-01 04:18:05 +00:00
[ty] Support async/await, async with and yield from (#19595)
## Summary - Add support for the return types of `async` functions - Add type inference for `await` expressions - Add support for `async with` / async context managers - Add support for `yield from` expressions This PR is generally lacking proper error handling in some cases (e.g. illegal `__await__` attributes). I'm planning to work on this in a follow-up. part of https://github.com/astral-sh/ty/issues/151 closes https://github.com/astral-sh/ty/issues/736 ## Ecosystem There are a lot of true positives on `prefect` which look similar to: ```diff prefect (https://github.com/PrefectHQ/prefect) + src/integrations/prefect-aws/tests/workers/test_ecs_worker.py:406:12: error[unresolved-attribute] Type `str` has no attribute `status_code` ``` This is due to a wrong return type annotation [here](e926b8c4c1/src/integrations/prefect-aws/tests/workers/test_ecs_worker.py (L355-L391)). ```diff mitmproxy (https://github.com/mitmproxy/mitmproxy) + test/mitmproxy/addons/test_clientplayback.py:18:1: error[invalid-argument-type] Argument to function `asynccontextmanager` is incorrect: Expected `(...) -> AsyncIterator[Unknown]`, found `def tcp_server(handle_conn, **server_args) -> Unknown | tuple[str, int]` ``` [This](a4d794c59a/test/mitmproxy/addons/test_clientplayback.py (L18-L19)) is a true positive. That function should return `AsyncIterator[Address]`, not `Address`. I looked through almost all of the other new diagnostics and they all look like known problems or true positives. ## Typing conformance The typing conformance diff looks good. ## Test Plan New Markdown tests
This commit is contained in:
parent
c5ac998892
commit
4ecf1d205a
12 changed files with 472 additions and 46 deletions
|
|
@ -17,5 +17,80 @@ class Manager:
|
|||
|
||||
async def test():
|
||||
async with Manager() as f:
|
||||
reveal_type(f) # revealed: @Todo(async `with` statement)
|
||||
reveal_type(f) # revealed: Target
|
||||
```
|
||||
|
||||
## Multiple targets
|
||||
|
||||
```py
|
||||
class Manager:
|
||||
async def __aenter__(self) -> tuple[int, str]:
|
||||
return 42, "hello"
|
||||
|
||||
async def __aexit__(self, exc_type, exc_value, traceback): ...
|
||||
|
||||
async def test():
|
||||
async with Manager() as (x, y):
|
||||
reveal_type(x) # revealed: int
|
||||
reveal_type(y) # revealed: str
|
||||
```
|
||||
|
||||
## `@asynccontextmanager`
|
||||
|
||||
```py
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncGenerator
|
||||
|
||||
class Session: ...
|
||||
|
||||
@asynccontextmanager
|
||||
async def connect() -> AsyncGenerator[Session]:
|
||||
yield Session()
|
||||
|
||||
# TODO: this should be `() -> _AsyncGeneratorContextManager[Session, None]`
|
||||
reveal_type(connect) # revealed: (...) -> _AsyncGeneratorContextManager[Unknown, None]
|
||||
|
||||
async def main():
|
||||
async with connect() as session:
|
||||
# TODO: should be `Session`
|
||||
reveal_type(session) # revealed: Unknown
|
||||
```
|
||||
|
||||
## `asyncio.timeout`
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.11"
|
||||
```
|
||||
|
||||
```py
|
||||
import asyncio
|
||||
|
||||
async def long_running_task():
|
||||
await asyncio.sleep(5)
|
||||
|
||||
async def main():
|
||||
async with asyncio.timeout(1):
|
||||
await long_running_task()
|
||||
```
|
||||
|
||||
## `asyncio.TaskGroup`
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.11"
|
||||
```
|
||||
|
||||
```py
|
||||
import asyncio
|
||||
|
||||
async def long_running_task():
|
||||
await asyncio.sleep(5)
|
||||
|
||||
async def main():
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
# TODO: should be `TaskGroup`
|
||||
reveal_type(tg) # revealed: Unknown
|
||||
|
||||
tg.create_task(long_running_task())
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue