From 423864e575a960af41e2464ee5b4d4f488931f34 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Thu, 17 Aug 2023 14:39:56 +0200 Subject: [PATCH 01/15] add TODO markers for all missing docstrings --- trio/_core/_generated_io_epoll.py | 3 +++ trio/_core/_generated_io_kqueue.py | 6 ++++++ trio/_core/_generated_io_windows.py | 9 +++++++++ trio/_core/_io_epoll.py | 3 +++ trio/_core/_io_kqueue.py | 6 ++++++ trio/_core/_io_windows.py | 9 +++++++++ trio/_subprocess.py | 3 +++ trio/_tests/verify_types.json | 10 +++------- trio/testing/_trio_test.py | 2 ++ trio/tests.py | 2 ++ 10 files changed, 46 insertions(+), 7 deletions(-) diff --git a/trio/_core/_generated_io_epoll.py b/trio/_core/_generated_io_epoll.py index abe49ed3ff..adbde68dd6 100644 --- a/trio/_core/_generated_io_epoll.py +++ b/trio/_core/_generated_io_epoll.py @@ -16,6 +16,7 @@ async def wait_readable(fd: (int | socket)) ->None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd) @@ -24,6 +25,7 @@ async def wait_readable(fd: (int | socket)) ->None: async def wait_writable(fd: (int | socket)) ->None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(fd) @@ -32,6 +34,7 @@ async def wait_writable(fd: (int | socket)) ->None: def notify_closing(fd: (int | socket)) ->None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd) diff --git a/trio/_core/_generated_io_kqueue.py b/trio/_core/_generated_io_kqueue.py index cfcf6354c7..20f75b434b 100644 --- a/trio/_core/_generated_io_kqueue.py +++ b/trio/_core/_generated_io_kqueue.py @@ -24,6 +24,7 @@ def current_kqueue() ->select.kqueue: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.current_kqueue() @@ -33,6 +34,7 @@ def current_kqueue() ->select.kqueue: def monitor_kevent(ident: int, filter: int) ->ContextManager[_core.UnboundedQueue [select.kevent]]: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.monitor_kevent(ident, filter) @@ -42,6 +44,7 @@ def monitor_kevent(ident: int, filter: int) ->ContextManager[_core.UnboundedQueu async def wait_kevent(ident: int, filter: int, abort_func: Callable[[ RaiseCancelT], Abort]) ->Abort: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_kevent(ident, filter, abort_func) @@ -50,6 +53,7 @@ async def wait_kevent(ident: int, filter: int, abort_func: Callable[[ async def wait_readable(fd: (int | socket)) ->None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd) @@ -58,6 +62,7 @@ async def wait_readable(fd: (int | socket)) ->None: async def wait_writable(fd: (int | socket)) ->None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(fd) @@ -66,6 +71,7 @@ async def wait_writable(fd: (int | socket)) ->None: def notify_closing(fd: (int | socket)) ->None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd) diff --git a/trio/_core/_generated_io_windows.py b/trio/_core/_generated_io_windows.py index 7fa6fd5126..aba3972ada 100644 --- a/trio/_core/_generated_io_windows.py +++ b/trio/_core/_generated_io_windows.py @@ -15,6 +15,7 @@ async def wait_readable(sock): + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(sock) @@ -23,6 +24,7 @@ async def wait_readable(sock): async def wait_writable(sock): + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(sock) @@ -31,6 +33,7 @@ async def wait_writable(sock): def notify_closing(handle): + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(handle) @@ -39,6 +42,7 @@ def notify_closing(handle): def register_with_iocp(handle): + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.register_with_iocp(handle) @@ -47,6 +51,7 @@ def register_with_iocp(handle): async def wait_overlapped(handle, lpOverlapped): + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_overlapped(handle, lpOverlapped) @@ -55,6 +60,7 @@ async def wait_overlapped(handle, lpOverlapped): async def write_overlapped(handle, data, file_offset=0): + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.write_overlapped(handle, data, file_offset) @@ -63,6 +69,7 @@ async def write_overlapped(handle, data, file_offset=0): async def readinto_overlapped(handle, buffer, file_offset=0): + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.readinto_overlapped(handle, buffer, file_offset) @@ -71,6 +78,7 @@ async def readinto_overlapped(handle, buffer, file_offset=0): def current_iocp(): + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.current_iocp() @@ -79,6 +87,7 @@ def current_iocp(): def monitor_completion_key(): + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.monitor_completion_key() diff --git a/trio/_core/_io_epoll.py b/trio/_core/_io_epoll.py index 0d247cae64..0313ceec82 100644 --- a/trio/_core/_io_epoll.py +++ b/trio/_core/_io_epoll.py @@ -310,14 +310,17 @@ def abort(_: RaiseCancelT) -> Abort: @_public async def wait_readable(self, fd: int | socket) -> None: + """TODO""" await self._epoll_wait(fd, "read_task") @_public async def wait_writable(self, fd: int | socket) -> None: + """TODO""" await self._epoll_wait(fd, "write_task") @_public def notify_closing(self, fd: int | socket) -> None: + """TODO""" if not isinstance(fd, int): fd = fd.fileno() wake_all( diff --git a/trio/_core/_io_kqueue.py b/trio/_core/_io_kqueue.py index 56a6559091..8abc1a3db4 100644 --- a/trio/_core/_io_kqueue.py +++ b/trio/_core/_io_kqueue.py @@ -110,6 +110,7 @@ def process_events(self, events: EventResult) -> None: @_public def current_kqueue(self) -> select.kqueue: + """TODO""" return self._kqueue @contextmanager @@ -117,6 +118,7 @@ def current_kqueue(self) -> select.kqueue: def monitor_kevent( self, ident: int, filter: int ) -> Iterator[_core.UnboundedQueue[select.kevent]]: + """TODO""" key = (ident, filter) if key in self._registered: raise _core.BusyResourceError( @@ -133,6 +135,7 @@ def monitor_kevent( async def wait_kevent( self, ident: int, filter: int, abort_func: Callable[[RaiseCancelT], Abort] ) -> Abort: + """TODO""" key = (ident, filter) if key in self._registered: raise _core.BusyResourceError( @@ -182,14 +185,17 @@ def abort(_: RaiseCancelT) -> Abort: @_public async def wait_readable(self, fd: int | socket) -> None: + """TODO""" await self._wait_common(fd, select.KQ_FILTER_READ) @_public async def wait_writable(self, fd: int | socket) -> None: + """TODO""" await self._wait_common(fd, select.KQ_FILTER_WRITE) @_public def notify_closing(self, fd: int | socket) -> None: + """TODO""" if not isinstance(fd, int): fd = fd.fileno() diff --git a/trio/_core/_io_windows.py b/trio/_core/_io_windows.py index 9757d25b5f..c5c2666c6a 100644 --- a/trio/_core/_io_windows.py +++ b/trio/_core/_io_windows.py @@ -705,14 +705,17 @@ def abort_fn(_): @_public async def wait_readable(self, sock): + """TODO""" await self._afd_poll(sock, "read_task") @_public async def wait_writable(self, sock): + """TODO""" await self._afd_poll(sock, "write_task") @_public def notify_closing(self, handle): + """TODO""" handle = _get_base_socket(handle) waiters = self._afd_waiters.get(handle) if waiters is not None: @@ -725,10 +728,12 @@ def notify_closing(self, handle): @_public def register_with_iocp(self, handle): + """TODO""" self._register_with_iocp(handle, CKeys.WAIT_OVERLAPPED) @_public async def wait_overlapped(self, handle, lpOverlapped): + """TODO""" handle = _handle(handle) if isinstance(lpOverlapped, int): lpOverlapped = ffi.cast("LPOVERLAPPED", lpOverlapped) @@ -813,6 +818,7 @@ async def _perform_overlapped(self, handle, submit_fn): @_public async def write_overlapped(self, handle, data, file_offset=0): + """TODO""" with ffi.from_buffer(data) as cbuf: def submit_write(lpOverlapped): @@ -836,6 +842,7 @@ def submit_write(lpOverlapped): @_public async def readinto_overlapped(self, handle, buffer, file_offset=0): + """TODO""" with ffi.from_buffer(buffer, require_writable=True) as cbuf: def submit_read(lpOverlapped): @@ -861,11 +868,13 @@ def submit_read(lpOverlapped): @_public def current_iocp(self): + """TODO""" return int(ffi.cast("uintptr_t", self._iocp)) @contextmanager @_public def monitor_completion_key(self): + """TODO""" key = next(self._completion_key_counter) queue = _core.UnboundedQueue() self._completion_key_queues[key] = queue diff --git a/trio/_subprocess.py b/trio/_subprocess.py index 7cf990fa53..d463c141c2 100644 --- a/trio/_subprocess.py +++ b/trio/_subprocess.py @@ -807,6 +807,7 @@ async def open_process( startupinfo: subprocess.STARTUPINFO | None = None, creationflags: int = 0, ) -> trio.Process: + """TODO: Lost the docstring defined above""" ... async def run_process( @@ -827,9 +828,11 @@ async def run_process( startupinfo: subprocess.STARTUPINFO | None = None, creationflags: int = 0, ) -> subprocess.CompletedProcess[bytes]: + """TODO: Lost the docstring defined above""" ... else: # Unix + # TODO: These functions are not seen by pyright (and mypy?) @overload # type: ignore[no-overload-impl] async def open_process( diff --git a/trio/_tests/verify_types.json b/trio/_tests/verify_types.json index c5e9c4dc66..ed588c2976 100644 --- a/trio/_tests/verify_types.json +++ b/trio/_tests/verify_types.json @@ -14,9 +14,9 @@ "withUnknownType": 22 }, "ignoreUnknownTypesFromImports": true, - "missingClassDocStringCount": 1, + "missingClassDocStringCount": 0, "missingDefaultParamCount": 0, - "missingFunctionDocStringCount": 4, + "missingFunctionDocStringCount": 0, "moduleName": "trio", "modules": [ { @@ -68,9 +68,6 @@ "trio._ssl.SSLStream.transport_stream", "trio._ssl.SSLStream.unwrap", "trio._ssl.SSLStream.wait_send_all_might_not_block", - "trio.lowlevel.notify_closing", - "trio.lowlevel.wait_readable", - "trio.lowlevel.wait_writable", "trio.open_ssl_over_tcp_listeners", "trio.open_ssl_over_tcp_stream", "trio.open_unix_socket", @@ -105,8 +102,7 @@ "trio.testing.memory_stream_pair", "trio.testing.memory_stream_pump", "trio.testing.open_stream_to_socket_listener", - "trio.testing.trio_test", - "trio.tests.TestsDeprecationWrapper" + "trio.testing.trio_test" ] } } diff --git a/trio/testing/_trio_test.py b/trio/testing/_trio_test.py index b4ef69ef09..f72a8eac5e 100644 --- a/trio/testing/_trio_test.py +++ b/trio/testing/_trio_test.py @@ -13,6 +13,8 @@ # Also: if a pytest fixture is passed in that subclasses the Clock abc, then # that clock is passed to trio.run(). def trio_test(fn): + """Wrap an async test so it can be run synchronously by pytest.""" + @wraps(fn) def wrapper(**kwargs): __tracebackhide__ = True diff --git a/trio/tests.py b/trio/tests.py index 4ffb583a3a..1c5f039f0f 100644 --- a/trio/tests.py +++ b/trio/tests.py @@ -16,6 +16,8 @@ # This won't give deprecation warning on import, but will give a warning on use of any # attribute in tests, and static analysis tools will also not see any content inside. class TestsDeprecationWrapper: + """trio.tests is deprecated, use trio._tests""" + __name__ = "trio.tests" def __getattr__(self, attr: str) -> Any: From 6dd575317102f0636b85f8a680ee086c14b7000e Mon Sep 17 00:00:00 2001 From: jakkdl Date: Tue, 26 Sep 2023 11:40:55 +0200 Subject: [PATCH 02/15] re-add placeholder docstrings lost when merging --- trio/_core/_generated_io_epoll.py | 3 ++ trio/_core/_generated_io_kqueue.py | 6 +++ trio/_core/_generated_io_windows.py | 9 +++++ trio/_core/_io_windows.py | 9 +++++ trio/_tests/verify_types_darwin.json | 37 ++--------------- trio/_tests/verify_types_linux.json | 25 ++---------- trio/_tests/verify_types_windows.json | 57 ++------------------------- 7 files changed, 39 insertions(+), 107 deletions(-) diff --git a/trio/_core/_generated_io_epoll.py b/trio/_core/_generated_io_epoll.py index 4dc2b59c98..4c6e395351 100644 --- a/trio/_core/_generated_io_epoll.py +++ b/trio/_core/_generated_io_epoll.py @@ -14,6 +14,7 @@ async def wait_readable(fd: (int | socket)) -> None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd) @@ -22,6 +23,7 @@ async def wait_readable(fd: (int | socket)) -> None: async def wait_writable(fd: (int | socket)) -> None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(fd) @@ -30,6 +32,7 @@ async def wait_writable(fd: (int | socket)) -> None: def notify_closing(fd: (int | socket)) -> None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd) diff --git a/trio/_core/_generated_io_kqueue.py b/trio/_core/_generated_io_kqueue.py index 9c8ca26ef3..d976703708 100644 --- a/trio/_core/_generated_io_kqueue.py +++ b/trio/_core/_generated_io_kqueue.py @@ -22,6 +22,7 @@ def current_kqueue() -> select.kqueue: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.current_kqueue() @@ -32,6 +33,7 @@ def current_kqueue() -> select.kqueue: def monitor_kevent( ident: int, filter: int ) -> ContextManager[_core.UnboundedQueue[select.kevent]]: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.monitor_kevent(ident, filter) @@ -42,6 +44,7 @@ def monitor_kevent( async def wait_kevent( ident: int, filter: int, abort_func: Callable[[RaiseCancelT], Abort] ) -> Abort: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_kevent( @@ -52,6 +55,7 @@ async def wait_kevent( async def wait_readable(fd: (int | socket)) -> None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd) @@ -60,6 +64,7 @@ async def wait_readable(fd: (int | socket)) -> None: async def wait_writable(fd: (int | socket)) -> None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(fd) @@ -68,6 +73,7 @@ async def wait_writable(fd: (int | socket)) -> None: def notify_closing(fd: (int | socket)) -> None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd) diff --git a/trio/_core/_generated_io_windows.py b/trio/_core/_generated_io_windows.py index ca444373fa..a58af98c56 100644 --- a/trio/_core/_generated_io_windows.py +++ b/trio/_core/_generated_io_windows.py @@ -21,6 +21,7 @@ async def wait_readable(sock: (_HasFileNo | int)) -> None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(sock) @@ -29,6 +30,7 @@ async def wait_readable(sock: (_HasFileNo | int)) -> None: async def wait_writable(sock: (_HasFileNo | int)) -> None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(sock) @@ -37,6 +39,7 @@ async def wait_writable(sock: (_HasFileNo | int)) -> None: def notify_closing(handle: (Handle | int | _HasFileNo)) -> None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(handle) @@ -45,6 +48,7 @@ def notify_closing(handle: (Handle | int | _HasFileNo)) -> None: def register_with_iocp(handle: (int | CData)) -> None: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.register_with_iocp(handle) @@ -55,6 +59,7 @@ def register_with_iocp(handle: (int | CData)) -> None: async def wait_overlapped( handle_: (int | CData), lpOverlapped: (CData | int) ) -> object: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_overlapped( @@ -67,6 +72,7 @@ async def wait_overlapped( async def write_overlapped( handle: (int | CData), data: Buffer, file_offset: int = 0 ) -> int: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.write_overlapped( @@ -79,6 +85,7 @@ async def write_overlapped( async def readinto_overlapped( handle: (int | CData), buffer: Buffer, file_offset: int = 0 ) -> int: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.readinto_overlapped( @@ -89,6 +96,7 @@ async def readinto_overlapped( def current_iocp() -> int: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.current_iocp() @@ -97,6 +105,7 @@ def current_iocp() -> int: def monitor_completion_key() -> ContextManager[tuple[int, UnboundedQueue[object]]]: + """TODO""" locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.monitor_completion_key() diff --git a/trio/_core/_io_windows.py b/trio/_core/_io_windows.py index ba84525506..b53d9ead48 100644 --- a/trio/_core/_io_windows.py +++ b/trio/_core/_io_windows.py @@ -729,14 +729,17 @@ def abort_fn(_: RaiseCancelT) -> Abort: @_public async def wait_readable(self, sock: _HasFileNo | int) -> None: + """TODO""" await self._afd_poll(sock, "read_task") @_public async def wait_writable(self, sock: _HasFileNo | int) -> None: + """TODO""" await self._afd_poll(sock, "write_task") @_public def notify_closing(self, handle: Handle | int | _HasFileNo) -> None: + """TODO""" handle = _get_base_socket(handle) waiters = self._afd_waiters.get(handle) if waiters is not None: @@ -749,12 +752,14 @@ def notify_closing(self, handle: Handle | int | _HasFileNo) -> None: @_public def register_with_iocp(self, handle: int | CData) -> None: + """TODO""" self._register_with_iocp(handle, CKeys.WAIT_OVERLAPPED) @_public async def wait_overlapped( self, handle_: int | CData, lpOverlapped: CData | int ) -> object: + """TODO""" handle = _handle(handle_) if isinstance(lpOverlapped, int): lpOverlapped = ffi.cast("LPOVERLAPPED", lpOverlapped) @@ -846,6 +851,7 @@ async def _perform_overlapped( async def write_overlapped( self, handle: int | CData, data: Buffer, file_offset: int = 0 ) -> int: + """TODO""" with ffi.from_buffer(data) as cbuf: def submit_write(lpOverlapped: _Overlapped) -> None: @@ -871,6 +877,7 @@ def submit_write(lpOverlapped: _Overlapped) -> None: async def readinto_overlapped( self, handle: int | CData, buffer: Buffer, file_offset: int = 0 ) -> int: + """TODO""" with ffi.from_buffer(buffer, require_writable=True) as cbuf: def submit_read(lpOverlapped: _Overlapped) -> None: @@ -896,12 +903,14 @@ def submit_read(lpOverlapped: _Overlapped) -> None: @_public def current_iocp(self) -> int: + """TODO""" assert self._iocp is not None return int(ffi.cast("uintptr_t", self._iocp)) @contextmanager @_public def monitor_completion_key(self) -> Iterator[tuple[int, UnboundedQueue[object]]]: + """TODO""" key = next(self._completion_key_counter) queue = _core.UnboundedQueue[object]() self._completion_key_queues[key] = queue diff --git a/trio/_tests/verify_types_darwin.json b/trio/_tests/verify_types_darwin.json index d6ba2e6c2e..53e2f972bf 100644 --- a/trio/_tests/verify_types_darwin.json +++ b/trio/_tests/verify_types_darwin.json @@ -8,45 +8,16 @@ }, "typeCompleteness": { "completenessScore": 1, - "diagnostics": [ - { - "message": "No docstring found for function \"trio.lowlevel.current_kqueue\"", - "name": "trio.lowlevel.current_kqueue" - }, - { - "message": "No docstring found for function \"trio.lowlevel.monitor_kevent\"", - "name": "trio.lowlevel.monitor_kevent" - }, - { - "message": "No docstring found for function \"trio.lowlevel.notify_closing\"", - "name": "trio.lowlevel.notify_closing" - }, - { - "message": "No docstring found for function \"trio.lowlevel.wait_kevent\"", - "name": "trio.lowlevel.wait_kevent" - }, - { - "message": "No docstring found for function \"trio.lowlevel.wait_readable\"", - "name": "trio.lowlevel.wait_readable" - }, - { - "message": "No docstring found for function \"trio.lowlevel.wait_writable\"", - "name": "trio.lowlevel.wait_writable" - }, - { - "message": "No docstring found for class \"trio.tests.TestsDeprecationWrapper\"", - "name": "trio.tests.TestsDeprecationWrapper" - } - ], + "diagnostics": [], "exportedSymbolCounts": { "withAmbiguousType": 0, "withKnownType": 631, "withUnknownType": 0 }, "ignoreUnknownTypesFromImports": true, - "missingClassDocStringCount": 1, + "missingClassDocStringCount": 0, "missingDefaultParamCount": 0, - "missingFunctionDocStringCount": 6, + "missingFunctionDocStringCount": 0, "moduleName": "trio", "modules": [ { @@ -76,7 +47,7 @@ ], "otherSymbolCounts": { "withAmbiguousType": 0, - "withKnownType": 684, + "withKnownType": 685, "withUnknownType": 0 }, "packageName": "trio" diff --git a/trio/_tests/verify_types_linux.json b/trio/_tests/verify_types_linux.json index 5b1233d2b2..fde209a6eb 100644 --- a/trio/_tests/verify_types_linux.json +++ b/trio/_tests/verify_types_linux.json @@ -8,33 +8,16 @@ }, "typeCompleteness": { "completenessScore": 1, - "diagnostics": [ - { - "message": "No docstring found for function \"trio.lowlevel.notify_closing\"", - "name": "trio.lowlevel.notify_closing" - }, - { - "message": "No docstring found for function \"trio.lowlevel.wait_readable\"", - "name": "trio.lowlevel.wait_readable" - }, - { - "message": "No docstring found for function \"trio.lowlevel.wait_writable\"", - "name": "trio.lowlevel.wait_writable" - }, - { - "message": "No docstring found for class \"trio.tests.TestsDeprecationWrapper\"", - "name": "trio.tests.TestsDeprecationWrapper" - } - ], + "diagnostics": [], "exportedSymbolCounts": { "withAmbiguousType": 0, "withKnownType": 628, "withUnknownType": 0 }, "ignoreUnknownTypesFromImports": true, - "missingClassDocStringCount": 1, + "missingClassDocStringCount": 0, "missingDefaultParamCount": 0, - "missingFunctionDocStringCount": 3, + "missingFunctionDocStringCount": 0, "moduleName": "trio", "modules": [ { @@ -64,7 +47,7 @@ ], "otherSymbolCounts": { "withAmbiguousType": 0, - "withKnownType": 684, + "withKnownType": 685, "withUnknownType": 0 }, "packageName": "trio" diff --git a/trio/_tests/verify_types_windows.json b/trio/_tests/verify_types_windows.json index bab6797c02..f3881c7df9 100644 --- a/trio/_tests/verify_types_windows.json +++ b/trio/_tests/verify_types_windows.json @@ -8,65 +8,16 @@ }, "typeCompleteness": { "completenessScore": 1, - "diagnostics": [ - { - "message": "No docstring found for function \"trio.lowlevel.current_iocp\"", - "name": "trio.lowlevel.current_iocp" - }, - { - "message": "No docstring found for function \"trio.lowlevel.monitor_completion_key\"", - "name": "trio.lowlevel.monitor_completion_key" - }, - { - "message": "No docstring found for function \"trio.lowlevel.notify_closing\"", - "name": "trio.lowlevel.notify_closing" - }, - { - "message": "No docstring found for function \"trio.lowlevel.open_process\"", - "name": "trio.lowlevel.open_process" - }, - { - "message": "No docstring found for function \"trio.lowlevel.readinto_overlapped\"", - "name": "trio.lowlevel.readinto_overlapped" - }, - { - "message": "No docstring found for function \"trio.lowlevel.register_with_iocp\"", - "name": "trio.lowlevel.register_with_iocp" - }, - { - "message": "No docstring found for function \"trio.lowlevel.wait_overlapped\"", - "name": "trio.lowlevel.wait_overlapped" - }, - { - "message": "No docstring found for function \"trio.lowlevel.wait_readable\"", - "name": "trio.lowlevel.wait_readable" - }, - { - "message": "No docstring found for function \"trio.lowlevel.wait_writable\"", - "name": "trio.lowlevel.wait_writable" - }, - { - "message": "No docstring found for function \"trio.lowlevel.write_overlapped\"", - "name": "trio.lowlevel.write_overlapped" - }, - { - "message": "No docstring found for function \"trio.run_process\"", - "name": "trio.run_process" - }, - { - "message": "No docstring found for class \"trio.tests.TestsDeprecationWrapper\"", - "name": "trio.tests.TestsDeprecationWrapper" - } - ], + "diagnostics": [], "exportedSymbolCounts": { "withAmbiguousType": 0, "withKnownType": 631, "withUnknownType": 0 }, "ignoreUnknownTypesFromImports": true, - "missingClassDocStringCount": 1, + "missingClassDocStringCount": 0, "missingDefaultParamCount": 0, - "missingFunctionDocStringCount": 11, + "missingFunctionDocStringCount": 0, "moduleName": "trio", "modules": [ { @@ -96,7 +47,7 @@ ], "otherSymbolCounts": { "withAmbiguousType": 0, - "withKnownType": 680, + "withKnownType": 681, "withUnknownType": 0 }, "packageName": "trio" From 5e48b18141d83e4013c26ae7b3a9a749ccaf0b70 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Tue, 26 Sep 2023 11:44:34 +0200 Subject: [PATCH 03/15] fix weird discrepancy in verify_types_*.json --- trio/_tests/verify_types_darwin.json | 2 +- trio/_tests/verify_types_linux.json | 2 +- trio/_tests/verify_types_windows.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/trio/_tests/verify_types_darwin.json b/trio/_tests/verify_types_darwin.json index 53e2f972bf..a81fa38eb8 100644 --- a/trio/_tests/verify_types_darwin.json +++ b/trio/_tests/verify_types_darwin.json @@ -47,7 +47,7 @@ ], "otherSymbolCounts": { "withAmbiguousType": 0, - "withKnownType": 685, + "withKnownType": 684, "withUnknownType": 0 }, "packageName": "trio" diff --git a/trio/_tests/verify_types_linux.json b/trio/_tests/verify_types_linux.json index fde209a6eb..ba94e37752 100644 --- a/trio/_tests/verify_types_linux.json +++ b/trio/_tests/verify_types_linux.json @@ -47,7 +47,7 @@ ], "otherSymbolCounts": { "withAmbiguousType": 0, - "withKnownType": 685, + "withKnownType": 684, "withUnknownType": 0 }, "packageName": "trio" diff --git a/trio/_tests/verify_types_windows.json b/trio/_tests/verify_types_windows.json index f3881c7df9..797ae43cd3 100644 --- a/trio/_tests/verify_types_windows.json +++ b/trio/_tests/verify_types_windows.json @@ -47,7 +47,7 @@ ], "otherSymbolCounts": { "withAmbiguousType": 0, - "withKnownType": 681, + "withKnownType": 680, "withUnknownType": 0 }, "packageName": "trio" From 148f77de6559839220b064dc0f02560155c098b8 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 9 Oct 2023 15:49:36 +0200 Subject: [PATCH 04/15] add missing functions from trio/_core_io_windows to reference-lowlevel.rst, and copy documentation from reference-lowlevel.rst to trio/_core_io kqueue, epoll and windows --- docs/source/reference-lowlevel.rst | 6 ++ trio/_core/_generated_io_epoll.py | 58 +++++++++++++++++- trio/_core/_generated_io_kqueue.py | 72 ++++++++++++++++++++-- trio/_core/_generated_io_windows.py | 93 ++++++++++++++++++++++++++--- trio/_core/_io_epoll.py | 58 +++++++++++++++++- trio/_core/_io_kqueue.py | 72 ++++++++++++++++++++-- trio/_core/_io_windows.py | 93 ++++++++++++++++++++++++++--- 7 files changed, 416 insertions(+), 36 deletions(-) diff --git a/docs/source/reference-lowlevel.rst b/docs/source/reference-lowlevel.rst index 712a36ad04..9966e6f9a6 100644 --- a/docs/source/reference-lowlevel.rst +++ b/docs/source/reference-lowlevel.rst @@ -257,6 +257,12 @@ anything real. See `#26 .. function:: wait_overlapped(handle, lpOverlapped) :async: +.. function:: write_overlapped(handle, data) + :async: + +.. function:: readinto_overlapped(handle, data) + :async: + .. function:: current_iocp() .. function:: monitor_completion_key() diff --git a/trio/_core/_generated_io_epoll.py b/trio/_core/_generated_io_epoll.py index 4c6e395351..98c0b7c260 100644 --- a/trio/_core/_generated_io_epoll.py +++ b/trio/_core/_generated_io_epoll.py @@ -14,7 +14,28 @@ async def wait_readable(fd: (int | socket)) -> None: - """TODO""" + """ + Block until the kernel reports that the given object is readable. + + On Unix systems, ``obj`` must either be an integer file descriptor, + or else an object with a ``.fileno()`` method which returns an + integer file descriptor. Any kind of file descriptor can be passed, + though the exact semantics will depend on your kernel. For example, + this probably won't do anything useful for on-disk files. + + On Windows systems, ``obj`` must either be an integer ``SOCKET`` + handle, or else an object with a ``.fileno()`` method which returns + an integer ``SOCKET`` handle. File descriptors aren't supported, + and neither are handles that refer to anything besides a + ``SOCKET``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become readable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd) @@ -23,7 +44,17 @@ async def wait_readable(fd: (int | socket)) -> None: async def wait_writable(fd: (int | socket)) -> None: - """TODO""" + """Block until the kernel reports that the given object is writable. + + See `wait_readable` for the definition of ``obj``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become writable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(fd) @@ -32,7 +63,28 @@ async def wait_writable(fd: (int | socket)) -> None: def notify_closing(fd: (int | socket)) -> None: - """TODO""" + """Call this before closing a file descriptor (on Unix) or socket (on + Windows). This will cause any `wait_readable` or `wait_writable` + calls on the given object to immediately wake up and raise + `~trio.ClosedResourceError`. + + This doesn't actually close the object – you still have to do that + yourself afterwards. Also, you want to be careful to make sure no + new tasks start waiting on the object in between when you call this + and when it's actually closed. So to close something properly, you + usually want to do these steps in order: + + 1. Explicitly mark the object as closed, so that any new attempts + to use it will abort before they start. + 2. Call `notify_closing` to wake up any already-existing users. + 3. Actually close the object. + + It's also possible to do them in a different order if that's more + convenient, *but only if* you make sure not to have any checkpoints in + between the steps. This way they all happen in a single atomic + step, so other tasks won't be able to tell what order they happened + in anyway. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd) diff --git a/trio/_core/_generated_io_kqueue.py b/trio/_core/_generated_io_kqueue.py index d976703708..cf1c68fe27 100644 --- a/trio/_core/_generated_io_kqueue.py +++ b/trio/_core/_generated_io_kqueue.py @@ -22,7 +22,10 @@ def current_kqueue() -> select.kqueue: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.current_kqueue() @@ -33,7 +36,10 @@ def current_kqueue() -> select.kqueue: def monitor_kevent( ident: int, filter: int ) -> ContextManager[_core.UnboundedQueue[select.kevent]]: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.monitor_kevent(ident, filter) @@ -44,7 +50,10 @@ def monitor_kevent( async def wait_kevent( ident: int, filter: int, abort_func: Callable[[RaiseCancelT], Abort] ) -> Abort: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_kevent( @@ -55,7 +64,27 @@ async def wait_kevent( async def wait_readable(fd: (int | socket)) -> None: - """TODO""" + """Block until the kernel reports that the given object is readable. + + On Unix systems, ``obj`` must either be an integer file descriptor, + or else an object with a ``.fileno()`` method which returns an + integer file descriptor. Any kind of file descriptor can be passed, + though the exact semantics will depend on your kernel. For example, + this probably won't do anything useful for on-disk files. + + On Windows systems, ``obj`` must either be an integer ``SOCKET`` + handle, or else an object with a ``.fileno()`` method which returns + an integer ``SOCKET`` handle. File descriptors aren't supported, + and neither are handles that refer to anything besides a + ``SOCKET``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become readable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd) @@ -64,7 +93,17 @@ async def wait_readable(fd: (int | socket)) -> None: async def wait_writable(fd: (int | socket)) -> None: - """TODO""" + """Block until the kernel reports that the given object is writable. + + See `wait_readable` for the definition of ``obj``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become writable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(fd) @@ -73,7 +112,28 @@ async def wait_writable(fd: (int | socket)) -> None: def notify_closing(fd: (int | socket)) -> None: - """TODO""" + """Call this before closing a file descriptor (on Unix) or socket (on + Windows). This will cause any `wait_readable` or `wait_writable` + calls on the given object to immediately wake up and raise + `~trio.ClosedResourceError`. + + This doesn't actually close the object – you still have to do that + yourself afterwards. Also, you want to be careful to make sure no + new tasks start waiting on the object in between when you call this + and when it's actually closed. So to close something properly, you + usually want to do these steps in order: + + 1. Explicitly mark the object as closed, so that any new attempts + to use it will abort before they start. + 2. Call `notify_closing` to wake up any already-existing users. + 3. Actually close the object. + + It's also possible to do them in a different order if that's more + convenient, *but only if* you make sure not to have any checkpoints in + between the steps. This way they all happen in a single atomic + step, so other tasks won't be able to tell what order they happened + in anyway. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(fd) diff --git a/trio/_core/_generated_io_windows.py b/trio/_core/_generated_io_windows.py index a58af98c56..867b20ca02 100644 --- a/trio/_core/_generated_io_windows.py +++ b/trio/_core/_generated_io_windows.py @@ -21,7 +21,27 @@ async def wait_readable(sock: (_HasFileNo | int)) -> None: - """TODO""" + """Block until the kernel reports that the given object is readable. + + On Unix systems, ``obj`` must either be an integer file descriptor, + or else an object with a ``.fileno()`` method which returns an + integer file descriptor. Any kind of file descriptor can be passed, + though the exact semantics will depend on your kernel. For example, + this probably won't do anything useful for on-disk files. + + On Windows systems, ``obj`` must either be an integer ``SOCKET`` + handle, or else an object with a ``.fileno()`` method which returns + an integer ``SOCKET`` handle. File descriptors aren't supported, + and neither are handles that refer to anything besides a + ``SOCKET``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become readable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(sock) @@ -30,7 +50,17 @@ async def wait_readable(sock: (_HasFileNo | int)) -> None: async def wait_writable(sock: (_HasFileNo | int)) -> None: - """TODO""" + """Block until the kernel reports that the given object is writable. + + See `wait_readable` for the definition of ``obj``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become writable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_writable(sock) @@ -39,7 +69,28 @@ async def wait_writable(sock: (_HasFileNo | int)) -> None: def notify_closing(handle: (Handle | int | _HasFileNo)) -> None: - """TODO""" + """Call this before closing a file descriptor (on Unix) or socket (on + Windows). This will cause any `wait_readable` or `wait_writable` + calls on the given object to immediately wake up and raise + `~trio.ClosedResourceError`. + + This doesn't actually close the object – you still have to do that + yourself afterwards. Also, you want to be careful to make sure no + new tasks start waiting on the object in between when you call this + and when it's actually closed. So to close something properly, you + usually want to do these steps in order: + + 1. Explicitly mark the object as closed, so that any new attempts + to use it will abort before they start. + 2. Call `notify_closing` to wake up any already-existing users. + 3. Actually close the object. + + It's also possible to do them in a different order if that's more + convenient, *but only if* you make sure not to have any checkpoints in + between the steps. This way they all happen in a single atomic + step, so other tasks won't be able to tell what order they happened + in anyway. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.notify_closing(handle) @@ -48,7 +99,11 @@ def notify_closing(handle: (Handle | int | _HasFileNo)) -> None: def register_with_iocp(handle: (int | CData)) -> None: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.register_with_iocp(handle) @@ -59,7 +114,11 @@ def register_with_iocp(handle: (int | CData)) -> None: async def wait_overlapped( handle_: (int | CData), lpOverlapped: (CData | int) ) -> object: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_overlapped( @@ -72,7 +131,11 @@ async def wait_overlapped( async def write_overlapped( handle: (int | CData), data: Buffer, file_offset: int = 0 ) -> int: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.write_overlapped( @@ -85,7 +148,11 @@ async def write_overlapped( async def readinto_overlapped( handle: (int | CData), buffer: Buffer, file_offset: int = 0 ) -> int: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.readinto_overlapped( @@ -96,7 +163,11 @@ async def readinto_overlapped( def current_iocp() -> int: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.current_iocp() @@ -105,7 +176,11 @@ def current_iocp() -> int: def monitor_completion_key() -> ContextManager[tuple[int, UnboundedQueue[object]]]: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return GLOBAL_RUN_CONTEXT.runner.io_manager.monitor_completion_key() diff --git a/trio/_core/_io_epoll.py b/trio/_core/_io_epoll.py index 0313ceec82..e378c56b24 100644 --- a/trio/_core/_io_epoll.py +++ b/trio/_core/_io_epoll.py @@ -310,17 +310,69 @@ def abort(_: RaiseCancelT) -> Abort: @_public async def wait_readable(self, fd: int | socket) -> None: - """TODO""" + """ + Block until the kernel reports that the given object is readable. + + On Unix systems, ``obj`` must either be an integer file descriptor, + or else an object with a ``.fileno()`` method which returns an + integer file descriptor. Any kind of file descriptor can be passed, + though the exact semantics will depend on your kernel. For example, + this probably won't do anything useful for on-disk files. + + On Windows systems, ``obj`` must either be an integer ``SOCKET`` + handle, or else an object with a ``.fileno()`` method which returns + an integer ``SOCKET`` handle. File descriptors aren't supported, + and neither are handles that refer to anything besides a + ``SOCKET``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become readable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ await self._epoll_wait(fd, "read_task") @_public async def wait_writable(self, fd: int | socket) -> None: - """TODO""" + """Block until the kernel reports that the given object is writable. + + See `wait_readable` for the definition of ``obj``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become writable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ await self._epoll_wait(fd, "write_task") @_public def notify_closing(self, fd: int | socket) -> None: - """TODO""" + """Call this before closing a file descriptor (on Unix) or socket (on + Windows). This will cause any `wait_readable` or `wait_writable` + calls on the given object to immediately wake up and raise + `~trio.ClosedResourceError`. + + This doesn't actually close the object – you still have to do that + yourself afterwards. Also, you want to be careful to make sure no + new tasks start waiting on the object in between when you call this + and when it's actually closed. So to close something properly, you + usually want to do these steps in order: + + 1. Explicitly mark the object as closed, so that any new attempts + to use it will abort before they start. + 2. Call `notify_closing` to wake up any already-existing users. + 3. Actually close the object. + + It's also possible to do them in a different order if that's more + convenient, *but only if* you make sure not to have any checkpoints in + between the steps. This way they all happen in a single atomic + step, so other tasks won't be able to tell what order they happened + in anyway. + """ if not isinstance(fd, int): fd = fd.fileno() wake_all( diff --git a/trio/_core/_io_kqueue.py b/trio/_core/_io_kqueue.py index 8abc1a3db4..d0c2a9597d 100644 --- a/trio/_core/_io_kqueue.py +++ b/trio/_core/_io_kqueue.py @@ -110,7 +110,10 @@ def process_events(self, events: EventResult) -> None: @_public def current_kqueue(self) -> select.kqueue: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__. + """ return self._kqueue @contextmanager @@ -118,7 +121,10 @@ def current_kqueue(self) -> select.kqueue: def monitor_kevent( self, ident: int, filter: int ) -> Iterator[_core.UnboundedQueue[select.kevent]]: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__. + """ key = (ident, filter) if key in self._registered: raise _core.BusyResourceError( @@ -135,7 +141,10 @@ def monitor_kevent( async def wait_kevent( self, ident: int, filter: int, abort_func: Callable[[RaiseCancelT], Abort] ) -> Abort: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__. + """ key = (ident, filter) if key in self._registered: raise _core.BusyResourceError( @@ -185,17 +194,68 @@ def abort(_: RaiseCancelT) -> Abort: @_public async def wait_readable(self, fd: int | socket) -> None: - """TODO""" + """Block until the kernel reports that the given object is readable. + + On Unix systems, ``obj`` must either be an integer file descriptor, + or else an object with a ``.fileno()`` method which returns an + integer file descriptor. Any kind of file descriptor can be passed, + though the exact semantics will depend on your kernel. For example, + this probably won't do anything useful for on-disk files. + + On Windows systems, ``obj`` must either be an integer ``SOCKET`` + handle, or else an object with a ``.fileno()`` method which returns + an integer ``SOCKET`` handle. File descriptors aren't supported, + and neither are handles that refer to anything besides a + ``SOCKET``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become readable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ await self._wait_common(fd, select.KQ_FILTER_READ) @_public async def wait_writable(self, fd: int | socket) -> None: - """TODO""" + """Block until the kernel reports that the given object is writable. + + See `wait_readable` for the definition of ``obj``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become writable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ await self._wait_common(fd, select.KQ_FILTER_WRITE) @_public def notify_closing(self, fd: int | socket) -> None: - """TODO""" + """Call this before closing a file descriptor (on Unix) or socket (on + Windows). This will cause any `wait_readable` or `wait_writable` + calls on the given object to immediately wake up and raise + `~trio.ClosedResourceError`. + + This doesn't actually close the object – you still have to do that + yourself afterwards. Also, you want to be careful to make sure no + new tasks start waiting on the object in between when you call this + and when it's actually closed. So to close something properly, you + usually want to do these steps in order: + + 1. Explicitly mark the object as closed, so that any new attempts + to use it will abort before they start. + 2. Call `notify_closing` to wake up any already-existing users. + 3. Actually close the object. + + It's also possible to do them in a different order if that's more + convenient, *but only if* you make sure not to have any checkpoints in + between the steps. This way they all happen in a single atomic + step, so other tasks won't be able to tell what order they happened + in anyway. + """ if not isinstance(fd, int): fd = fd.fileno() diff --git a/trio/_core/_io_windows.py b/trio/_core/_io_windows.py index b53d9ead48..65c216e841 100644 --- a/trio/_core/_io_windows.py +++ b/trio/_core/_io_windows.py @@ -729,17 +729,68 @@ def abort_fn(_: RaiseCancelT) -> Abort: @_public async def wait_readable(self, sock: _HasFileNo | int) -> None: - """TODO""" + """Block until the kernel reports that the given object is readable. + + On Unix systems, ``obj`` must either be an integer file descriptor, + or else an object with a ``.fileno()`` method which returns an + integer file descriptor. Any kind of file descriptor can be passed, + though the exact semantics will depend on your kernel. For example, + this probably won't do anything useful for on-disk files. + + On Windows systems, ``obj`` must either be an integer ``SOCKET`` + handle, or else an object with a ``.fileno()`` method which returns + an integer ``SOCKET`` handle. File descriptors aren't supported, + and neither are handles that refer to anything besides a + ``SOCKET``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become readable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ await self._afd_poll(sock, "read_task") @_public async def wait_writable(self, sock: _HasFileNo | int) -> None: - """TODO""" + """Block until the kernel reports that the given object is writable. + + See `wait_readable` for the definition of ``obj``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become writable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ await self._afd_poll(sock, "write_task") @_public def notify_closing(self, handle: Handle | int | _HasFileNo) -> None: - """TODO""" + """Call this before closing a file descriptor (on Unix) or socket (on + Windows). This will cause any `wait_readable` or `wait_writable` + calls on the given object to immediately wake up and raise + `~trio.ClosedResourceError`. + + This doesn't actually close the object – you still have to do that + yourself afterwards. Also, you want to be careful to make sure no + new tasks start waiting on the object in between when you call this + and when it's actually closed. So to close something properly, you + usually want to do these steps in order: + + 1. Explicitly mark the object as closed, so that any new attempts + to use it will abort before they start. + 2. Call `notify_closing` to wake up any already-existing users. + 3. Actually close the object. + + It's also possible to do them in a different order if that's more + convenient, *but only if* you make sure not to have any checkpoints in + between the steps. This way they all happen in a single atomic + step, so other tasks won't be able to tell what order they happened + in anyway. + """ handle = _get_base_socket(handle) waiters = self._afd_waiters.get(handle) if waiters is not None: @@ -752,14 +803,22 @@ def notify_closing(self, handle: Handle | int | _HasFileNo) -> None: @_public def register_with_iocp(self, handle: int | CData) -> None: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ self._register_with_iocp(handle, CKeys.WAIT_OVERLAPPED) @_public async def wait_overlapped( self, handle_: int | CData, lpOverlapped: CData | int ) -> object: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ handle = _handle(handle_) if isinstance(lpOverlapped, int): lpOverlapped = ffi.cast("LPOVERLAPPED", lpOverlapped) @@ -851,7 +910,11 @@ async def _perform_overlapped( async def write_overlapped( self, handle: int | CData, data: Buffer, file_offset: int = 0 ) -> int: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ with ffi.from_buffer(data) as cbuf: def submit_write(lpOverlapped: _Overlapped) -> None: @@ -877,7 +940,11 @@ def submit_write(lpOverlapped: _Overlapped) -> None: async def readinto_overlapped( self, handle: int | CData, buffer: Buffer, file_offset: int = 0 ) -> int: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ with ffi.from_buffer(buffer, require_writable=True) as cbuf: def submit_read(lpOverlapped: _Overlapped) -> None: @@ -903,14 +970,22 @@ def submit_read(lpOverlapped: _Overlapped) -> None: @_public def current_iocp(self) -> int: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ assert self._iocp is not None return int(ffi.cast("uintptr_t", self._iocp)) @contextmanager @_public def monitor_completion_key(self) -> Iterator[tuple[int, UnboundedQueue[object]]]: - """TODO""" + """TODO: these are implemented, but are currently more of a sketch than + anything real. See `#26 + `__ and `#52 + `__. + """ key = next(self._completion_key_counter) queue = _core.UnboundedQueue[object]() self._completion_key_queues[key] = queue From f304740485e688cdd5a31c569638005dc41733a6 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 9 Oct 2023 16:40:04 +0200 Subject: [PATCH 05/15] copy docstrings for open_process and run_process, don't abandon gen_exports when getting ruff warnings, add docstring for fromshare --- trio/_core/_generated_io_epoll.py | 22 +++ trio/_socket.py | 1 + trio/_subprocess.py | 248 +++++++++++++++++++++++++++++- trio/_tools/gen_exports.py | 7 +- 4 files changed, 272 insertions(+), 6 deletions(-) diff --git a/trio/_core/_generated_io_epoll.py b/trio/_core/_generated_io_epoll.py index 1bb5264cbe..acdbcddc7d 100644 --- a/trio/_core/_generated_io_epoll.py +++ b/trio/_core/_generated_io_epoll.py @@ -15,6 +15,28 @@ async def wait_readable(fd: (int | _HasFileNo)) -> None: + """ + Block until the kernel reports that the given object is readable. + + On Unix systems, ``obj`` must either be an integer file descriptor, + or else an object with a ``.fileno()`` method which returns an + integer file descriptor. Any kind of file descriptor can be passed, + though the exact semantics will depend on your kernel. For example, + this probably won't do anything useful for on-disk files. + + On Windows systems, ``obj`` must either be an integer ``SOCKET`` + handle, or else an object with a ``.fileno()`` method which returns + an integer ``SOCKET`` handle. File descriptors aren't supported, + and neither are handles that refer to anything besides a + ``SOCKET``. + + :raises trio.BusyResourceError: + if another task is already waiting for the given socket to + become readable. + :raises trio.ClosedResourceError: + if another task calls :func:`notify_closing` while this + function is still working. + """ locals()[LOCALS_KEY_KI_PROTECTION_ENABLED] = True try: return await GLOBAL_RUN_CONTEXT.runner.io_manager.wait_readable(fd) diff --git a/trio/_socket.py b/trio/_socket.py index 540641a628..d3a42f700a 100644 --- a/trio/_socket.py +++ b/trio/_socket.py @@ -310,6 +310,7 @@ def fromfd( @_wraps(_stdlib_socket.fromshare, assigned=(), updated=()) def fromshare(info: bytes) -> SocketType: + """Like :func:`socket.fromshare`, but returns a Trio socket object.""" return from_stdlib_socket(_stdlib_socket.fromshare(info)) diff --git a/trio/_subprocess.py b/trio/_subprocess.py index f77021703e..fdcd516ce5 100644 --- a/trio/_subprocess.py +++ b/trio/_subprocess.py @@ -810,7 +810,56 @@ async def open_process( startupinfo: subprocess.STARTUPINFO | None = None, creationflags: int = 0, ) -> trio.Process: - """TODO: Lost the docstring defined above""" + r"""Execute a child program in a new process. + + After construction, you can interact with the child process by writing data to its + `~trio.Process.stdin` stream (a `~trio.abc.SendStream`), reading data from its + `~trio.Process.stdout` and/or `~trio.Process.stderr` streams (both + `~trio.abc.ReceiveStream`\s), sending it signals using `~trio.Process.terminate`, + `~trio.Process.kill`, or `~trio.Process.send_signal`, and waiting for it to exit + using `~trio.Process.wait`. See `trio.Process` for details. + + Each standard stream is only available if you specify that a pipe should be created + for it. For example, if you pass ``stdin=subprocess.PIPE``, you can write to the + `~trio.Process.stdin` stream, else `~trio.Process.stdin` will be ``None``. + + Unlike `trio.run_process`, this function doesn't do any kind of automatic + management of the child process. It's up to you to implement whatever semantics you + want. + + Args: + command (list or str): The command to run. Typically this is a + sequence of strings such as ``['ls', '-l', 'directory with spaces']``, + where the first element names the executable to invoke and the other + elements specify its arguments. With ``shell=True`` in the + ``**options``, or on Windows, ``command`` may alternatively + be a string, which will be parsed following platform-dependent + :ref:`quoting rules `. + stdin: Specifies what the child process's standard input + stream should connect to: output written by the parent + (``subprocess.PIPE``), nothing (``subprocess.DEVNULL``), + or an open file (pass a file descriptor or something whose + ``fileno`` method returns one). If ``stdin`` is unspecified, + the child process will have the same standard input stream + as its parent. + stdout: Like ``stdin``, but for the child process's standard output + stream. + stderr: Like ``stdin``, but for the child process's standard error + stream. An additional value ``subprocess.STDOUT`` is supported, + which causes the child's standard output and standard error + messages to be intermixed on a single standard output stream, + attached to whatever the ``stdout`` option says to attach it to. + **options: Other :ref:`general subprocess options ` + are also accepted. + + Returns: + A new `trio.Process` object. + + Raises: + OSError: if the process spawning fails, for example because the + specified command could not be found. + + """ ... async def run_process( @@ -831,12 +880,203 @@ async def run_process( startupinfo: subprocess.STARTUPINFO | None = None, creationflags: int = 0, ) -> subprocess.CompletedProcess[bytes]: - """TODO: Lost the docstring defined above""" + """Run ``command`` in a subprocess and wait for it to complete. + + This function can be called in two different ways. + + One option is a direct call, like:: + + completed_process_info = await trio.run_process(...) + + In this case, it returns a :class:`subprocess.CompletedProcess` instance + describing the results. Use this if you want to treat a process like a + function call. + + The other option is to run it as a task using `Nursery.start` – the enhanced version + of `~Nursery.start_soon` that lets a task pass back a value during startup:: + + process = await nursery.start(trio.run_process, ...) + + In this case, `~Nursery.start` returns a `Process` object that you can use + to interact with the process while it's running. Use this if you want to + treat a process like a background task. + + Either way, `run_process` makes sure that the process has exited before + returning, handles cancellation, optionally checks for errors, and + provides some convenient shorthands for dealing with the child's + input/output. + + **Input:** `run_process` supports all the same ``stdin=`` arguments as + `subprocess.Popen`. In addition, if you simply want to pass in some fixed + data, you can pass a plain `bytes` object, and `run_process` will take + care of setting up a pipe, feeding in the data you gave, and then sending + end-of-file. The default is ``b""``, which means that the child will receive + an empty stdin. If you want the child to instead read from the parent's + stdin, use ``stdin=None``. + + **Output:** By default, any output produced by the subprocess is + passed through to the standard output and error streams of the + parent Trio process. + + When calling `run_process` directly, you can capture the subprocess's output by + passing ``capture_stdout=True`` to capture the subprocess's standard output, and/or + ``capture_stderr=True`` to capture its standard error. Captured data is collected up + by Trio into an in-memory buffer, and then provided as the + :attr:`~subprocess.CompletedProcess.stdout` and/or + :attr:`~subprocess.CompletedProcess.stderr` attributes of the returned + :class:`~subprocess.CompletedProcess` object. The value for any stream that was not + captured will be ``None``. + + If you want to capture both stdout and stderr while keeping them + separate, pass ``capture_stdout=True, capture_stderr=True``. + + If you want to capture both stdout and stderr but mixed together + in the order they were printed, use: ``capture_stdout=True, stderr=subprocess.STDOUT``. + This directs the child's stderr into its stdout, so the combined + output will be available in the `~subprocess.CompletedProcess.stdout` + attribute. + + If you're using ``await nursery.start(trio.run_process, ...)`` and want to capture + the subprocess's output for further processing, then use ``stdout=subprocess.PIPE`` + and then make sure to read the data out of the `Process.stdout` stream. If you want + to capture stderr separately, use ``stderr=subprocess.PIPE``. If you want to capture + both, but mixed together in the correct order, use ``stdout=subprocess.PIPE, + stderr=subprocess.STDOUT``. + + **Error checking:** If the subprocess exits with a nonzero status + code, indicating failure, :func:`run_process` raises a + :exc:`subprocess.CalledProcessError` exception rather than + returning normally. The captured outputs are still available as + the :attr:`~subprocess.CalledProcessError.stdout` and + :attr:`~subprocess.CalledProcessError.stderr` attributes of that + exception. To disable this behavior, so that :func:`run_process` + returns normally even if the subprocess exits abnormally, pass ``check=False``. + + Note that this can make the ``capture_stdout`` and ``capture_stderr`` + arguments useful even when starting `run_process` as a task: if you only + care about the output if the process fails, then you can enable capturing + and then read the output off of the `~subprocess.CalledProcessError`. + + **Cancellation:** If cancelled, `run_process` sends a termination + request to the subprocess, then waits for it to fully exit. The + ``deliver_cancel`` argument lets you control how the process is terminated. + + .. note:: `run_process` is intentionally similar to the standard library + `subprocess.run`, but some of the defaults are different. Specifically, we + default to: + + - ``check=True``, because `"errors should never pass silently / unless + explicitly silenced" `__. + + - ``stdin=b""``, because it produces less-confusing results if a subprocess + unexpectedly tries to read from stdin. + + To get the `subprocess.run` semantics, use ``check=False, stdin=None``. + + Args: + command (list or str): The command to run. Typically this is a + sequence of strings such as ``['ls', '-l', 'directory with spaces']``, + where the first element names the executable to invoke and the other + elements specify its arguments. With ``shell=True`` in the + ``**options``, or on Windows, ``command`` may alternatively + be a string, which will be parsed following platform-dependent + :ref:`quoting rules `. + + stdin (:obj:`bytes`, subprocess.PIPE, file descriptor, or None): The + bytes to provide to the subprocess on its standard input stream, or + ``None`` if the subprocess's standard input should come from the + same place as the parent Trio process's standard input. As is the + case with the :mod:`subprocess` module, you can also pass a file + descriptor or an object with a ``fileno()`` method, in which case + the subprocess's standard input will come from that file. + + When starting `run_process` as a background task, you can also use + ``stdin=subprocess.PIPE``, in which case `Process.stdin` will be a + `~trio.abc.SendStream` that you can use to send data to the child. + + capture_stdout (bool): If true, capture the bytes that the subprocess + writes to its standard output stream and return them in the + `~subprocess.CompletedProcess.stdout` attribute of the returned + `subprocess.CompletedProcess` or `subprocess.CalledProcessError`. + + capture_stderr (bool): If true, capture the bytes that the subprocess + writes to its standard error stream and return them in the + `~subprocess.CompletedProcess.stderr` attribute of the returned + `~subprocess.CompletedProcess` or `subprocess.CalledProcessError`. + + check (bool): If false, don't validate that the subprocess exits + successfully. You should be sure to check the + ``returncode`` attribute of the returned object if you pass + ``check=False``, so that errors don't pass silently. + + deliver_cancel (async function or None): If `run_process` is cancelled, + then it needs to kill the child process. There are multiple ways to + do this, so we let you customize it. + + If you pass None (the default), then the behavior depends on the + platform: + + - On Windows, Trio calls ``TerminateProcess``, which should kill the + process immediately. + + - On Unix-likes, the default behavior is to send a ``SIGTERM``, wait + 5 seconds, and send a ``SIGKILL``. + + Alternatively, you can customize this behavior by passing in an + arbitrary async function, which will be called with the `Process` + object as an argument. For example, the default Unix behavior could + be implemented like this:: + + async def my_deliver_cancel(process): + process.send_signal(signal.SIGTERM) + await trio.sleep(5) + process.send_signal(signal.SIGKILL) + + When the process actually exits, the ``deliver_cancel`` function + will automatically be cancelled – so if the process exits after + ``SIGTERM``, then we'll never reach the ``SIGKILL``. + + In any case, `run_process` will always wait for the child process to + exit before raising `Cancelled`. + + **options: :func:`run_process` also accepts any :ref:`general subprocess + options ` and passes them on to the + :class:`~trio.Process` constructor. This includes the + ``stdout`` and ``stderr`` options, which provide additional + redirection possibilities such as ``stderr=subprocess.STDOUT``, + ``stdout=subprocess.DEVNULL``, or file descriptors. + + Returns: + + When called normally – a `subprocess.CompletedProcess` instance + describing the return code and outputs. + + When called via `Nursery.start` – a `trio.Process` instance. + + Raises: + UnicodeError: if ``stdin`` is specified as a Unicode string, rather + than bytes + ValueError: if multiple redirections are specified for the same + stream, e.g., both ``capture_stdout=True`` and + ``stdout=subprocess.DEVNULL`` + subprocess.CalledProcessError: if ``check=False`` is not passed + and the process exits with a nonzero exit status + OSError: if an error is encountered starting or communicating with + the process + + .. note:: The child process runs in the same process group as the parent + Trio process, so a Ctrl+C will be delivered simultaneously to both + parent and child. If you don't want this behavior, consult your + platform's documentation for starting child processes in a different + process group. + + """ ... else: # Unix - # TODO: These functions are not seen by pyright (and mypy?) - + # pyright doesn't give any error about these missing docstrings as they're + # overloads. But might still be a problem for other static analyzers / docstring + # readers (?) @overload # type: ignore[no-overload-impl] async def open_process( command: StrOrBytesPath, diff --git a/trio/_tools/gen_exports.py b/trio/_tools/gen_exports.py index 22d5f5abcf..06a72b21ae 100755 --- a/trio/_tools/gen_exports.py +++ b/trio/_tools/gen_exports.py @@ -163,10 +163,13 @@ def run_ruff(file: File, source: str) -> tuple[bool, str]: capture_output=True, encoding="utf8", ) + warnings = "" - if result.returncode != 0 or result.stderr: + if result.returncode != 0: return False, f"Failed to run ruff!\n{result.stderr}" - return True, result.stdout + elif result.stderr: + print(f"Warnings when running ruff:\n{result.stderr}") + return True, warnings + result.stdout def run_linters(file: File, source: str) -> str: From 73e3982a52fccce096b0e0632bd4f99698a08233 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 9 Oct 2023 17:07:24 +0200 Subject: [PATCH 06/15] print warning to stderr, undo removal of --output-format=text, print the value of success on AssertionError --- trio/_tests/tools/test_gen_exports.py | 2 +- trio/_tools/gen_exports.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/trio/_tests/tools/test_gen_exports.py b/trio/_tests/tools/test_gen_exports.py index b4e23916a0..2c54011d2b 100644 --- a/trio/_tests/tools/test_gen_exports.py +++ b/trio/_tests/tools/test_gen_exports.py @@ -150,7 +150,7 @@ def test_run_ruff(tmp_path) -> None: file = File(tmp_path / "module.py", "module") success, _ = run_ruff(file, "class not valid code ><") - assert not success + assert not success, success test_function = '''def combine_and(data: list[str]) -> str: """Join values of text, and have 'and' with the last one properly.""" diff --git a/trio/_tools/gen_exports.py b/trio/_tools/gen_exports.py index 06a72b21ae..afb497e104 100755 --- a/trio/_tools/gen_exports.py +++ b/trio/_tools/gen_exports.py @@ -155,6 +155,7 @@ def run_ruff(file: File, source: str) -> tuple[bool, str]: "-m", "ruff", "--fix-only", + "--output-format=text", "--stdin-filename", file.path, "-", @@ -163,13 +164,12 @@ def run_ruff(file: File, source: str) -> tuple[bool, str]: capture_output=True, encoding="utf8", ) - warnings = "" if result.returncode != 0: return False, f"Failed to run ruff!\n{result.stderr}" elif result.stderr: - print(f"Warnings when running ruff:\n{result.stderr}") - return True, warnings + result.stdout + print(f"Warnings when running ruff:\n{result.stderr}", file=sys.stderr) + return True, result.stdout def run_linters(file: File, source: str) -> str: From 6a2eb9570e824d5f162979c95ad2f1b23ac4715b Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 9 Oct 2023 20:57:03 -0500 Subject: [PATCH 07/15] Update ruff version and regenerate again --- test-requirements.in | 2 +- trio/_core/_generated_instrumentation.py | 2 +- trio/_core/_generated_io_epoll.py | 3 ++- trio/_core/_generated_io_kqueue.py | 5 +++-- trio/_core/_generated_io_windows.py | 7 ++++--- trio/_core/_generated_run.py | 9 ++++----- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/test-requirements.in b/test-requirements.in index 37fb6b5157..9f9cc81cfc 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -13,7 +13,7 @@ cryptography>=41.0.0 # cryptography<41 segfaults on pypy3.10 black; implementation_name == "cpython" mypy; implementation_name == "cpython" types-pyOpenSSL; implementation_name == "cpython" # and annotations -ruff >= 0.0.291 +ruff >= 0.0.292 astor # code generation pip-tools >= 6.13.0 codespell diff --git a/trio/_core/_generated_instrumentation.py b/trio/_core/_generated_instrumentation.py index dee8200c64..652fed1288 100644 --- a/trio/_core/_generated_instrumentation.py +++ b/trio/_core/_generated_instrumentation.py @@ -3,9 +3,9 @@ # ************************************************************* from __future__ import annotations +from ._instrumentation import Instrument from ._ki import LOCALS_KEY_KI_PROTECTION_ENABLED from ._run import GLOBAL_RUN_CONTEXT -from ._instrumentation import Instrument def add_instrument(instrument: Instrument) -> None: diff --git a/trio/_core/_generated_io_epoll.py b/trio/_core/_generated_io_epoll.py index acdbcddc7d..8f11f02e28 100644 --- a/trio/_core/_generated_io_epoll.py +++ b/trio/_core/_generated_io_epoll.py @@ -3,9 +3,10 @@ # ************************************************************* from __future__ import annotations +from typing import TYPE_CHECKING + from ._ki import LOCALS_KEY_KI_PROTECTION_ENABLED from ._run import GLOBAL_RUN_CONTEXT -from typing import TYPE_CHECKING if TYPE_CHECKING: from .._file_io import _HasFileNo diff --git a/trio/_core/_generated_io_kqueue.py b/trio/_core/_generated_io_kqueue.py index 30308e4704..577810d864 100644 --- a/trio/_core/_generated_io_kqueue.py +++ b/trio/_core/_generated_io_kqueue.py @@ -3,16 +3,17 @@ # ************************************************************* from __future__ import annotations +from typing import TYPE_CHECKING, Callable, ContextManager + from ._ki import LOCALS_KEY_KI_PROTECTION_ENABLED from ._run import GLOBAL_RUN_CONTEXT -from typing import Callable, ContextManager, TYPE_CHECKING if TYPE_CHECKING: import select from .. import _core - from ._traps import Abort, RaiseCancelT from .._file_io import _HasFileNo + from ._traps import Abort, RaiseCancelT import sys assert not TYPE_CHECKING or sys.platform == "darwin" diff --git a/trio/_core/_generated_io_windows.py b/trio/_core/_generated_io_windows.py index fc002bce33..fa530d0a1f 100644 --- a/trio/_core/_generated_io_windows.py +++ b/trio/_core/_generated_io_windows.py @@ -3,16 +3,17 @@ # ************************************************************* from __future__ import annotations +from typing import TYPE_CHECKING, ContextManager + from ._ki import LOCALS_KEY_KI_PROTECTION_ENABLED from ._run import GLOBAL_RUN_CONTEXT -from typing import TYPE_CHECKING, ContextManager if TYPE_CHECKING: - from .._file_io import _HasFileNo - from ._windows_cffi import Handle, CData from typing_extensions import Buffer + from .._file_io import _HasFileNo from ._unbounded_queue import UnboundedQueue + from ._windows_cffi import CData, Handle import sys assert not TYPE_CHECKING or sys.platform == "win32" diff --git a/trio/_core/_generated_run.py b/trio/_core/_generated_run.py index 54d721e332..3e1b7b78f1 100644 --- a/trio/_core/_generated_run.py +++ b/trio/_core/_generated_run.py @@ -3,17 +3,16 @@ # ************************************************************* from __future__ import annotations -from ._ki import LOCALS_KEY_KI_PROTECTION_ENABLED -from ._run import GLOBAL_RUN_CONTEXT +import contextvars from collections.abc import Awaitable, Callable from typing import Any from outcome import Outcome -import contextvars -from ._run import _NO_SEND, RunStatistics, Task -from ._entry_queue import TrioToken from .._abc import Clock +from ._entry_queue import TrioToken +from ._ki import LOCALS_KEY_KI_PROTECTION_ENABLED +from ._run import _NO_SEND, GLOBAL_RUN_CONTEXT, RunStatistics, Task def current_statistics() -> RunStatistics: From ef7f02b56f6f6016196024a1d56805255249f3d0 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 9 Oct 2023 21:02:39 -0500 Subject: [PATCH 08/15] Update ruff requirement --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index dd2caf58dc..ee77afab7d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -96,7 +96,7 @@ pyright==1.1.329 # via -r test-requirements.in pytest==7.4.2 # via -r test-requirements.in -ruff==0.0.291 +ruff==0.0.292 # via -r test-requirements.in sniffio==1.3.0 # via -r test-requirements.in From c0875c3c568fd91ac1ba8f8105cf644e19b4d1f8 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 9 Oct 2023 21:11:54 -0500 Subject: [PATCH 09/15] Success should still be false for warnings --- trio/_tools/gen_exports.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trio/_tools/gen_exports.py b/trio/_tools/gen_exports.py index afb497e104..c863b0567a 100755 --- a/trio/_tools/gen_exports.py +++ b/trio/_tools/gen_exports.py @@ -168,7 +168,7 @@ def run_ruff(file: File, source: str) -> tuple[bool, str]: if result.returncode != 0: return False, f"Failed to run ruff!\n{result.stderr}" elif result.stderr: - print(f"Warnings when running ruff:\n{result.stderr}", file=sys.stderr) + return False, f"Warnings when running ruff:\n{result.stderr}" return True, result.stdout @@ -185,12 +185,12 @@ def run_linters(file: File, source: str) -> str: success, response = run_black(file, source) if not success: - print(response) + print(response, file=sys.stderr) sys.exit(1) success, response = run_ruff(file, response) if not success: # pragma: no cover # Test for run_ruff should catch - print(response) + print(response, file=sys.stderr) sys.exit(1) return response From 330a51d855ef7a50aa2b53acb87bbbefb92c7687 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 9 Oct 2023 21:12:45 -0500 Subject: [PATCH 10/15] Have asserts show error response when not success --- trio/_tests/tools/test_gen_exports.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trio/_tests/tools/test_gen_exports.py b/trio/_tests/tools/test_gen_exports.py index 2c54011d2b..2ee2cf9939 100644 --- a/trio/_tests/tools/test_gen_exports.py +++ b/trio/_tests/tools/test_gen_exports.py @@ -149,8 +149,8 @@ def test_run_ruff(tmp_path) -> None: file = File(tmp_path / "module.py", "module") - success, _ = run_ruff(file, "class not valid code ><") - assert not success, success + success, response = run_ruff(file, "class not valid code ><") + assert not success, response test_function = '''def combine_and(data: list[str]) -> str: """Join values of text, and have 'and' with the last one properly.""" @@ -161,7 +161,7 @@ def test_run_ruff(tmp_path) -> None: return ' '.join(data)''' success, response = run_ruff(file, test_function) - assert success + assert success, response assert response == test_function From 956ec5c89821983b3e01bd9eea8aa6aa5e922109 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 9 Oct 2023 21:27:02 -0500 Subject: [PATCH 11/15] Update `verify_types.json` --- trio/_tests/verify_types_linux.json | 9 ++------- trio/_tests/verify_types_windows.json | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/trio/_tests/verify_types_linux.json b/trio/_tests/verify_types_linux.json index 0bce0982de..234f031c92 100644 --- a/trio/_tests/verify_types_linux.json +++ b/trio/_tests/verify_types_linux.json @@ -8,12 +8,7 @@ }, "typeCompleteness": { "completenessScore": 1, - "diagnostics": [ - { - "message": "No docstring found for function \"trio.lowlevel.wait_readable\"", - "name": "trio.lowlevel.wait_readable" - } - ], + "diagnostics": [], "exportedSymbolCounts": { "withAmbiguousType": 0, "withKnownType": 627, @@ -22,7 +17,7 @@ "ignoreUnknownTypesFromImports": true, "missingClassDocStringCount": 0, "missingDefaultParamCount": 0, - "missingFunctionDocStringCount": 1, + "missingFunctionDocStringCount": 0, "moduleName": "trio", "modules": [ { diff --git a/trio/_tests/verify_types_windows.json b/trio/_tests/verify_types_windows.json index 82a8f11abf..3ed300f65e 100644 --- a/trio/_tests/verify_types_windows.json +++ b/trio/_tests/verify_types_windows.json @@ -8,12 +8,7 @@ }, "typeCompleteness": { "completenessScore": 1, - "diagnostics": [ - { - "message": "No docstring found for function \"trio.socket.fromshare\"", - "name": "trio.socket.fromshare" - } - ], + "diagnostics": [], "exportedSymbolCounts": { "withAmbiguousType": 0, "withKnownType": 630, @@ -22,7 +17,7 @@ "ignoreUnknownTypesFromImports": true, "missingClassDocStringCount": 0, "missingDefaultParamCount": 0, - "missingFunctionDocStringCount": 1, + "missingFunctionDocStringCount": 0, "moduleName": "trio", "modules": [ { From 340868a469381336b3a727cddc092102804701fe Mon Sep 17 00:00:00 2001 From: jakkdl Date: Thu, 2 Nov 2023 13:45:58 +0100 Subject: [PATCH 12/15] undo changes to gen_exports --- trio/_tests/tools/test_gen_exports.py | 6 +++--- trio/_tools/gen_exports.py | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/trio/_tests/tools/test_gen_exports.py b/trio/_tests/tools/test_gen_exports.py index 04c6cec253..9c0b5f625d 100644 --- a/trio/_tests/tools/test_gen_exports.py +++ b/trio/_tests/tools/test_gen_exports.py @@ -150,8 +150,8 @@ def test_run_ruff(tmp_path: Path) -> None: file = File(tmp_path / "module.py", "module") - success, response = run_ruff(file, "class not valid code ><") - assert not success, response + success, _ = run_ruff(file, "class not valid code ><") + assert not success test_function = '''def combine_and(data: list[str]) -> str: """Join values of text, and have 'and' with the last one properly.""" @@ -162,7 +162,7 @@ def test_run_ruff(tmp_path: Path) -> None: return ' '.join(data)''' success, response = run_ruff(file, test_function) - assert success, response + assert success assert response == test_function diff --git a/trio/_tools/gen_exports.py b/trio/_tools/gen_exports.py index 77160f5941..2eec02dfc1 100755 --- a/trio/_tools/gen_exports.py +++ b/trio/_tools/gen_exports.py @@ -168,8 +168,6 @@ def run_ruff(file: File, source: str) -> tuple[bool, str]: if result.returncode != 0: return False, f"Failed to run ruff!\n{result.stderr}" - elif result.stderr: - return False, f"Warnings when running ruff:\n{result.stderr}" return True, result.stdout @@ -186,12 +184,12 @@ def run_linters(file: File, source: str) -> str: success, response = run_black(file, source) if not success: - print(response, file=sys.stderr) + print(response) sys.exit(1) success, response = run_ruff(file, response) if not success: # pragma: no cover # Test for run_ruff should catch - print(response, file=sys.stderr) + print(response) sys.exit(1) return response From fb16d108fd9ad32bb075aadf35ba47a92d817228 Mon Sep 17 00:00:00 2001 From: John Litborn <11260241+jakkdl@users.noreply.github.com> Date: Sat, 11 Nov 2023 12:54:06 +0100 Subject: [PATCH 13/15] Apply suggestions from code review Co-authored-by: EXPLOSION --- trio/_core/_io_epoll.py | 9 ++++----- trio/_core/_io_kqueue.py | 6 +++--- trio/_core/_io_windows.py | 6 +++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/trio/_core/_io_epoll.py b/trio/_core/_io_epoll.py index 32afb88361..fbf1f5a97f 100644 --- a/trio/_core/_io_epoll.py +++ b/trio/_core/_io_epoll.py @@ -310,16 +310,15 @@ def abort(_: RaiseCancelT) -> Abort: @_public async def wait_readable(self, fd: int | _HasFileNo) -> None: - """ - Block until the kernel reports that the given object is readable. + """Block until the kernel reports that the given object is readable. - On Unix systems, ``obj`` must either be an integer file descriptor, + On Unix systems, ``fd`` must either be an integer file descriptor, or else an object with a ``.fileno()`` method which returns an integer file descriptor. Any kind of file descriptor can be passed, though the exact semantics will depend on your kernel. For example, this probably won't do anything useful for on-disk files. - On Windows systems, ``obj`` must either be an integer ``SOCKET`` + On Windows systems, ``fd`` must either be an integer ``SOCKET`` handle, or else an object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File descriptors aren't supported, and neither are handles that refer to anything besides a @@ -338,7 +337,7 @@ async def wait_readable(self, fd: int | _HasFileNo) -> None: async def wait_writable(self, fd: int | _HasFileNo) -> None: """Block until the kernel reports that the given object is writable. - See `wait_readable` for the definition of ``obj``. + See `wait_readable` for the definition of ``fd``. :raises trio.BusyResourceError: if another task is already waiting for the given socket to diff --git a/trio/_core/_io_kqueue.py b/trio/_core/_io_kqueue.py index 13cd2685ad..521631684f 100644 --- a/trio/_core/_io_kqueue.py +++ b/trio/_core/_io_kqueue.py @@ -195,13 +195,13 @@ def abort(_: RaiseCancelT) -> Abort: async def wait_readable(self, fd: int | _HasFileNo) -> None: """Block until the kernel reports that the given object is readable. - On Unix systems, ``obj`` must either be an integer file descriptor, + On Unix systems, ``fd`` must either be an integer file descriptor, or else an object with a ``.fileno()`` method which returns an integer file descriptor. Any kind of file descriptor can be passed, though the exact semantics will depend on your kernel. For example, this probably won't do anything useful for on-disk files. - On Windows systems, ``obj`` must either be an integer ``SOCKET`` + On Windows systems, ``fd`` must either be an integer ``SOCKET`` handle, or else an object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File descriptors aren't supported, and neither are handles that refer to anything besides a @@ -220,7 +220,7 @@ async def wait_readable(self, fd: int | _HasFileNo) -> None: async def wait_writable(self, fd: int | _HasFileNo) -> None: """Block until the kernel reports that the given object is writable. - See `wait_readable` for the definition of ``obj``. + See `wait_readable` for the definition of ``fd``. :raises trio.BusyResourceError: if another task is already waiting for the given socket to diff --git a/trio/_core/_io_windows.py b/trio/_core/_io_windows.py index 4f4f85e06c..0cb65762bc 100644 --- a/trio/_core/_io_windows.py +++ b/trio/_core/_io_windows.py @@ -730,13 +730,13 @@ def abort_fn(_: RaiseCancelT) -> Abort: async def wait_readable(self, sock: _HasFileNo | int) -> None: """Block until the kernel reports that the given object is readable. - On Unix systems, ``obj`` must either be an integer file descriptor, + On Unix systems, ``sock`` must either be an integer file descriptor, or else an object with a ``.fileno()`` method which returns an integer file descriptor. Any kind of file descriptor can be passed, though the exact semantics will depend on your kernel. For example, this probably won't do anything useful for on-disk files. - On Windows systems, ``obj`` must either be an integer ``SOCKET`` + On Windows systems, ``sock`` must either be an integer ``SOCKET`` handle, or else an object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File descriptors aren't supported, and neither are handles that refer to anything besides a @@ -755,7 +755,7 @@ async def wait_readable(self, sock: _HasFileNo | int) -> None: async def wait_writable(self, sock: _HasFileNo | int) -> None: """Block until the kernel reports that the given object is writable. - See `wait_readable` for the definition of ``obj``. + See `wait_readable` for the definition of ``sock``. :raises trio.BusyResourceError: if another task is already waiting for the given socket to From 34cdead77c0d311d2e0c0dd0e84b15b5b95b9fd0 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Sat, 11 Nov 2023 12:55:52 +0100 Subject: [PATCH 14/15] update generated files --- trio/_core/_generated_io_epoll.py | 9 ++++----- trio/_core/_generated_io_kqueue.py | 6 +++--- trio/_core/_generated_io_windows.py | 6 +++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/trio/_core/_generated_io_epoll.py b/trio/_core/_generated_io_epoll.py index d4da6263e9..c88c7ccae8 100644 --- a/trio/_core/_generated_io_epoll.py +++ b/trio/_core/_generated_io_epoll.py @@ -16,16 +16,15 @@ async def wait_readable(fd: (int | _HasFileNo)) -> None: - """ - Block until the kernel reports that the given object is readable. + """Block until the kernel reports that the given object is readable. - On Unix systems, ``obj`` must either be an integer file descriptor, + On Unix systems, ``fd`` must either be an integer file descriptor, or else an object with a ``.fileno()`` method which returns an integer file descriptor. Any kind of file descriptor can be passed, though the exact semantics will depend on your kernel. For example, this probably won't do anything useful for on-disk files. - On Windows systems, ``obj`` must either be an integer ``SOCKET`` + On Windows systems, ``fd`` must either be an integer ``SOCKET`` handle, or else an object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File descriptors aren't supported, and neither are handles that refer to anything besides a @@ -48,7 +47,7 @@ async def wait_readable(fd: (int | _HasFileNo)) -> None: async def wait_writable(fd: (int | _HasFileNo)) -> None: """Block until the kernel reports that the given object is writable. - See `wait_readable` for the definition of ``obj``. + See `wait_readable` for the definition of ``fd``. :raises trio.BusyResourceError: if another task is already waiting for the given socket to diff --git a/trio/_core/_generated_io_kqueue.py b/trio/_core/_generated_io_kqueue.py index 8da68c3917..a9114dc6ba 100644 --- a/trio/_core/_generated_io_kqueue.py +++ b/trio/_core/_generated_io_kqueue.py @@ -64,13 +64,13 @@ async def wait_kevent( async def wait_readable(fd: (int | _HasFileNo)) -> None: """Block until the kernel reports that the given object is readable. - On Unix systems, ``obj`` must either be an integer file descriptor, + On Unix systems, ``fd`` must either be an integer file descriptor, or else an object with a ``.fileno()`` method which returns an integer file descriptor. Any kind of file descriptor can be passed, though the exact semantics will depend on your kernel. For example, this probably won't do anything useful for on-disk files. - On Windows systems, ``obj`` must either be an integer ``SOCKET`` + On Windows systems, ``fd`` must either be an integer ``SOCKET`` handle, or else an object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File descriptors aren't supported, and neither are handles that refer to anything besides a @@ -93,7 +93,7 @@ async def wait_readable(fd: (int | _HasFileNo)) -> None: async def wait_writable(fd: (int | _HasFileNo)) -> None: """Block until the kernel reports that the given object is writable. - See `wait_readable` for the definition of ``obj``. + See `wait_readable` for the definition of ``fd``. :raises trio.BusyResourceError: if another task is already waiting for the given socket to diff --git a/trio/_core/_generated_io_windows.py b/trio/_core/_generated_io_windows.py index 7e6754477d..5e579fcd8a 100644 --- a/trio/_core/_generated_io_windows.py +++ b/trio/_core/_generated_io_windows.py @@ -22,13 +22,13 @@ async def wait_readable(sock: (_HasFileNo | int)) -> None: """Block until the kernel reports that the given object is readable. - On Unix systems, ``obj`` must either be an integer file descriptor, + On Unix systems, ``sock`` must either be an integer file descriptor, or else an object with a ``.fileno()`` method which returns an integer file descriptor. Any kind of file descriptor can be passed, though the exact semantics will depend on your kernel. For example, this probably won't do anything useful for on-disk files. - On Windows systems, ``obj`` must either be an integer ``SOCKET`` + On Windows systems, ``sock`` must either be an integer ``SOCKET`` handle, or else an object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File descriptors aren't supported, and neither are handles that refer to anything besides a @@ -51,7 +51,7 @@ async def wait_readable(sock: (_HasFileNo | int)) -> None: async def wait_writable(sock: (_HasFileNo | int)) -> None: """Block until the kernel reports that the given object is writable. - See `wait_readable` for the definition of ``obj``. + See `wait_readable` for the definition of ``sock``. :raises trio.BusyResourceError: if another task is already waiting for the given socket to From 218c13d226f816d9c1c03fa6312a5285b99ce090 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Sat, 11 Nov 2023 13:06:29 +0100 Subject: [PATCH 15/15] add summary to notify_closing --- src/trio/_core/_generated_io_epoll.py | 4 +++- src/trio/_core/_generated_io_kqueue.py | 4 +++- src/trio/_core/_generated_io_windows.py | 4 +++- src/trio/_core/_io_epoll.py | 4 +++- src/trio/_core/_io_kqueue.py | 4 +++- src/trio/_core/_io_windows.py | 4 +++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/trio/_core/_generated_io_epoll.py b/src/trio/_core/_generated_io_epoll.py index c88c7ccae8..d2547e7619 100644 --- a/src/trio/_core/_generated_io_epoll.py +++ b/src/trio/_core/_generated_io_epoll.py @@ -64,7 +64,9 @@ async def wait_writable(fd: (int | _HasFileNo)) -> None: def notify_closing(fd: (int | _HasFileNo)) -> None: - """Call this before closing a file descriptor (on Unix) or socket (on + """Notify waiters of the given object that it will be closed. + + Call this before closing a file descriptor (on Unix) or socket (on Windows). This will cause any `wait_readable` or `wait_writable` calls on the given object to immediately wake up and raise `~trio.ClosedResourceError`. diff --git a/src/trio/_core/_generated_io_kqueue.py b/src/trio/_core/_generated_io_kqueue.py index a9114dc6ba..18467f0447 100644 --- a/src/trio/_core/_generated_io_kqueue.py +++ b/src/trio/_core/_generated_io_kqueue.py @@ -110,7 +110,9 @@ async def wait_writable(fd: (int | _HasFileNo)) -> None: def notify_closing(fd: (int | _HasFileNo)) -> None: - """Call this before closing a file descriptor (on Unix) or socket (on + """Notify waiters of the given object that it will be closed. + + Call this before closing a file descriptor (on Unix) or socket (on Windows). This will cause any `wait_readable` or `wait_writable` calls on the given object to immediately wake up and raise `~trio.ClosedResourceError`. diff --git a/src/trio/_core/_generated_io_windows.py b/src/trio/_core/_generated_io_windows.py index 5e579fcd8a..b705a77267 100644 --- a/src/trio/_core/_generated_io_windows.py +++ b/src/trio/_core/_generated_io_windows.py @@ -68,7 +68,9 @@ async def wait_writable(sock: (_HasFileNo | int)) -> None: def notify_closing(handle: (Handle | int | _HasFileNo)) -> None: - """Call this before closing a file descriptor (on Unix) or socket (on + """Notify waiters of the given object that it will be closed. + + Call this before closing a file descriptor (on Unix) or socket (on Windows). This will cause any `wait_readable` or `wait_writable` calls on the given object to immediately wake up and raise `~trio.ClosedResourceError`. diff --git a/src/trio/_core/_io_epoll.py b/src/trio/_core/_io_epoll.py index fbf1f5a97f..4b080f4063 100644 --- a/src/trio/_core/_io_epoll.py +++ b/src/trio/_core/_io_epoll.py @@ -350,7 +350,9 @@ async def wait_writable(self, fd: int | _HasFileNo) -> None: @_public def notify_closing(self, fd: int | _HasFileNo) -> None: - """Call this before closing a file descriptor (on Unix) or socket (on + """Notify waiters of the given object that it will be closed. + + Call this before closing a file descriptor (on Unix) or socket (on Windows). This will cause any `wait_readable` or `wait_writable` calls on the given object to immediately wake up and raise `~trio.ClosedResourceError`. diff --git a/src/trio/_core/_io_kqueue.py b/src/trio/_core/_io_kqueue.py index 521631684f..61c37fe26e 100644 --- a/src/trio/_core/_io_kqueue.py +++ b/src/trio/_core/_io_kqueue.py @@ -233,7 +233,9 @@ async def wait_writable(self, fd: int | _HasFileNo) -> None: @_public def notify_closing(self, fd: int | _HasFileNo) -> None: - """Call this before closing a file descriptor (on Unix) or socket (on + """Notify waiters of the given object that it will be closed. + + Call this before closing a file descriptor (on Unix) or socket (on Windows). This will cause any `wait_readable` or `wait_writable` calls on the given object to immediately wake up and raise `~trio.ClosedResourceError`. diff --git a/src/trio/_core/_io_windows.py b/src/trio/_core/_io_windows.py index 0cb65762bc..bdc100ddb5 100644 --- a/src/trio/_core/_io_windows.py +++ b/src/trio/_core/_io_windows.py @@ -768,7 +768,9 @@ async def wait_writable(self, sock: _HasFileNo | int) -> None: @_public def notify_closing(self, handle: Handle | int | _HasFileNo) -> None: - """Call this before closing a file descriptor (on Unix) or socket (on + """Notify waiters of the given object that it will be closed. + + Call this before closing a file descriptor (on Unix) or socket (on Windows). This will cause any `wait_readable` or `wait_writable` calls on the given object to immediately wake up and raise `~trio.ClosedResourceError`.