mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
* Unskip passing flaky CPython tests Remove RUSTPYTHON skip overlays after Linux release repeats passed for enum, threading, logging, subprocess, socket, int, re, strtod, and regrtest cases. Assisted-by: Grok:4.6 * Fix fork-child SIGSEGV and yield-from throw args Forget JoinHandle of threads that did not survive fork instead of dropping them. Zero-reinit the import lock and condvars in the child rather than unlock/notify. Resume the world if os.fork() fails. Call a yield-from subiterator throw() without None fillers so a 1-arg throw stays 1-arg. Drop the stale Windows expectedFailure on logging queue config and unskip test_reinit_tls_after_fork plus the asyncio local_addr family test. Assisted-by: Grok:4.6 * Restore import lock depth after fork; add WASI clock info Zeroing IMP_LOCK in the child discarded its recursion count, so imp.release_lock() raised and test_fork1 exited 1. Re-acquire the saved depth, then drop the extra pre-fork hold. Expose time.get_clock_info on WASI via clock_getres so test_int Stopwatch can run. Unskip lock_tests test_acquire_contended and test_thread_leak. Assisted-by: Grok:4.6 * Honor chmod follow_symlinks=False via fchmodat Linux chmod followed the symlink, so dangling links raised FileNotFoundError instead of NotImplementedError. Use fchmodat with AT_SYMLINK_NOFOLLOW and map ENOTSUP to NotImplementedError. Unskip httpservers truncated Content-Length (CPython already skips as root) and tempfile symlink-mode cleanup on Linux. Assisted-by: Grok:4.6 * Unskip httplib local bad-hostname HTTPS test The skip was leftover. With default ssl-rustls the test passes under -W error on Linux. Assisted-by: Grok:4.6 * Fix list cmp deadlock, wasm digit overflow, REPL prompt Do not hold list/deque locks across item comparison callbacks. Compute int-to-str digit bounds in u64 so the check cannot wrap on wasm32. Snapshot set contents when list.extend copies a set. For a non-TTY stdin, write the REPL prompt to stderr and read lines from stdin. Flush stdout/stderr after each interactive command. Set sys.ps1/ps2 to ">>> " and "... ". Unskip list mutation compares, asyncio all_tasks race, importlib deadlock/parallel/circular-import tests, and REPL stdout flush. Assisted-by: Grok:4.6 * Avoid unnecessary u64 cast in int-to-str digit check bits() is already u64, so `bits as u64` fails clippy unnecessary_cast under -Dwarnings. Assisted-by: Grok:4.6
228 lines
7.6 KiB
Python
Vendored
228 lines
7.6 KiB
Python
Vendored
import asyncio
|
|
import threading
|
|
import unittest
|
|
from threading import Thread
|
|
from unittest import TestCase
|
|
import weakref
|
|
from test import support
|
|
from test.support import threading_helper
|
|
|
|
threading_helper.requires_working_threading(module=True)
|
|
|
|
|
|
class MyException(Exception):
|
|
pass
|
|
|
|
|
|
def tearDownModule():
|
|
asyncio.events._set_event_loop_policy(None)
|
|
|
|
|
|
class TestFreeThreading:
|
|
def test_all_tasks_race(self) -> None:
|
|
async def main():
|
|
loop = asyncio.get_running_loop()
|
|
future = loop.create_future()
|
|
|
|
async def coro():
|
|
await future
|
|
|
|
tasks = set()
|
|
|
|
async with asyncio.TaskGroup() as tg:
|
|
for _ in range(100):
|
|
tasks.add(tg.create_task(coro()))
|
|
|
|
all_tasks = asyncio.all_tasks(loop)
|
|
self.assertEqual(len(all_tasks), 101)
|
|
|
|
for task in all_tasks:
|
|
self.assertEqual(task.get_loop(), loop)
|
|
self.assertFalse(task.done())
|
|
|
|
current = asyncio.current_task()
|
|
self.assertEqual(current.get_loop(), loop)
|
|
self.assertSetEqual(all_tasks, tasks | {current})
|
|
future.set_result(None)
|
|
|
|
def runner():
|
|
with asyncio.Runner() as runner:
|
|
loop = runner.get_loop()
|
|
loop.set_task_factory(self.factory)
|
|
runner.run(main())
|
|
|
|
threads = []
|
|
|
|
for _ in range(10):
|
|
thread = Thread(target=runner)
|
|
threads.append(thread)
|
|
|
|
with threading_helper.start_threads(threads):
|
|
pass
|
|
|
|
def test_all_tasks_different_thread(self) -> None:
|
|
loop = None
|
|
started = threading.Event()
|
|
done = threading.Event() # used for main task not finishing early
|
|
async def coro():
|
|
await asyncio.Future()
|
|
|
|
lock = threading.Lock()
|
|
tasks = set()
|
|
|
|
async def main():
|
|
nonlocal tasks, loop
|
|
loop = asyncio.get_running_loop()
|
|
started.set()
|
|
for i in range(1000):
|
|
with lock:
|
|
asyncio.create_task(coro())
|
|
tasks = asyncio.all_tasks(loop)
|
|
done.wait()
|
|
|
|
runner = threading.Thread(target=lambda: asyncio.run(main()))
|
|
|
|
def check():
|
|
started.wait()
|
|
with lock:
|
|
self.assertSetEqual(tasks & asyncio.all_tasks(loop), tasks)
|
|
|
|
threads = [threading.Thread(target=check) for _ in range(10)]
|
|
runner.start()
|
|
|
|
with threading_helper.start_threads(threads):
|
|
pass
|
|
|
|
done.set()
|
|
runner.join()
|
|
|
|
def test_task_different_thread_finalized(self) -> None:
|
|
task = None
|
|
async def func():
|
|
nonlocal task
|
|
task = asyncio.current_task()
|
|
def runner():
|
|
with asyncio.Runner() as runner:
|
|
loop = runner.get_loop()
|
|
loop.set_task_factory(self.factory)
|
|
runner.run(func())
|
|
thread = Thread(target=runner)
|
|
thread.start()
|
|
thread.join()
|
|
wr = weakref.ref(task)
|
|
del thread
|
|
del task
|
|
# task finalization in different thread shouldn't crash
|
|
support.gc_collect()
|
|
self.assertIsNone(wr())
|
|
|
|
def test_run_coroutine_threadsafe(self) -> None:
|
|
results = []
|
|
|
|
def in_thread(loop: asyncio.AbstractEventLoop):
|
|
coro = asyncio.sleep(0.1, result=42)
|
|
fut = asyncio.run_coroutine_threadsafe(coro, loop)
|
|
result = fut.result()
|
|
self.assertEqual(result, 42)
|
|
results.append(result)
|
|
|
|
async def main():
|
|
loop = asyncio.get_running_loop()
|
|
async with asyncio.TaskGroup() as tg:
|
|
for _ in range(10):
|
|
tg.create_task(asyncio.to_thread(in_thread, loop))
|
|
self.assertEqual(results, [42] * 10)
|
|
|
|
with asyncio.Runner() as r:
|
|
loop = r.get_loop()
|
|
loop.set_task_factory(self.factory)
|
|
r.run(main())
|
|
|
|
def test_run_coroutine_threadsafe_exception(self) -> None:
|
|
async def coro():
|
|
await asyncio.sleep(0)
|
|
raise MyException("test")
|
|
|
|
def in_thread(loop: asyncio.AbstractEventLoop):
|
|
fut = asyncio.run_coroutine_threadsafe(coro(), loop)
|
|
return fut.result()
|
|
|
|
async def main():
|
|
loop = asyncio.get_running_loop()
|
|
tasks = []
|
|
for _ in range(10):
|
|
task = loop.create_task(asyncio.to_thread(in_thread, loop))
|
|
tasks.append(task)
|
|
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
|
|
self.assertEqual(len(results), 10)
|
|
for result in results:
|
|
self.assertIsInstance(result, MyException)
|
|
self.assertEqual(str(result), "test")
|
|
|
|
with asyncio.Runner() as r:
|
|
loop = r.get_loop()
|
|
loop.set_task_factory(self.factory)
|
|
r.run(main())
|
|
|
|
|
|
class TestPyFreeThreading(TestFreeThreading, TestCase):
|
|
|
|
def setUp(self):
|
|
self._old_current_task = asyncio.current_task
|
|
asyncio.current_task = asyncio.tasks.current_task = asyncio.tasks._py_current_task
|
|
self._old_all_tasks = asyncio.all_tasks
|
|
asyncio.all_tasks = asyncio.tasks.all_tasks = asyncio.tasks._py_all_tasks
|
|
self._old_Task = asyncio.Task
|
|
asyncio.Task = asyncio.tasks.Task = asyncio.tasks._PyTask
|
|
self._old_Future = asyncio.Future
|
|
asyncio.Future = asyncio.futures.Future = asyncio.futures._PyFuture
|
|
return super().setUp()
|
|
|
|
def tearDown(self):
|
|
asyncio.current_task = asyncio.tasks.current_task = self._old_current_task
|
|
asyncio.all_tasks = asyncio.tasks.all_tasks = self._old_all_tasks
|
|
asyncio.Task = asyncio.tasks.Task = self._old_Task
|
|
asyncio.Future = asyncio.tasks.Future = self._old_Future
|
|
return super().tearDown()
|
|
|
|
def factory(self, loop, coro, **kwargs):
|
|
return asyncio.tasks._PyTask(coro, loop=loop, **kwargs)
|
|
|
|
|
|
@unittest.skipUnless(hasattr(asyncio.tasks, "_c_all_tasks"), "requires _asyncio")
|
|
class TestCFreeThreading(TestFreeThreading, TestCase):
|
|
|
|
def setUp(self):
|
|
self._old_current_task = asyncio.current_task
|
|
asyncio.current_task = asyncio.tasks.current_task = asyncio.tasks._c_current_task
|
|
self._old_all_tasks = asyncio.all_tasks
|
|
asyncio.all_tasks = asyncio.tasks.all_tasks = asyncio.tasks._c_all_tasks
|
|
self._old_Task = asyncio.Task
|
|
asyncio.Task = asyncio.tasks.Task = asyncio.tasks._CTask
|
|
self._old_Future = asyncio.Future
|
|
asyncio.Future = asyncio.futures.Future = asyncio.futures._CFuture
|
|
return super().setUp()
|
|
|
|
def tearDown(self):
|
|
asyncio.current_task = asyncio.tasks.current_task = self._old_current_task
|
|
asyncio.all_tasks = asyncio.tasks.all_tasks = self._old_all_tasks
|
|
asyncio.Task = asyncio.tasks.Task = self._old_Task
|
|
asyncio.Future = asyncio.futures.Future = self._old_Future
|
|
return super().tearDown()
|
|
|
|
|
|
def factory(self, loop, coro, **kwargs):
|
|
return asyncio.tasks._CTask(coro, loop=loop, **kwargs)
|
|
|
|
|
|
class TestEagerPyFreeThreading(TestPyFreeThreading):
|
|
def factory(self, loop, coro, eager_start=True, **kwargs):
|
|
return asyncio.tasks._PyTask(coro, loop=loop, **kwargs, eager_start=eager_start)
|
|
|
|
|
|
@unittest.skipUnless(hasattr(asyncio.tasks, "_c_all_tasks"), "requires _asyncio")
|
|
class TestEagerCFreeThreading(TestCFreeThreading, TestCase):
|
|
def factory(self, loop, coro, eager_start=True, **kwargs):
|
|
return asyncio.tasks._CTask(coro, loop=loop, **kwargs, eager_start=eager_start)
|