From 2609fa36314fc621798e4c92f4ff63973d81b11a Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 1 Mar 2022 12:21:58 +0000 Subject: [PATCH 1/6] `asyncio.unix_events`: Make `AbstractChildWatcher` abstract, add `PidfdChildWatcher` --- stdlib/asyncio/unix_events.pyi | 51 +++++++++++++++++++++- tests/stubtest_allowlists/darwin-py310.txt | 2 - tests/stubtest_allowlists/darwin-py39.txt | 2 - tests/stubtest_allowlists/linux-py310.txt | 2 - tests/stubtest_allowlists/linux-py39.txt | 2 - 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/stdlib/asyncio/unix_events.pyi b/stdlib/asyncio/unix_events.pyi index fbf3e12d067d..de36ebeabdcb 100644 --- a/stdlib/asyncio/unix_events.pyi +++ b/stdlib/asyncio/unix_events.pyi @@ -1,8 +1,10 @@ import sys import types from _typeshed import Self +from abc import ABCMeta, abstractmethod from socket import socket from typing import Any, Callable +from typing_extensions import Literal from .base_events import Server from .events import AbstractEventLoop, BaseDefaultEventLoopPolicy, _ProtocolFactory, _SSLContext @@ -12,13 +14,20 @@ from .selector_events import BaseSelectorEventLoop # but other parts of typeshed need this defintion. # So, it is special cased. class AbstractChildWatcher: + @abstractmethod def add_child_handler(self, pid: int, callback: Callable[..., Any], *args: Any) -> None: ... + @abstractmethod def remove_child_handler(self, pid: int) -> bool: ... + @abstractmethod def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + @abstractmethod def close(self) -> None: ... + @abstractmethod def __enter__(self: Self) -> Self: ... + @abstractmethod def __exit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None) -> None: ... if sys.version_info >= (3, 8): + @abstractmethod def is_active(self) -> bool: ... if sys.platform != "win32": @@ -48,14 +57,23 @@ if sys.platform != "win32": else: __all__ = ["SelectorEventLoop", "AbstractChildWatcher", "SafeChildWatcher", "FastChildWatcher", "DefaultEventLoopPolicy"] - class BaseChildWatcher(AbstractChildWatcher): + class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): def __init__(self) -> None: ... + def close(self) -> None: ... + def is_active(self) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... class SafeChildWatcher(BaseChildWatcher): def __enter__(self: Self) -> Self: ... + def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[..., Any], *args: Any) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... class FastChildWatcher(BaseChildWatcher): def __enter__(self: Self) -> Self: ... + def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[..., Any], *args: Any) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... class _UnixSelectorEventLoop(BaseSelectorEventLoop): if sys.version_info < (3, 7): @@ -86,8 +104,39 @@ if sys.platform != "win32": ) -> None: ... class MultiLoopChildWatcher(AbstractChildWatcher): + def __init__(self) -> None: ... + def is_active(self) -> bool: ... + def close(self) -> None: ... def __enter__(self: Self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[..., Any], *args: Any) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... class ThreadedChildWatcher(AbstractChildWatcher): + def __init__(self) -> None: ... + def is_active(self) -> Literal[True]: ... + def close(self) -> None: ... def __enter__(self: Self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... def __del__(self, _warn: _Warn = ...) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[..., Any], *args: Any) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + + if sys.version_info >= (3, 9): + class PidfdChildWatcher(AbstractChildWatcher): + def __init__(self) -> None: ... + def __enter__(self: Self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... + def is_active(self) -> bool: ... + def close(self) -> None: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[..., Any], *args: Any) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... diff --git a/tests/stubtest_allowlists/darwin-py310.txt b/tests/stubtest_allowlists/darwin-py310.txt index dc2d08fc393e..8a3185cd8c13 100644 --- a/tests/stubtest_allowlists/darwin-py310.txt +++ b/tests/stubtest_allowlists/darwin-py310.txt @@ -3,6 +3,4 @@ _curses.color_pair curses.color_pair # Exists at runtime, but missing from stubs -asyncio.PidfdChildWatcher -asyncio.unix_events.PidfdChildWatcher mmap.MADV_FREE diff --git a/tests/stubtest_allowlists/darwin-py39.txt b/tests/stubtest_allowlists/darwin-py39.txt index 84829dbb446a..1b9e1e10eb83 100644 --- a/tests/stubtest_allowlists/darwin-py39.txt +++ b/tests/stubtest_allowlists/darwin-py39.txt @@ -1,6 +1,4 @@ _?curses.A_ITALIC # Exists at runtime, but missing from stubs -asyncio.PidfdChildWatcher -asyncio.unix_events.PidfdChildWatcher mmap.MADV_FREE diff --git a/tests/stubtest_allowlists/linux-py310.txt b/tests/stubtest_allowlists/linux-py310.txt index a9d097674c10..d435cdd0a567 100644 --- a/tests/stubtest_allowlists/linux-py310.txt +++ b/tests/stubtest_allowlists/linux-py310.txt @@ -18,8 +18,6 @@ signal.sigwaitinfo select.epoll.register # Exists at runtime, but missing from stubs -asyncio.PidfdChildWatcher -asyncio.unix_events.PidfdChildWatcher os.EFD_CLOEXEC os.EFD_NONBLOCK os.EFD_SEMAPHORE diff --git a/tests/stubtest_allowlists/linux-py39.txt b/tests/stubtest_allowlists/linux-py39.txt index dff92f9e8379..125db92f25ae 100644 --- a/tests/stubtest_allowlists/linux-py39.txt +++ b/tests/stubtest_allowlists/linux-py39.txt @@ -2,8 +2,6 @@ select.epoll.register # Exists at runtime, but missing from stubs -asyncio.PidfdChildWatcher -asyncio.unix_events.PidfdChildWatcher os.copy_file_range os.pidfd_open posix.copy_file_range From 7732a6f67637657da213b1125c0bbe8b69857c24 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 1 Mar 2022 12:26:41 +0000 Subject: [PATCH 2/6] py37 compat --- stdlib/asyncio/unix_events.pyi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/asyncio/unix_events.pyi b/stdlib/asyncio/unix_events.pyi index de36ebeabdcb..c70df93ed915 100644 --- a/stdlib/asyncio/unix_events.pyi +++ b/stdlib/asyncio/unix_events.pyi @@ -60,7 +60,9 @@ if sys.platform != "win32": class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): def __init__(self) -> None: ... def close(self) -> None: ... - def is_active(self) -> bool: ... + if sys.version_info >= (3, 8): + def is_active(self) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... class SafeChildWatcher(BaseChildWatcher): From a873674eace0bb0f7103cf90a73488f557fa6ce1 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 1 Mar 2022 14:01:21 +0000 Subject: [PATCH 3/6] `# type: ignore` instead --- stdlib/asyncio/unix_events.pyi | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stdlib/asyncio/unix_events.pyi b/stdlib/asyncio/unix_events.pyi index c70df93ed915..974d6cc2587f 100644 --- a/stdlib/asyncio/unix_events.pyi +++ b/stdlib/asyncio/unix_events.pyi @@ -1,7 +1,7 @@ import sys import types from _typeshed import Self -from abc import ABCMeta, abstractmethod +from abc import abstractmethod from socket import socket from typing import Any, Callable from typing_extensions import Literal @@ -57,7 +57,11 @@ if sys.platform != "win32": else: __all__ = ["SelectorEventLoop", "AbstractChildWatcher", "SafeChildWatcher", "FastChildWatcher", "DefaultEventLoopPolicy"] - class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): + # `type: ignore[misc]` because the class is still abstract, but does not define any new abstractmethods in the class body. + # mypy assumes the class is meant to be concrete unless we specify `metaclass=ABCMeta` + # (which we don't want to do, as it doesn't match the runtime implementation), + # so raises an error warning us that some abstractmethods from the superclass do not have concrete implementations. + class BaseChildWatcher(AbstractChildWatcher): # type: ignore[misc] def __init__(self) -> None: ... def close(self) -> None: ... if sys.version_info >= (3, 8): From 2fbacc87a85c14225bb14521a047ac76b1798ace Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 1 Mar 2022 14:04:32 +0000 Subject: [PATCH 4/6] Update unix_events.pyi --- stdlib/asyncio/unix_events.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/asyncio/unix_events.pyi b/stdlib/asyncio/unix_events.pyi index 974d6cc2587f..0305adf1228b 100644 --- a/stdlib/asyncio/unix_events.pyi +++ b/stdlib/asyncio/unix_events.pyi @@ -61,7 +61,7 @@ if sys.platform != "win32": # mypy assumes the class is meant to be concrete unless we specify `metaclass=ABCMeta` # (which we don't want to do, as it doesn't match the runtime implementation), # so raises an error warning us that some abstractmethods from the superclass do not have concrete implementations. - class BaseChildWatcher(AbstractChildWatcher): # type: ignore[misc] + class BaseChildWatcher(AbstractChildWatcher): # type: ignore def __init__(self) -> None: ... def close(self) -> None: ... if sys.version_info >= (3, 8): From 3ca70f4aa1d2bdebbcfa824446c78a26ab68f2e6 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 1 Mar 2022 14:19:00 +0000 Subject: [PATCH 5/6] Once more go back to using the metaclass lie --- stdlib/asyncio/unix_events.pyi | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/stdlib/asyncio/unix_events.pyi b/stdlib/asyncio/unix_events.pyi index 0305adf1228b..c70df93ed915 100644 --- a/stdlib/asyncio/unix_events.pyi +++ b/stdlib/asyncio/unix_events.pyi @@ -1,7 +1,7 @@ import sys import types from _typeshed import Self -from abc import abstractmethod +from abc import ABCMeta, abstractmethod from socket import socket from typing import Any, Callable from typing_extensions import Literal @@ -57,11 +57,7 @@ if sys.platform != "win32": else: __all__ = ["SelectorEventLoop", "AbstractChildWatcher", "SafeChildWatcher", "FastChildWatcher", "DefaultEventLoopPolicy"] - # `type: ignore[misc]` because the class is still abstract, but does not define any new abstractmethods in the class body. - # mypy assumes the class is meant to be concrete unless we specify `metaclass=ABCMeta` - # (which we don't want to do, as it doesn't match the runtime implementation), - # so raises an error warning us that some abstractmethods from the superclass do not have concrete implementations. - class BaseChildWatcher(AbstractChildWatcher): # type: ignore + class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): def __init__(self) -> None: ... def close(self) -> None: ... if sys.version_info >= (3, 8): From 94c986cc5ac291290bed14f93f0277c62b7e60e7 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 1 Mar 2022 15:05:36 +0000 Subject: [PATCH 6/6] Update stdlib/asyncio/unix_events.pyi Co-authored-by: Sebastian Rittau --- stdlib/asyncio/unix_events.pyi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/asyncio/unix_events.pyi b/stdlib/asyncio/unix_events.pyi index c70df93ed915..b8c569b10cd4 100644 --- a/stdlib/asyncio/unix_events.pyi +++ b/stdlib/asyncio/unix_events.pyi @@ -57,6 +57,8 @@ if sys.platform != "win32": else: __all__ = ["SelectorEventLoop", "AbstractChildWatcher", "SafeChildWatcher", "FastChildWatcher", "DefaultEventLoopPolicy"] + # Doesn't actually have ABCMeta metaclass at runtime, but mypy complains if we don't have it in the stub. + # See discussion in #7412 class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): def __init__(self) -> None: ... def close(self) -> None: ...