Part of the retire wait_for_psce epic: araucaria-project/ocabox-common#15 (full rationale there). Summary: wait_for_psce works around an asyncio.wait_for bug fixed in CPython long ago (3.12 reimplemented wait_for on asyncio.timeout()); the workaround's shield(wait_for(task)) sandwich creates orphanable middle tasks (Task exception was never retrieved, see ocabox-server#46) and can replace CancelledError with the task's real exception on the cancel path.
Replacement pattern
# before:
result = await wait_for_psce(coro(...), timeout=t)
# after (py ≥ 3.11):
async with asyncio.timeout(t):
result = await coro(...)
Rules:
TimeoutError is asyncio.TimeoutError since 3.11 — do NOT touch existing exception handlers.
- Negative/zero timeouts behave the same (immediate
TimeoutError).
- When the argument is an already-created
Task/Future (not a coroutine), the pattern is async with asyncio.timeout(t): await fut — works, but double-check the site did not rely on the extra task wrapper.
- Remove any
# do not use asyncio.wait_for(), use wait_for_psce() warning comments near the call sites.
- Behavior must stay identical for callers: same result, same
TimeoutError on timeout, CancelledError (not the inner exception) when the caller is cancelled.
Scope (this repo — PUBLIC API, deprecate instead of delete)
wait_for_psce is exported from serverish.base — external users may depend on it, so this repo gets a deprecation shim, not deletion:
- Bump
python = "^3.10" → "^3.11" in pyproject.toml.
- Migrate internal call sites:
serverish/messenger/msg_reader.py — 1 site (wait_for_psce(self._msg_processed.wait(), timeout=to)).
serverish/base/task_manager.py — 1 site (wait_for_psce(task.task, timeout) — NOTE: the argument is an existing Task; use async with asyncio.timeout(timeout): await task.task and double-check the surrounding cancel/cleanup logic still holds).
- Replace the body of
serverish/base/asyncio_util_functions.py::wait_for_psce with a thin shim:
async def wait_for_psce(fut, timeout):
"""Deprecated: use asyncio.timeout(). Kept for API compatibility."""
warnings.warn("wait_for_psce is deprecated; use asyncio.timeout()",
DeprecationWarning, stacklevel=2)
async with asyncio.timeout(timeout):
return await fut
Keep the serverish.base export. Removal is a future major-version decision.
- Version bump REQUIRED in this PR:
2.3.0 → 2.4.0 in pyproject.toml (this repo keeps no CHANGELOG — skip that part).
Acceptance
- All listed call sites migrated; the local
wait_for_psce copy deleted; no remaining references (grep -r wait_for_psce).
- Python floor bumped as specified; version bump + compact CHANGELOG entry.
- Cancellation-semantics tests (add if missing): (1) result delivery, (2)
TimeoutError on timeout, (3) caller cancelled → CancelledError propagates and the awaited work is cancelled. Model them on ocabox-server/test/utils/test_wait_for_psce.py (post-#47), adapted to the new pattern.
- Existing test suite green.
🤖 Generated with Claude Code
Part of the retire wait_for_psce epic: araucaria-project/ocabox-common#15 (full rationale there). Summary:
wait_for_psceworks around anasyncio.wait_forbug fixed in CPython long ago (3.12 reimplementedwait_foronasyncio.timeout()); the workaround'sshield(wait_for(task))sandwich creates orphanable middle tasks (Task exception was never retrieved, see ocabox-server#46) and can replaceCancelledErrorwith the task's real exception on the cancel path.Replacement pattern
Rules:
TimeoutErrorisasyncio.TimeoutErrorsince 3.11 — do NOT touch existing exception handlers.TimeoutError).Task/Future(not a coroutine), the pattern isasync with asyncio.timeout(t): await fut— works, but double-check the site did not rely on the extra task wrapper.# do not use asyncio.wait_for(), use wait_for_psce()warning comments near the call sites.TimeoutErroron timeout,CancelledError(not the inner exception) when the caller is cancelled.Scope (this repo — PUBLIC API, deprecate instead of delete)
wait_for_psceis exported fromserverish.base— external users may depend on it, so this repo gets a deprecation shim, not deletion:python = "^3.10"→"^3.11"in pyproject.toml.serverish/messenger/msg_reader.py— 1 site (wait_for_psce(self._msg_processed.wait(), timeout=to)).serverish/base/task_manager.py— 1 site (wait_for_psce(task.task, timeout)— NOTE: the argument is an existing Task; useasync with asyncio.timeout(timeout): await task.taskand double-check the surrounding cancel/cleanup logic still holds).serverish/base/asyncio_util_functions.py::wait_for_pscewith a thin shim:serverish.baseexport. Removal is a future major-version decision.2.3.0→2.4.0inpyproject.toml(this repo keeps no CHANGELOG — skip that part).Acceptance
wait_for_pscecopy deleted; no remaining references (grep -r wait_for_psce).TimeoutErroron timeout, (3) caller cancelled →CancelledErrorpropagates and the awaited work is cancelled. Model them onocabox-server/test/utils/test_wait_for_psce.py(post-#47), adapted to the new pattern.🤖 Generated with Claude Code