diff --git a/pyrightconfig.stricter.json b/pyrightconfig.stricter.json index caae17851..8e55ff80b 100644 --- a/pyrightconfig.stricter.json +++ b/pyrightconfig.stricter.json @@ -9,6 +9,7 @@ "**/@python2", "stdlib/distutils/command", "stdlib/lib2to3/refactor.pyi", + "stdlib/multiprocessing/reduction.pyi", "stdlib/sqlite3/dbapi2.pyi", "stdlib/_tkinter.pyi", "stdlib/tkinter", diff --git a/stdlib/copyreg.pyi b/stdlib/copyreg.pyi index 401144a95..d9d7a7cef 100644 --- a/stdlib/copyreg.pyi +++ b/stdlib/copyreg.pyi @@ -15,4 +15,5 @@ def add_extension(module: Hashable, name: Hashable, code: SupportsInt) -> None: def remove_extension(module: Hashable, name: Hashable, code: int) -> None: ... def clear_extension_cache() -> None: ... -dispatch_table: dict[type, Callable[[type], str | _Reduce[type]]] # undocumented +_DispatchTableType = dict[type, Callable[[type], str | _Reduce[type]]] # imported by multiprocessing.reduction +dispatch_table: _DispatchTableType # undocumented diff --git a/stdlib/multiprocessing/__init__.pyi b/stdlib/multiprocessing/__init__.pyi index 979176c47..1836017ed 100644 --- a/stdlib/multiprocessing/__init__.pyi +++ b/stdlib/multiprocessing/__init__.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable, Iterable from logging import Logger -from multiprocessing import connection, context, pool, synchronize +from multiprocessing import connection, context, pool, reduction as reducer, synchronize from multiprocessing.context import ( AuthenticationError as AuthenticationError, BaseContext, diff --git a/stdlib/multiprocessing/reduction.pyi b/stdlib/multiprocessing/reduction.pyi new file mode 100644 index 000000000..9e7387da6 --- /dev/null +++ b/stdlib/multiprocessing/reduction.pyi @@ -0,0 +1,84 @@ +import pickle +import sys +from abc import ABCMeta +from copyreg import _DispatchTableType +from typing import Any +from typing_extensions import Literal + +if sys.platform == "win32": + __all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupHandle", "duplicate", "steal_handle"] +else: + __all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupFd", "sendfds", "recvfds"] + +class ForkingPickler(pickle.Pickler): + dispatch_table: _DispatchTableType + def __init__(self, *args) -> None: ... + @classmethod + def register(cls, type, reduce) -> None: ... + @classmethod + def dumps(cls, obj, protocol: Any | None = ...): ... + loads = pickle.loads + +register = ForkingPickler.register + +def dump(obj, file, protocol: Any | None = ...) -> None: ... + +if sys.platform == "win32": + if sys.version_info >= (3, 8): + def duplicate(handle, target_process: Any | None = ..., inheritable: bool = ..., *, source_process: Any | None = ...): ... + else: + def duplicate(handle, target_process: Any | None = ..., inheritable: bool = ...): ... + + def steal_handle(source_pid, handle): ... + def send_handle(conn, handle, destination_pid) -> None: ... + def recv_handle(conn): ... + + class DupHandle: + def __init__(self, handle, access, pid: Any | None = ...) -> None: ... + def detach(self): ... + +else: + if sys.platform == "darwin": + ACKNOWLEDGE: Literal[True] + else: + ACKNOWLEDGE: Literal[False] + + def recvfds(sock, size): ... + def send_handle(conn, handle, destination_pid) -> None: ... + def recv_handle(conn) -> None: ... + def sendfds(sock, fds) -> None: ... + def DupFd(fd): ... + +# These aliases are to work around pyright complaints. +# Pyright doesn't like it when a class object is defined as an alias +# of a global object with the same name. +_ForkingPickler = ForkingPickler +_register = register +_dump = dump +_send_handle = send_handle +_recv_handle = recv_handle + +if sys.platform == "win32": + _steal_handle = steal_handle + _duplicate = duplicate + _DupHandle = DupHandle +else: + _sendfds = sendfds + _recvfds = recvfds + _DupFd = DupFd + +class AbstractReducer(metaclass=ABCMeta): + ForkingPickler = _ForkingPickler + register = _register + dump = _dump + send_handle = _send_handle + recv_handle = _recv_handle + if sys.platform == "win32": + steal_handle = _steal_handle + duplicate = _duplicate + DupHandle = _DupHandle + else: + sendfds = _sendfds + recvfds = _recvfds + DupFd = _DupFd + def __init__(self, *args) -> None: ... diff --git a/tests/stubtest_allowlists/py3_common.txt b/tests/stubtest_allowlists/py3_common.txt index 472696c7b..dfbbf8d43 100644 --- a/tests/stubtest_allowlists/py3_common.txt +++ b/tests/stubtest_allowlists/py3_common.txt @@ -200,6 +200,8 @@ threading.Lock # A factory function that returns 'most efficient lock'. Marking threading.RLock # Similar to above multiprocessing.dummy.Lock # Similar to above multiprocessing.dummy.RLock # Similar to above +# alias for a class defined elsewhere, mypy infers the variable has type `(*args) -> ForkingPickler` but stubtest infers the runtime type as +multiprocessing.reduction.AbstractReducer.ForkingPickler tkinter.Misc.grid_propagate # The noarg placeholder is a set value list tkinter.Misc.pack_propagate # The noarg placeholder is a set value list tkinter.Tk.eval # from __getattr__ @@ -699,7 +701,6 @@ multiprocessing.managers.SyncManager.JoinableQueue multiprocessing.managers.SyncManager.Pool multiprocessing.pool.Pool.Process multiprocessing.pool.ThreadPool.Process -multiprocessing.reducer multiprocessing.synchronize.Semaphore.get_value rlcompleter.Completer.attr_matches rlcompleter.Completer.global_matches diff --git a/tests/stubtest_allowlists/win32.txt b/tests/stubtest_allowlists/win32.txt index f95a753f0..6f25a3ad7 100644 --- a/tests/stubtest_allowlists/win32.txt +++ b/tests/stubtest_allowlists/win32.txt @@ -7,6 +7,9 @@ distutils.msvccompiler.HKEYS locale.[A-Z0-9_]+ # Constants that should be moved to _locale and re-exported conditionally locale.nl_langinfo # Function that should be moved to _locale and re-exported conditionally mmap.PAGESIZE +# alias for a class defined elsewhere, +# mypy infers the variable has type `(*args) -> DupHandle` but stubtest infers the runtime type as +multiprocessing.reduction.AbstractReducer.DupHandle msilib.MSI[A-Z_]+ msilib.text.dirname msilib.PID_[A-Z_]+