Files
youknowdot 16e333bded Align asyncio initialization and future state with Python 3.15 (#9136)
* Align asyncio initialization and future state with Python 3.15

Reject native Future and Task reinitialization, validate task contexts, and restore task registration after failed eager context entry. Generate matching asyncio and concurrent Future sources and tests with update_lib from CPython d220409a13b77de7a6c30abe16ea90db01528edc. Preserve the existing pending-task reinitialization regression.

Assisted-by: OpenAI Codex:model version unavailable

* Remove asyncio snippet duplicating canonical reinitialization coverage

Keep the exact-final Lib tests as the reinitialization coverage instead of adding a new snippet for the same contract. Native and library implementation bytes remain unchanged.

Assisted-by: OpenAI Codex:model version unavailable

* Validate both objects in asyncio awaited-by helpers

Match CPython by ignoring non-native Future/Task waiters in add and discard. Keep the existing native reentrant hashing fixture reachable using Future subclasses and use native instances for its membership checks.

Assisted-by: OpenAI Codex:model version unavailable
2026-10-15 10:02:39 +00:00

103 lines
3.2 KiB
Python

"""The private _asyncio accessors, reached directly instead of through a loop.
CPython's _asyncio rejects every call below with "loop ... is not the running
loop" before it gets anywhere, and does not expose _current_tasks at all, so
these only run where they are reachable.
"""
import sys
from testutils import assert_raises
if sys.implementation.name != "rustpython":
sys.exit(0)
import _asyncio
def _task():
pass
# The "already entered" message formats both tasks; a plain function used to be
# formatted as the wrong type there.
_asyncio._enter_task(0, _task)
with assert_raises(RuntimeError) as cm:
_asyncio._enter_task(0, _task)
assert "Cannot enter into task" in str(cm.exception), cm.exception
assert "<function _task at " in str(cm.exception), cm.exception
_asyncio._leave_task(0, _task)
# The module-level task registry is writable from Python, so current_task() has
# to cope with it holding something that is not a mapping.
saved_current_tasks = _asyncio._current_tasks
try:
_asyncio._current_tasks = 42
assert _asyncio.current_task(loop=object()) is None
finally:
_asyncio._current_tasks = saved_current_tasks
class BadException(Exception):
def __new__(cls, *args):
return 42
# throw() has to check what __new__ handed back before treating it as an
# exception.
future = _asyncio.Future(loop=object())
with assert_raises(TypeError):
future.__await__().throw(BadException)
# InvalidStateError is looked up in asyncio.exceptions every time a pending
# future is asked for its result, so what is found there need not be an
# exception type at all.
import asyncio # noqa: E402
import asyncio.exceptions # noqa: E402
pending = _asyncio.Future(loop=object())
with assert_raises(asyncio.exceptions.InvalidStateError):
pending.result()
saved_invalid_state_error = asyncio.exceptions.InvalidStateError
try:
for replacement in (lambda *args: 42, None):
asyncio.InvalidStateError = replacement
asyncio.exceptions.InvalidStateError = replacement
with assert_raises(RuntimeError):
pending.result()
with assert_raises(RuntimeError):
pending.exception()
finally:
asyncio.InvalidStateError = saved_invalid_state_error
asyncio.exceptions.InvalidStateError = saved_invalid_state_error
# The awaited-by set is built with the waiter's __hash__, which can come back
# to the same future; the field must not be locked while that runs.
class Reentrant(_asyncio.Future):
def __hash__(self):
_asyncio.future_add_to_awaited_by(awaited, Reentrant(loop=object()))
return 1
def __eq__(self, other):
return self is other
awaited = _asyncio.Future(loop=object())
_asyncio.future_add_to_awaited_by(awaited, Reentrant(loop=object()))
with assert_raises(RecursionError):
# converting the single waiter into a set hashes both of them
_asyncio.future_add_to_awaited_by(awaited, Reentrant(loop=object()))
plain = _asyncio.Future(loop=object())
waiter = _asyncio.Future(loop=object())
_asyncio.future_add_to_awaited_by(plain, waiter)
_asyncio.future_add_to_awaited_by(plain, _asyncio.Future(loop=object()))
assert waiter in plain._asyncio_awaited_by
_asyncio.future_discard_from_awaited_by(plain, waiter)
assert waiter not in plain._asyncio_awaited_by
print("ok")