From e37ea1b3a04e9d3cfd8d125f5c2af4ebb7db3eca Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Sat, 28 Oct 2023 13:44:46 -0400 Subject: [PATCH 1/7] add abandon_on_cancel to to_thread_run_sync: new name for cancellable flag Replaced in code and docs --- docs/source/reference-core.rst | 6 +++--- trio/_socket.py | 6 +++--- trio/_subprocess_platform/waitid.py | 4 ++-- trio/_tests/test_threads.py | 29 ++++++++++++++++------------- trio/_threads.py | 21 +++++++++++++-------- trio/_wait_for_object.py | 2 +- 6 files changed, 38 insertions(+), 30 deletions(-) diff --git a/docs/source/reference-core.rst b/docs/source/reference-core.rst index 333cb0537b..98cf579399 100644 --- a/docs/source/reference-core.rst +++ b/docs/source/reference-core.rst @@ -1827,8 +1827,8 @@ to spawn a child thread, and then use a :ref:`memory channel The ``from_thread.run*`` functions reuse the host task that called :func:`trio.to_thread.run_sync` to run your provided function, as long as you're - using the default ``cancellable=False`` so Trio can be sure that the task will remain - around to perform the work. If you pass ``cancellable=True`` at the outset, or if + using the default ``abandon_on_cancel=False`` so Trio can be sure that the task will remain + around to perform the work. If you pass ``abandon_on_cancel=True`` at the outset, or if you provide a :class:`~trio.lowlevel.TrioToken` when calling back in to Trio, your functions will be executed in a new system task. Therefore, the :func:`~trio.lowlevel.current_task`, :func:`current_effective_deadline`, or other @@ -1836,7 +1836,7 @@ to spawn a child thread, and then use a :ref:`memory channel You can also use :func:`trio.from_thread.check_cancelled` to check for cancellation from a thread that was spawned by :func:`trio.to_thread.run_sync`. If the call to -:func:`~trio.to_thread.run_sync` was cancelled (even if ``cancellable=False``!), then +:func:`~trio.to_thread.run_sync` was cancelled (even if ``abandon_on_cancel=False``!), then :func:`~trio.from_thread.check_cancelled` will raise :func:`trio.Cancelled`. It's like ``trio.from_thread.run(trio.sleep, 0)``, but much faster. diff --git a/trio/_socket.py b/trio/_socket.py index efef2ff5ed..061cc09367 100644 --- a/trio/_socket.py +++ b/trio/_socket.py @@ -240,7 +240,7 @@ def numeric_only_failure(exc: BaseException) -> bool: type, proto, flags, - cancellable=True, + abandon_on_cancel=True, ) @@ -261,7 +261,7 @@ async def getnameinfo( return await hr.getnameinfo(sockaddr, flags) else: return await trio.to_thread.run_sync( - _stdlib_socket.getnameinfo, sockaddr, flags, cancellable=True + _stdlib_socket.getnameinfo, sockaddr, flags, abandon_on_cancel=True ) @@ -272,7 +272,7 @@ async def getprotobyname(name: str) -> int: """ return await trio.to_thread.run_sync( - _stdlib_socket.getprotobyname, name, cancellable=True + _stdlib_socket.getprotobyname, name, abandon_on_cancel=True ) diff --git a/trio/_subprocess_platform/waitid.py b/trio/_subprocess_platform/waitid.py index 7d941747e3..165e946d21 100644 --- a/trio/_subprocess_platform/waitid.py +++ b/trio/_subprocess_platform/waitid.py @@ -73,14 +73,14 @@ async def _waitid_system_task(pid: int, event: Event) -> None: """Spawn a thread that waits for ``pid`` to exit, then wake any tasks that were waiting on it. """ - # cancellable=True: if this task is cancelled, then we abandon the + # abandon_on_cancel=True: if this task is cancelled, then we abandon the # thread to keep running waitpid in the background. Since this is # always run as a system task, this will only happen if the whole # call to trio.run is shutting down. try: await to_thread_run_sync( - sync_wait_reapable, pid, cancellable=True, limiter=waitid_limiter + sync_wait_reapable, pid, abandon_on_cancel=True, limiter=waitid_limiter ) except OSError: # If waitid fails, waitpid will fail too, so it still makes diff --git a/trio/_tests/test_threads.py b/trio/_tests/test_threads.py index ff865d2ef0..34e71dcf6d 100644 --- a/trio/_tests/test_threads.py +++ b/trio/_tests/test_threads.py @@ -315,10 +315,10 @@ def f(q): q.get() register[0] = "finished" - async def child(q, cancellable): + async def child(q, abandon_on_cancel): record.append("start") try: - return await to_thread_run_sync(f, q, cancellable=cancellable) + return await to_thread_run_sync(f, q, abandon_on_cancel=abandon_on_cancel) finally: record.append("exit") @@ -378,7 +378,7 @@ def thread_fn(): async def main(): async def child(): - await to_thread_run_sync(thread_fn, cancellable=True) + await to_thread_run_sync(thread_fn, abandon_on_cancel=True) async with _core.open_nursery() as nursery: nursery.start_soon(child) @@ -462,7 +462,10 @@ def thread_fn(cancel_scope): async def run_thread(event): with _core.CancelScope() as cancel_scope: await to_thread_run_sync( - thread_fn, cancel_scope, limiter=limiter_arg, cancellable=cancel + thread_fn, + cancel_scope, + abandon_on_cancel=cancel, + limiter=limiter_arg, ) print("run_thread finished, cancelled:", cancel_scope.cancelled_caught) event.set() @@ -847,7 +850,7 @@ async def test_trio_token_weak_referenceable(): assert token is weak_reference() -async def test_unsafe_cancellable_kwarg(): +async def test_unsafe_abandon_on_cancel_kwarg(): # This is a stand in for a numpy ndarray or other objects # that (maybe surprisingly) lack a notion of truthiness class BadBool: @@ -855,7 +858,7 @@ def __bool__(self): raise NotImplementedError with pytest.raises(NotImplementedError): - await to_thread_run_sync(int, cancellable=BadBool()) + await to_thread_run_sync(int, abandon_on_cancel=BadBool()) async def test_from_thread_reuses_task(): @@ -898,7 +901,7 @@ def sync_check(): assert not queue.get_nowait() with _core.CancelScope() as cancel_scope: - await to_thread_run_sync(sync_check, cancellable=True) + await to_thread_run_sync(sync_check, abandon_on_cancel=True) assert cancel_scope.cancelled_caught assert not await to_thread_run_sync(partial(queue.get, timeout=1)) @@ -922,7 +925,7 @@ def async_check(): assert not queue.get_nowait() with _core.CancelScope() as cancel_scope: - await to_thread_run_sync(async_check, cancellable=True) + await to_thread_run_sync(async_check, abandon_on_cancel=True) assert cancel_scope.cancelled_caught assert not await to_thread_run_sync(partial(queue.get, timeout=1)) @@ -941,11 +944,11 @@ async def async_time_bomb(): async def test_from_thread_check_cancelled(): q = stdlib_queue.Queue() - async def child(cancellable, scope): + async def child(abandon_on_cancel, scope): with scope: record.append("start") try: - return await to_thread_run_sync(f, cancellable=cancellable) + return await to_thread_run_sync(f, abandon_on_cancel=abandon_on_cancel) except _core.Cancelled: record.append("cancel") raise @@ -974,7 +977,7 @@ def f(): # implicit assertion, Cancelled not raised via nursery assert record[1] == "exit" - # cancellable=False case: a cancel will pop out but be handled by + # abandon_on_cancel=False case: a cancel will pop out but be handled by # the appropriate cancel scope record = [] ev = threading.Event() @@ -990,7 +993,7 @@ def f(): assert "cancel" in record assert record[-1] == "exit" - # cancellable=True case: slightly different thread behavior needed + # abandon_on_cancel=True case: slightly different thread behavior needed # check thread is cancelled "soon" after abandonment def f(): # noqa: F811 ev.wait() @@ -1033,7 +1036,7 @@ async def test_reentry_doesnt_deadlock(): async def child() -> None: while True: - await to_thread_run_sync(from_thread_run, sleep, 0, cancellable=False) + await to_thread_run_sync(from_thread_run, sleep, 0, abandon_on_cancel=False) with move_on_after(2): async with _core.open_nursery() as nursery: diff --git a/trio/_threads.py b/trio/_threads.py index 023b1c198e..ecfcf9e258 100644 --- a/trio/_threads.py +++ b/trio/_threads.py @@ -175,6 +175,7 @@ async def to_thread_run_sync( # type: ignore[misc] sync_fn: Callable[..., RetT], *args: object, thread_name: str | None = None, + abandon_on_cancel: bool | None = None, cancellable: bool = False, limiter: CapacityLimiter | None = None, ) -> RetT: @@ -197,8 +198,10 @@ async def to_thread_run_sync( # type: ignore[misc] sync_fn: An arbitrary synchronous callable. *args: Positional arguments to pass to sync_fn. If you need keyword arguments, use :func:`functools.partial`. - cancellable (bool): Whether to allow cancellation of this operation. See - discussion below. + abandon_on_cancel (bool): Whether to abandon this thread upon + cancellation of this operation. See discussion below. + cancellable (bool): *Deprecated* synonym for ``abandon_on_cancel``. + Providing a value to ``abandon_on_cancel`` overrides this argument. thread_name (str): Optional string to set the name of the thread. Will always set `threading.Thread.name`, but only set the os name if pthread.h is available (i.e. most POSIX installations). @@ -224,17 +227,17 @@ async def to_thread_run_sync( # type: ignore[misc] starting the thread. But once the thread is running, there are two ways it can handle being cancelled: - * If ``cancellable=False``, the function ignores the cancellation and + * If ``abandon_on_cancel=False``, the function ignores the cancellation and keeps going, just like if we had called ``sync_fn`` synchronously. This is the default behavior. - * If ``cancellable=True``, then this function immediately raises + * If ``abandon_on_cancel=True``, then this function immediately raises `~trio.Cancelled`. In this case **the thread keeps running in background** – we just abandon it to do whatever it's going to do, and silently discard any return value or errors that it raises. Only use this if you know that the operation is safe and side-effect free. (For example: :func:`trio.socket.getaddrinfo` uses a thread with - ``cancellable=True``, because it doesn't really affect anything if a + ``abandon_on_cancel=True``, because it doesn't really affect anything if a stray hostname lookup keeps running in the background.) The ``limiter`` is only released after the thread has *actually* @@ -262,6 +265,8 @@ async def to_thread_run_sync( # type: ignore[misc] """ await trio.lowlevel.checkpoint_if_cancelled() + if abandon_on_cancel is not None: + cancellable = abandon_on_cancel abandon_on_cancel = bool(cancellable) # raise early if cancellable.__bool__ raises if limiter is None: limiter = current_default_thread_limiter() @@ -382,14 +387,14 @@ def from_thread_check_cancelled() -> None: """Raise `trio.Cancelled` if the associated Trio task entered a cancelled status. Only applicable to threads spawned by `trio.to_thread.run_sync`. Poll to allow - ``cancellable=False`` threads to raise :exc:`~trio.Cancelled` at a suitable - place, or to end abandoned ``cancellable=True`` threads sooner than they may + ``abandon_on_cancel=False`` threads to raise :exc:`~trio.Cancelled` at a suitable + place, or to end abandoned ``abandon_on_cancel=True`` threads sooner than they may otherwise. Raises: Cancelled: If the corresponding call to `trio.to_thread.run_sync` has had a delivery of cancellation attempted against it, regardless of the value of - ``cancellable`` supplied as an argument to it. + ``abandon_on_cancel`` supplied as an argument to it. RuntimeError: If this thread is not spawned from `trio.to_thread.run_sync`. .. note:: diff --git a/trio/_wait_for_object.py b/trio/_wait_for_object.py index d2193d9c86..53832513a3 100644 --- a/trio/_wait_for_object.py +++ b/trio/_wait_for_object.py @@ -45,7 +45,7 @@ async def WaitForSingleObject(obj: int | CData) -> None: WaitForMultipleObjects_sync, handle, cancel_handle, - cancellable=True, + abandon_on_cancel=True, limiter=trio.CapacityLimiter(math.inf), ) finally: From c119c2a8c82724397ef71f46e24f6382814c6dd8 Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Sat, 28 Oct 2023 13:49:32 -0400 Subject: [PATCH 2/7] add newsfragment --- newsfragments/2841.feature.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 newsfragments/2841.feature.rst diff --git a/newsfragments/2841.feature.rst b/newsfragments/2841.feature.rst new file mode 100644 index 0000000000..9792eb3b74 --- /dev/null +++ b/newsfragments/2841.feature.rst @@ -0,0 +1,3 @@ +To better reflect the underlying thread handling semantics, +`trio.to_thread.run_sync` gained a new keyword argument ``abandon_on_cancel`` +that overrides the older (now deprecated) ``cancellable`` flag. From cfd5cbd72c749018d6716cc2eefbdc5065b7f46a Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Mon, 30 Oct 2023 20:39:17 -0400 Subject: [PATCH 3/7] thoroughly deprecate applied suggestions from code review --- docs/source/reference-core.rst | 2 +- newsfragments/2841.feature.rst | 3 --- newsfragments/2841.removal.rst | 8 ++++++ trio/_tests/test_threads.py | 16 ++++++++++++ trio/_threads.py | 46 ++++++++++++++++++++++++++++------ 5 files changed, 64 insertions(+), 11 deletions(-) delete mode 100644 newsfragments/2841.feature.rst create mode 100644 newsfragments/2841.removal.rst diff --git a/docs/source/reference-core.rst b/docs/source/reference-core.rst index 98cf579399..a6dc5b1f5a 100644 --- a/docs/source/reference-core.rst +++ b/docs/source/reference-core.rst @@ -1836,7 +1836,7 @@ to spawn a child thread, and then use a :ref:`memory channel You can also use :func:`trio.from_thread.check_cancelled` to check for cancellation from a thread that was spawned by :func:`trio.to_thread.run_sync`. If the call to -:func:`~trio.to_thread.run_sync` was cancelled (even if ``abandon_on_cancel=False``!), then +:func:`~trio.to_thread.run_sync` was cancelled, then :func:`~trio.from_thread.check_cancelled` will raise :func:`trio.Cancelled`. It's like ``trio.from_thread.run(trio.sleep, 0)``, but much faster. diff --git a/newsfragments/2841.feature.rst b/newsfragments/2841.feature.rst deleted file mode 100644 index 9792eb3b74..0000000000 --- a/newsfragments/2841.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -To better reflect the underlying thread handling semantics, -`trio.to_thread.run_sync` gained a new keyword argument ``abandon_on_cancel`` -that overrides the older (now deprecated) ``cancellable`` flag. diff --git a/newsfragments/2841.removal.rst b/newsfragments/2841.removal.rst new file mode 100644 index 0000000000..687573f39a --- /dev/null +++ b/newsfragments/2841.removal.rst @@ -0,0 +1,8 @@ +To better reflect the underlying thread handling semantics, +the keyword argument for `trio.to_thread.run_sync` that was +previously called ``cancellable`` is now named ``abandon_on_cancel``. +It still does the same thing -- allow the thread to be abandoned +if the call to `trio.to_thread.run_sync` is cancelled -- but since we now +have other ways to propagate a cancellation without abandoning +the thread, "cancellable" has become somewhat of a misnomer. +The old ``cancellable`` name is now deprecated. diff --git a/trio/_tests/test_threads.py b/trio/_tests/test_threads.py index 73180ecacc..b65de9f609 100644 --- a/trio/_tests/test_threads.py +++ b/trio/_tests/test_threads.py @@ -1077,3 +1077,19 @@ async def child() -> None: async with _core.open_nursery() as nursery: for _ in range(4): nursery.start_soon(child) + + +async def test_cancellable_and_abandon_raises() -> None: + with pytest.raises(ValueError): + await to_thread_run_sync(bool, cancellable=True, abandon_on_cancel=False) + + with pytest.raises(ValueError): + await to_thread_run_sync(bool, cancellable=True, abandon_on_cancel=True) + + +async def test_cancellable_warns() -> None: + with pytest.warns(DeprecationWarning): + await to_thread_run_sync(bool, cancellable=False) + + with pytest.warns(DeprecationWarning): + await to_thread_run_sync(bool, cancellable=True) diff --git a/trio/_threads.py b/trio/_threads.py index 6df8136459..3732a6f169 100644 --- a/trio/_threads.py +++ b/trio/_threads.py @@ -6,9 +6,10 @@ import inspect import queue as stdlib_queue import threading +import warnings from collections.abc import Awaitable, Callable from itertools import count -from typing import Generic, TypeVar +from typing import Generic, TypeVar, overload import attr import outcome @@ -171,13 +172,35 @@ def run_in_system_nursery(self, token: TrioToken) -> None: token.run_sync_soon(self.run_sync) +@overload +async def to_thread_run_sync( + sync_fn: Callable[..., RetT], + *args: object, + thread_name: str | None = None, + abandon_on_cancel: bool = False, + limiter: CapacityLimiter | None = None, +) -> RetT: + ... + + +@overload +async def to_thread_run_sync( + sync_fn: Callable[..., RetT], + *args: object, + thread_name: str | None = None, + cancellable: bool = False, + limiter: CapacityLimiter | None = None, +) -> RetT: + ... + + @enable_ki_protection # Decorator used on function with Coroutine[Any, Any, RetT] async def to_thread_run_sync( # type: ignore[misc] sync_fn: Callable[..., RetT], *args: object, thread_name: str | None = None, abandon_on_cancel: bool | None = None, - cancellable: bool = False, + cancellable: bool | None = None, limiter: CapacityLimiter | None = None, ) -> RetT: """Convert a blocking operation into an async operation using a thread. @@ -201,8 +224,6 @@ async def to_thread_run_sync( # type: ignore[misc] arguments, use :func:`functools.partial`. abandon_on_cancel (bool): Whether to abandon this thread upon cancellation of this operation. See discussion below. - cancellable (bool): *Deprecated* synonym for ``abandon_on_cancel``. - Providing a value to ``abandon_on_cancel`` overrides this argument. thread_name (str): Optional string to set the name of the thread. Will always set `threading.Thread.name`, but only set the os name if pthread.h is available (i.e. most POSIX installations). @@ -266,9 +287,20 @@ async def to_thread_run_sync( # type: ignore[misc] """ await trio.lowlevel.checkpoint_if_cancelled() - if abandon_on_cancel is not None: - cancellable = abandon_on_cancel - abandon_on_cancel = bool(cancellable) # raise early if cancellable.__bool__ raises + if cancellable is not None: + if abandon_on_cancel is not None: + raise ValueError( + "Cannot set `cancellable` and `abandon_on_cancel` simultaneously." + ) + warnings.warn( + DeprecationWarning( + "`cancellable` keyword argument is deprecated, " + "use `abandon on cancel` instead." + ) + ) + abandon_on_cancel = cancellable + # raise early if abandon_on_cancel.__bool__ raises + abandon_on_cancel = bool(abandon_on_cancel) if limiter is None: limiter = current_default_thread_limiter() From 5149e84d87d9c8d023187cbc7d0636d0cd2508a5 Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Tue, 31 Oct 2023 16:54:50 -0400 Subject: [PATCH 4/7] clean mypy and ruff --- trio/_tests/test_threads.py | 12 ++++++------ trio/_threads.py | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/trio/_tests/test_threads.py b/trio/_tests/test_threads.py index b65de9f609..7b04b7865a 100644 --- a/trio/_tests/test_threads.py +++ b/trio/_tests/test_threads.py @@ -556,7 +556,7 @@ def release_on_behalf_of(self, borrower: Task) -> None: # TODO: should CapacityLimiter have an abc or protocol so users can modify it? # because currently it's `final` so writing code like this is not allowed. - await to_thread_run_sync(lambda: None, limiter=CustomLimiter()) # type: ignore[arg-type] + await to_thread_run_sync(lambda: None, limiter=CustomLimiter()) # type: ignore[call-overload] assert record == ["acquire", "release"] @@ -574,7 +574,7 @@ def release_on_behalf_of(self, borrower: Task) -> NoReturn: bs = BadCapacityLimiter() with pytest.raises(ValueError) as excinfo: - await to_thread_run_sync(lambda: None, limiter=bs) # type: ignore[arg-type] + await to_thread_run_sync(lambda: None, limiter=bs) # type: ignore[call-overload] assert excinfo.value.__context__ is None assert record == ["acquire", "release"] record = [] @@ -583,7 +583,7 @@ def release_on_behalf_of(self, borrower: Task) -> NoReturn: # chains with it d: dict[str, object] = {} with pytest.raises(ValueError) as excinfo: - await to_thread_run_sync(lambda: d["x"], limiter=bs) # type: ignore[arg-type] + await to_thread_run_sync(lambda: d["x"], limiter=bs) # type: ignore[call-overload] assert isinstance(excinfo.value.__context__, KeyError) assert record == ["acquire", "release"] @@ -892,7 +892,7 @@ def __bool__(self) -> bool: raise NotImplementedError with pytest.raises(NotImplementedError): - await to_thread_run_sync(int, abandon_on_cancel=BadBool()) # type: ignore[arg-type] + await to_thread_run_sync(int, abandon_on_cancel=BadBool()) # type: ignore[call-overload] async def test_from_thread_reuses_task() -> None: @@ -1081,10 +1081,10 @@ async def child() -> None: async def test_cancellable_and_abandon_raises() -> None: with pytest.raises(ValueError): - await to_thread_run_sync(bool, cancellable=True, abandon_on_cancel=False) + await to_thread_run_sync(bool, cancellable=True, abandon_on_cancel=False) # type: ignore[call-overload] with pytest.raises(ValueError): - await to_thread_run_sync(bool, cancellable=True, abandon_on_cancel=True) + await to_thread_run_sync(bool, cancellable=True, abandon_on_cancel=True) # type: ignore[call-overload] async def test_cancellable_warns() -> None: diff --git a/trio/_threads.py b/trio/_threads.py index 3732a6f169..8399c7dc8d 100644 --- a/trio/_threads.py +++ b/trio/_threads.py @@ -6,7 +6,6 @@ import inspect import queue as stdlib_queue import threading -import warnings from collections.abc import Awaitable, Callable from itertools import count from typing import Generic, TypeVar, overload @@ -16,7 +15,6 @@ from sniffio import current_async_library_cvar import trio -from trio._core._traps import RaiseCancelT from ._core import ( RunVar, @@ -25,6 +23,8 @@ enable_ki_protection, start_thread_soon, ) +from ._core._traps import RaiseCancelT +from ._deprecate import warn_deprecated from ._sync import CapacityLimiter from ._util import coroutine_or_error @@ -172,8 +172,8 @@ def run_in_system_nursery(self, token: TrioToken) -> None: token.run_sync_soon(self.run_sync) -@overload -async def to_thread_run_sync( +@overload # Decorator used on function with Coroutine[Any, Any, RetT] +async def to_thread_run_sync( # type: ignore[misc] sync_fn: Callable[..., RetT], *args: object, thread_name: str | None = None, @@ -183,8 +183,8 @@ async def to_thread_run_sync( ... -@overload -async def to_thread_run_sync( +@overload # Decorator used on function with Coroutine[Any, Any, RetT] +async def to_thread_run_sync( # type: ignore[misc] sync_fn: Callable[..., RetT], *args: object, thread_name: str | None = None, From 5e46fc2eee28d56ac7ea2f859375d41023f66cc7 Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Tue, 31 Oct 2023 17:01:08 -0400 Subject: [PATCH 5/7] Use TrioDeprecationWarning --- trio/_tests/test_threads.py | 5 +++-- trio/_threads.py | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/trio/_tests/test_threads.py b/trio/_tests/test_threads.py index 7b04b7865a..4002a0e808 100644 --- a/trio/_tests/test_threads.py +++ b/trio/_tests/test_threads.py @@ -29,6 +29,7 @@ CancelScope, CapacityLimiter, Event, + TrioDeprecationWarning, _core, fail_after, move_on_after, @@ -1088,8 +1089,8 @@ async def test_cancellable_and_abandon_raises() -> None: async def test_cancellable_warns() -> None: - with pytest.warns(DeprecationWarning): + with pytest.warns(TrioDeprecationWarning): await to_thread_run_sync(bool, cancellable=False) - with pytest.warns(DeprecationWarning): + with pytest.warns(TrioDeprecationWarning): await to_thread_run_sync(bool, cancellable=True) diff --git a/trio/_threads.py b/trio/_threads.py index 8399c7dc8d..53b4e004d6 100644 --- a/trio/_threads.py +++ b/trio/_threads.py @@ -292,11 +292,11 @@ async def to_thread_run_sync( # type: ignore[misc] raise ValueError( "Cannot set `cancellable` and `abandon_on_cancel` simultaneously." ) - warnings.warn( - DeprecationWarning( - "`cancellable` keyword argument is deprecated, " - "use `abandon on cancel` instead." - ) + warn_deprecated( + "`cancellable=` keyword argument", + "0.23.0", + issue=2841, + instead="`abandon_on_cancel=` keyword argument", ) abandon_on_cancel = cancellable # raise early if abandon_on_cancel.__bool__ raises From 8cfbf864c0a8c9035b34f9aaee09b00424340961 Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Tue, 31 Oct 2023 18:31:34 -0400 Subject: [PATCH 6/7] Apply suggestions from code review Co-authored-by: Joshua Oreman --- trio/_threads.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trio/_threads.py b/trio/_threads.py index 53b4e004d6..e6662c06d2 100644 --- a/trio/_threads.py +++ b/trio/_threads.py @@ -293,10 +293,10 @@ async def to_thread_run_sync( # type: ignore[misc] "Cannot set `cancellable` and `abandon_on_cancel` simultaneously." ) warn_deprecated( - "`cancellable=` keyword argument", + "The `cancellable=` keyword argument to `trio.to_thread.run_sync`", "0.23.0", issue=2841, - instead="`abandon_on_cancel=` keyword argument", + instead="`abandon_on_cancel=`", ) abandon_on_cancel = cancellable # raise early if abandon_on_cancel.__bool__ raises From 1959c30b3d7b2c3c30f73739731fc8b7adc3f6f6 Mon Sep 17 00:00:00 2001 From: richardsheridan Date: Tue, 31 Oct 2023 18:33:23 -0400 Subject: [PATCH 7/7] fix newsfragment type --- newsfragments/{2841.removal.rst => 2841.deprecated.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename newsfragments/{2841.removal.rst => 2841.deprecated.rst} (100%) diff --git a/newsfragments/2841.removal.rst b/newsfragments/2841.deprecated.rst similarity index 100% rename from newsfragments/2841.removal.rst rename to newsfragments/2841.deprecated.rst