Skip to content

Commit df9a513

Browse files
authored
gevent: Use TypeVarTuple instead of old ParamSpec workaround. (#11129)
1 parent 336c062 commit df9a513

File tree

5 files changed

+28
-44
lines changed

5 files changed

+28
-44
lines changed

stubs/gevent/@tests/stubtest_allowlist.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,6 @@ gevent._config._PositiveValueMixin.validate
122122
gevent._ffi.watcher.AbstractWatcherType.__new__
123123

124124
# these are inconsistent due to the ParamSpec hack for positional only callables
125-
gevent._ffi.loop.AbstractLoop.run_callback
126-
gevent._ffi.loop.AbstractLoop.run_callback_threadsafe
127-
gevent._ffi.watcher.watcher.start
128-
gevent._hub_primitives.WaitOperationsGreenlet.cancel_waits_close_and_then
129125
gevent.baseserver.BaseServer.do_close
130126
gevent.baseserver.BaseServer.do_handle
131127

stubs/gevent/gevent/_ffi/loop.pyi

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ from _typeshed import FileDescriptor
33
from collections.abc import Callable
44
from types import TracebackType
55
from typing import Protocol
6-
from typing_extensions import ParamSpec, TypeAlias
6+
from typing_extensions import TypeAlias, TypeVarTuple, Unpack
77

88
from gevent._types import _AsyncWatcher, _Callback, _ChildWatcher, _IoWatcher, _StatWatcher, _TimerWatcher, _Watcher
99

10-
_P = ParamSpec("_P")
10+
_Ts = TypeVarTuple("_Ts")
1111
_ErrorHandlerFunc: TypeAlias = Callable[
1212
[object | None, type[BaseException] | None, BaseException | None, TracebackType | None], object
1313
]
@@ -74,10 +74,7 @@ class AbstractLoop:
7474

7575
def async_(self, ref: bool = True, priority: int | None = None) -> _AsyncWatcher: ...
7676
def stat(self, path: str, interval: float = 0.0, ref: bool = True, priority: bool | None = ...) -> _StatWatcher: ...
77-
# These technically don't allow the functions arguments to be passed in as kwargs
78-
# but there's no way to express that yet with ParamSpec, however, we would still like
79-
# to verify that the arguments match
80-
def run_callback(self, func: Callable[_P, object], *args: _P.args, **_: _P.kwargs) -> _Callback: ...
81-
def run_callback_threadsafe(self, func: Callable[_P, object], *args: _P.args, **_: _P.kwargs) -> _Callback: ...
77+
def run_callback(self, func: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> _Callback: ...
78+
def run_callback_threadsafe(self, func: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> _Callback: ...
8279
def callback(self, priority: float | None = ...) -> _Callback: ...
8380
def fileno(self) -> FileDescriptor | None: ...

stubs/gevent/gevent/_ffi/watcher.pyi

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ from _typeshed import FileDescriptor, StrOrBytesPath
22
from collections.abc import Callable
33
from types import TracebackType
44
from typing import Any, overload
5-
from typing_extensions import Concatenate, Literal, ParamSpec, Self
5+
from typing_extensions import Literal, Self, TypeVarTuple, Unpack
66

77
from gevent._types import _Loop, _StatResult
88

9-
_P = ParamSpec("_P")
9+
_Ts = TypeVarTuple("_Ts")
1010

1111
class AbstractWatcherType(type):
1212
def new_handle(cls, obj: object) -> int: ...
@@ -22,7 +22,7 @@ class watcher(metaclass=AbstractWatcherType):
2222
def ref(self) -> bool: ...
2323
callback: Callable[..., Any]
2424
args: tuple[Any, ...]
25-
def start(self, callback: Callable[_P, Any], *args: _P.args, **_: _P.kwargs) -> None: ...
25+
def start(self, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ...
2626
def stop(self) -> None: ...
2727
@property
2828
def priority(self) -> int | None: ...
@@ -36,27 +36,23 @@ class watcher(metaclass=AbstractWatcherType):
3636
class IoMixin:
3737
EVENT_MASK: int
3838
def __init__(self, loop: _Loop, fd: FileDescriptor, events: int, ref: bool = True, priority: int | None = None) -> None: ...
39-
# pass_events means the first argument of the callback needs to be an integer, but we can't
40-
# type check the other passed in args in this case
4139
@overload
42-
def start(self, callback: Callable[Concatenate[int, _P], Any], *args: Any, pass_events: Literal[True]) -> None: ...
40+
def start(self, callback: Callable[[int, Unpack[_Ts]], Any], *args: Unpack[_Ts], pass_events: Literal[True]) -> None: ...
4341
@overload
44-
def start(self, callback: Callable[_P, Any], *args: _P.args, **_: _P.kwargs) -> None: ...
42+
def start(self, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ...
4543

4644
class TimerMixin:
4745
def __init__(
4846
self, loop: _Loop, after: float = 0.0, repeat: float = 0.0, ref: bool = True, priority: int | None = None
4947
) -> None: ...
50-
# this has one specific allowed keyword argument, if it is given we don't try to check
51-
# the passed in arguments, but if it isn't passed in, then we do.
5248
@overload
53-
def start(self, callback: Callable[..., Any], *args: Any, update: bool) -> None: ...
49+
def start(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], update: bool) -> None: ...
5450
@overload
55-
def start(self, callback: Callable[_P, Any], *args: _P.args, **_: _P.kwargs) -> None: ...
51+
def start(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ...
5652
@overload
57-
def again(self, callback: Callable[..., Any], *args: Any, update: bool) -> None: ...
53+
def again(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], update: bool) -> None: ...
5854
@overload
59-
def again(self, callback: Callable[_P, Any], *args: _P.args, **_: _P.kwargs) -> None: ...
55+
def again(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ...
6056

6157
class SignalMixin:
6258
def __init__(self, loop: _Loop, signalnum: int, ref: bool = True, priority: int | None = None) -> None: ...

stubs/gevent/gevent/_hub_primitives.pyi

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ from _typeshed import FileDescriptor
22
from collections.abc import Callable, Collection, Iterable
33
from types import TracebackType
44
from typing import Any, Generic, Protocol, TypeVar, overload
5-
from typing_extensions import ParamSpec, Self
5+
from typing_extensions import Self, TypeVarTuple, Unpack
66

77
from gevent._greenlet_primitives import SwitchOutGreenletWithLoop
88
from gevent._types import _Loop, _Watcher
@@ -12,8 +12,8 @@ from gevent.socket import socket
1212
__all__ = ["WaitOperationsGreenlet", "iwait_on_objects", "wait_on_objects", "wait_read", "wait_write", "wait_readwrite"]
1313

1414
_T = TypeVar("_T")
15+
_Ts = TypeVarTuple("_Ts")
1516
_WaitableT = TypeVar("_WaitableT", bound=_Waitable)
16-
_P = ParamSpec("_P")
1717

1818
class _Waitable(Protocol):
1919
def rawlink(self, __callback: Callable[[Any], object]) -> object: ...
@@ -22,14 +22,12 @@ class _Waitable(Protocol):
2222
class WaitOperationsGreenlet(SwitchOutGreenletWithLoop):
2323
loop: _Loop
2424
def wait(self, watcher: _Watcher) -> None: ...
25-
# These then doesn't allow keyword arguments, but ParamSpec doesn't allow for that
2625
def cancel_waits_close_and_then(
2726
self,
2827
watchers: Iterable[_Watcher],
2928
exc_kind: type[BaseException] | BaseException,
30-
then: Callable[_P, object],
31-
*then_args: _P.args,
32-
**_: _P.kwargs,
29+
then: Callable[[Unpack[_Ts]], object],
30+
*then_args: Unpack[_Ts],
3331
) -> None: ...
3432
def cancel_wait(self, watcher: _Watcher, error: type[BaseException] | BaseException, close_watcher: bool = False) -> None: ...
3533

stubs/gevent/gevent/_types.pyi

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ from _typeshed import FileDescriptor, StrOrBytesPath
33
from collections.abc import Callable
44
from types import TracebackType
55
from typing import Any, Protocol, overload
6-
from typing_extensions import Concatenate, Literal, ParamSpec, TypeAlias
6+
from typing_extensions import Literal, TypeAlias, TypeVarTuple, Unpack
77

8-
_P = ParamSpec("_P")
8+
_Ts = TypeVarTuple("_Ts")
99

1010
# gevent uses zope.interface interanlly which does not work well with type checkers
1111
# partially due to the odd call signatures without self and partially due to them
@@ -62,16 +62,13 @@ class _Loop(Protocol): # noqa: Y046
6262

6363
def async_(self, ref: bool = True, priority: int | None = None) -> _AsyncWatcher: ...
6464
def stat(self, path: str, interval: float = 0.0, ref: bool = True, priority: bool | None = ...) -> _StatWatcher: ...
65-
# These technically don't allow the functions arguments to be passed in as kwargs
66-
# but there's no way to express that yet with ParamSpec, however, we would still like
67-
# to verify that the arguments match
68-
def run_callback(self, func: Callable[_P, Any], *args: _P.args, **_: _P.kwargs) -> _Callback: ...
69-
def run_callback_threadsafe(self, func: Callable[_P, Any], *args: _P.args, **_: _P.kwargs) -> _Callback: ...
65+
def run_callback(self, func: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> _Callback: ...
66+
def run_callback_threadsafe(self, func: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> _Callback: ...
7067
def fileno(self) -> FileDescriptor | None: ...
7168

7269
class _Watcher(Protocol):
7370
# while IWatcher allows for kwargs the actual implementation does not...
74-
def start(self, callback: Callable[_P, Any], *args: _P.args, **_: _P.kwargs) -> None: ...
71+
def start(self, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ...
7572
def stop(self) -> None: ...
7673
def close(self) -> None: ...
7774

@@ -80,23 +77,23 @@ class _TimerWatcher(_Watcher, Protocol):
8077
# this has one specific allowed keyword argument, if it is given we don't try to check
8178
# the passed in arguments, but if it isn't passed in, then we do.
8279
@overload
83-
def start(self, callback: Callable[..., Any], *args: Any, update: bool) -> None: ...
80+
def start(self, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts], update: bool) -> None: ...
8481
@overload
85-
def start(self, callback: Callable[_P, Any], *args: _P.args, **_: _P.kwargs) -> None: ...
82+
def start(self, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ...
8683
@overload
87-
def again(self, callback: Callable[..., Any], *args: Any, update: bool) -> None: ...
84+
def again(self, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts], update: bool) -> None: ...
8885
@overload
89-
def again(self, callback: Callable[_P, Any], *args: _P.args, **_: _P.kwargs) -> None: ...
86+
def again(self, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ...
9087

9188
# this matches Intersection[_Watcher, IoMixin]
9289
class _IoWatcher(_Watcher, Protocol):
9390
EVENT_MASK: int
9491
# pass_events means the first argument of the callback needs to be an integer, but we can't
9592
# type check the other passed in args in this case
9693
@overload
97-
def start(self, callback: Callable[Concatenate[int, _P], Any], *args: Any, pass_events: Literal[True]) -> None: ...
94+
def start(self, callback: Callable[[int, Unpack[_Ts]], Any], *args: Unpack[_Ts], pass_events: Literal[True]) -> None: ...
9895
@overload
99-
def start(self, callback: Callable[_P, Any], *args: _P.args, **_: _P.kwargs) -> None: ...
96+
def start(self, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ...
10097

10198
# this matches Intersection[_Watcher, ChildMixin]
10299
class _ChildWatcher(_Watcher, Protocol):

0 commit comments

Comments
 (0)