Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace plugin.args_invariant_decorator_callback with use of ParamSpec #92

Merged
merged 4 commits into from
Dec 1, 2023
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
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ jobs:
fail-fast: false
matrix:
python:
- '3.7'
- '3.8'
- '3.9'
- '3.10'
- '3.11'
- '3.12'
check_formatting: ['0']
check_typing: ['0']
runtime_only: ['0']
Expand Down
6 changes: 1 addition & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Install trio-typing with mypy extras::

pip install trio-typing[mypy]

Note that due to recent plugin API changes, trio-typing 0.7.0+ requires mypy 0.920+.
Note that due to recent plugin API changes, trio-typing 0.10.0+ requires mypy 1.0+.

Enable the plugin in your ``mypy.ini``::

Expand Down Expand Up @@ -129,10 +129,6 @@ The ``trio_typing`` package provides:

The ``trio_typing.plugin`` mypy plugin provides:

* Argument type checking for functions decorated with
``@asynccontextmanager`` (either the one in ``async_generator`` or the
one in 3.7+ ``contextlib``) and ``@async_generator``

* Inference of more specific ``trio.open_file()`` and ``trio.Path.open()``
return types based on constant ``mode`` and ``buffering`` arguments, so
``await trio.open_file("foo", "rb", 0)`` returns an unbuffered async
Expand Down
2 changes: 2 additions & 0 deletions allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ trio.Process.__aenter__
.*_AttrsAttributes__
.*__attrs_own_setattr__
.*__attrs_post_init__
.*_AT
.*__slots__

# Probably invalid __match_args__
trio.MemoryReceiveChannel.__match_args__
Expand Down
17 changes: 7 additions & 10 deletions async_generator-stubs/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ from typing import (
overload,
)
from trio_typing import AsyncGenerator, CompatAsyncGenerator, YieldType, SendType
from typing_extensions import Protocol
from typing_extensions import Protocol, ParamSpec

_T = TypeVar("_T")
_P = ParamSpec("_P")

# The returned async generator's YieldType and SendType and the
# argument types of the decorated function get inferred by
# The returned async generator's YieldType and SendType get inferred by
# trio_typing.plugin
def async_generator(
__fn: Callable[..., Awaitable[_T]]
) -> Callable[..., CompatAsyncGenerator[Any, Any, _T]]: ...
__fn: Callable[_P, Awaitable[_T]]
) -> Callable[_P, CompatAsyncGenerator[Any, Any, _T]]: ...

# The return type and a more specific argument type can be
# inferred by trio_typing.plugin, based on the enclosing
Expand All @@ -40,12 +40,9 @@ async def yield_from_(agen: AsyncGenerator[Any, Any]) -> None: ...
async def yield_from_(agen: AsyncIterable[Any]) -> None: ...
def isasyncgen(obj: object) -> bool: ...
def isasyncgenfunction(obj: object) -> bool: ...

# Argument types of the decorated function get inferred by
# trio_typing.plugin
def asynccontextmanager(
fn: Callable[..., AsyncIterator[_T]]
) -> Callable[..., AsyncContextManager[_T]]: ...
fn: Callable[_P, AsyncIterator[_T]]
) -> Callable[_P, AsyncContextManager[_T]]: ...

class _AsyncCloseable(Protocol):
def aclose(self) -> Awaitable[None]: ...
Expand Down
2 changes: 1 addition & 1 deletion ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
set -ex -o pipefail

BLACK_VERSION=22.3
MYPY_VERSION=1.4
MYPY_VERSION=1.7

pip install -U pip setuptools wheel

Expand Down
11 changes: 6 additions & 5 deletions outcome-stubs/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ from typing import (
Union,
)
from types import TracebackType
from typing_extensions import Protocol
from typing_extensions import Protocol, ParamSpec

T = TypeVar("T")
U = TypeVar("U")
T_co = TypeVar("T_co", covariant=True)
T_contra = TypeVar("T_contra", contravariant=True)
P = ParamSpec("P")

# Can't use AsyncGenerator as it creates a dependency cycle
# (outcome stubs -> trio_typing stubs -> trio.hazmat stubs -> outcome)
Expand Down Expand Up @@ -47,9 +48,9 @@ class Error:

Outcome = Union[Value[T], Error]

# TODO: narrower typing for these (the args and kwargs should
# be acceptable to the callable)
def capture(sync_fn: Callable[..., T], *args: Any, **kwargs: Any) -> Outcome[T]: ...
def capture(
sync_fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs
) -> Outcome[T]: ...
async def acapture(
async_fn: Callable[..., Awaitable[T]], *args: Any, **kwargs: Any
async_fn: Callable[P, Awaitable[T]], *args: P.args, **kwargs: P.kwargs
) -> Outcome[T]: ...
9 changes: 6 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
],
extras_require={
"mypy": [ # can't be installed on PyPy due to its dependency on typed-ast
"mypy >= 0.920",
"mypy >= 1.0",
],
},
keywords=["async", "trio", "mypy"],
Expand All @@ -42,8 +42,11 @@
"Operating System :: POSIX :: BSD",
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Development Status :: 3 - Alpha",
Expand Down
107 changes: 87 additions & 20 deletions trio-stubs/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ from types import TracebackType
from _typeshed import StrOrBytesPath
from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer
from trio_typing import TaskStatus, takes_callable_and_args
from typing_extensions import Protocol, Literal
from typing_extensions import Protocol, Literal, Buffer
from mypy_extensions import NamedArg, VarArg
import signal
import io
Expand All @@ -48,9 +48,6 @@ _T = TypeVar("_T")
_T_co = TypeVar("_T_co", covariant=True)
_T_contra = TypeVar("_T_contra", contravariant=True)

class _Statistics:
def __getattr__(self, name: str) -> Any: ...

# Inheriting from this (even outside of stubs) produces a class that
# mypy thinks is abstract, but the interpreter thinks is concrete.
class _NotConstructible(Protocol):
Expand Down Expand Up @@ -208,13 +205,24 @@ class TooSlowError(Exception):
pass

# _sync
@attr.s(frozen=True, slots=True)
class EventStatistics:
tasks_waiting: int = attr.ib()

@final
@attr.s(eq=False, repr=False, slots=True)
class Event(metaclass=ABCMeta):
def is_set(self) -> bool: ...
def set(self) -> None: ...
async def wait(self) -> None: ...
def statistics(self) -> _Statistics: ...
def statistics(self) -> EventStatistics: ...

@attr.s(frozen=True, slots=True)
class CapacityLimiterStatistics:
borrowed_tokens: int = attr.ib()
total_tokens: int | float = attr.ib()
borrowers: list[trio.lowlevel.Task | object] = attr.ib()
tasks_waiting: int = attr.ib()

@final
class CapacityLimiter(metaclass=ABCMeta):
Expand All @@ -232,9 +240,14 @@ class CapacityLimiter(metaclass=ABCMeta):
async def acquire_on_behalf_of(self, borrower: object) -> None: ...
def release(self) -> None: ...
def release_on_behalf_of(self, borrower: object) -> None: ...
def statistics(self) -> _Statistics: ...
def statistics(self) -> CapacityLimiterStatistics: ...
async def __aenter__(self) -> None: ...
async def __aexit__(self, *exc: object) -> None: ...
async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None: ...

@final
class Semaphore(metaclass=ABCMeta):
Expand All @@ -246,29 +259,55 @@ class Semaphore(metaclass=ABCMeta):
def acquire_nowait(self) -> None: ...
async def acquire(self) -> None: ...
def release(self) -> None: ...
def statistics(self) -> _Statistics: ...
def statistics(self) -> lowlevel.ParkingLotStatistics: ...
async def __aenter__(self) -> None: ...
async def __aexit__(self, *exc: object) -> None: ...
async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None: ...

@attr.s(frozen=True, slots=True)
class LockStatistics:
locked: bool = attr.ib()
owner: trio.lowlevel.Task | None = attr.ib()
tasks_waiting: int = attr.ib()

@final
class Lock(metaclass=ABCMeta):
def locked(self) -> bool: ...
def acquire_nowait(self) -> None: ...
async def acquire(self) -> None: ...
def release(self) -> None: ...
def statistics(self) -> _Statistics: ...
def statistics(self) -> LockStatistics: ...
async def __aenter__(self) -> None: ...
async def __aexit__(self, *exc: object) -> None: ...
async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None: ...

@final
class StrictFIFOLock(metaclass=ABCMeta):
def locked(self) -> bool: ...
def acquire_nowait(self) -> None: ...
async def acquire(self) -> None: ...
def release(self) -> None: ...
def statistics(self) -> _Statistics: ...
def statistics(self) -> LockStatistics: ...
async def __aenter__(self) -> None: ...
async def __aexit__(self, *exc: object) -> None: ...
async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None: ...

@attr.s(frozen=True, slots=True)
class ConditionStatistics:
tasks_waiting: int = attr.ib()
lock_statistics: LockStatistics = attr.ib()

@final
class Condition(metaclass=ABCMeta):
Expand All @@ -280,9 +319,14 @@ class Condition(metaclass=ABCMeta):
async def wait(self) -> None: ...
def notify(self, n: int = 1) -> None: ...
def notify_all(self) -> None: ...
def statistics(self) -> _Statistics: ...
def statistics(self) -> ConditionStatistics: ...
async def __aenter__(self) -> None: ...
async def __aexit__(self, *exc: object) -> None: ...
async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None: ...

# _highlevel_generic
async def aclose_forcefully(resource: trio.abc.AsyncResource) -> None: ...
Expand All @@ -298,14 +342,23 @@ class StapledStream(trio.abc.HalfCloseableStream):
async def send_eof(self) -> None: ...

# _channel
@attr.s(frozen=True, slots=True)
class _MemoryChannelStats:
current_buffer_used: int = attr.ib()
max_buffer_size: int | float = attr.ib()
open_send_channels: int = attr.ib()
open_receive_channels: int = attr.ib()
tasks_waiting_send: int = attr.ib()
tasks_waiting_receive: int = attr.ib()

@final
@attr.s(eq=False, repr=False)
class MemorySendChannel(trio.abc.SendChannel[_T_contra]):
def send_nowait(self, value: _T_contra) -> None: ...
async def send(self, value: _T_contra) -> None: ...
def clone(self: _T) -> _T: ...
async def aclose(self) -> None: ...
def statistics(self) -> _Statistics: ...
def statistics(self) -> _MemoryChannelStats: ...
def close(self) -> None: ...
def __enter__(self) -> MemorySendChannel[_T_contra]: ...
def __exit__(
Expand All @@ -322,7 +375,7 @@ class MemoryReceiveChannel(trio.abc.ReceiveChannel[_T_co]):
async def receive(self) -> _T_co: ...
def clone(self: _T) -> _T: ...
async def aclose(self) -> None: ...
def statistics(self) -> _Statistics: ...
def statistics(self) -> _MemoryChannelStats: ...
def close(self) -> None: ...
def __enter__(self) -> MemoryReceiveChannel[_T_co]: ...
def __exit__(
Expand All @@ -349,7 +402,12 @@ def open_signal_receiver(
class SocketStream(trio.abc.HalfCloseableStream):
socket: trio.socket.SocketType
def __init__(self, socket: trio.socket.SocketType) -> None: ...
def setsockopt(self, level: int, option: int, value: Union[int, bytes]) -> None: ...
@overload
def setsockopt(
self, level: int, option: int, value: int | Buffer, length: None = None
) -> None: ...
@overload
def setsockopt(self, level: int, option: int, value: None, length: int) -> None: ...
@overload
def getsockopt(self, level: int, option: int) -> int: ...
@overload
Expand Down Expand Up @@ -400,6 +458,10 @@ class DTLSEndpoint(metaclass=ABCMeta):
exc_tb: TracebackType | None,
) -> None: ...

@attr.frozen
class DTLSChannelStatistics:
incoming_packets_dropped_in_trio: int

@final
class DTLSChannel(_NotConstructible, trio.abc.Channel[bytes], metaclass=ABCMeta):
endpoint: DTLSEndpoint
Expand All @@ -411,7 +473,7 @@ class DTLSChannel(_NotConstructible, trio.abc.Channel[bytes], metaclass=ABCMeta)
async def receive(self) -> bytes: ...
def set_ciphertext_mtu(self, new_mtu: int) -> None: ...
def get_cleartext_mtu(self) -> int: ...
def statistics(self) -> Any: ...
def statistics(self) -> DTLSChannelStatistics: ...
async def aclose(self) -> None: ...
def close(self) -> None: ...
def __enter__(self) -> DTLSChannel: ...
Expand Down Expand Up @@ -452,7 +514,12 @@ class AsyncIO(AsyncIterator[AnyStr], Generic[AnyStr], trio.abc.AsyncResource):
async def __anext__(self) -> AnyStr: ...
def __aiter__(self) -> AsyncIterator[AnyStr]: ...
async def __aenter__(self: _T) -> _T: ...
async def __aexit__(self, *exc: object) -> None: ...
async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None: ...

class AsyncBinaryIO(AsyncIO[bytes]):
pass
Expand Down
15 changes: 11 additions & 4 deletions trio-stubs/abc.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import socket
import trio
from abc import ABCMeta, abstractmethod
from typing import List, Tuple, Union, Any, Optional, Generic, TypeVar, AsyncIterator
from types import TracebackType

_T = TypeVar("_T")

Expand Down Expand Up @@ -43,16 +45,21 @@ class SocketFactory(metaclass=ABCMeta):
@abstractmethod
def socket(
self,
family: Optional[int] = None,
type: Optional[int] = None,
proto: Optional[int] = None,
family: socket.AddressFamily | int = ...,
type: socket.SocketKind | int = ...,
proto: int = ...,
) -> trio.socket.SocketType: ...

class AsyncResource(metaclass=ABCMeta):
@abstractmethod
async def aclose(self) -> None: ...
async def __aenter__(self: _T) -> _T: ...
async def __aexit__(self, *exc: object) -> None: ...
async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None: ...

class SendStream(AsyncResource):
@abstractmethod
Expand Down
Loading
Loading