Skip to content

Commit

Permalink
Merge pull request #92 from oremanj/support-mypy17
Browse files Browse the repository at this point in the history
Replace plugin.args_invariant_decorator_callback with use of ParamSpec
  • Loading branch information
oremanj authored Dec 1, 2023
2 parents 6ebf74a + a765f88 commit 2404a3b
Show file tree
Hide file tree
Showing 17 changed files with 201 additions and 109 deletions.
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

0 comments on commit 2404a3b

Please sign in to comment.