From 7acc7cf603dee1e129957a68b1584e7cc9a9536a Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Thu, 27 Jul 2023 20:33:46 +0200 Subject: [PATCH] Stop using chess.engine.EventLoopPolicy --- chess/engine.py | 80 +-------------------------- docs/engine.rst | 6 -- examples/bratko_kopec/bratko_kopec.py | 1 - fuzz/engine.py | 1 - test.py | 17 ------ 5 files changed, 2 insertions(+), 103 deletions(-) diff --git a/chess/engine.py b/chess/engine.py index 6fac0c602..6958479f9 100644 --- a/chess/engine.py +++ b/chess/engine.py @@ -42,77 +42,8 @@ MANAGED_OPTIONS = ["uci_chess960", "uci_variant", "multipv", "ponder"] -class EventLoopPolicy(asyncio.AbstractEventLoopPolicy): - """ - An event loop policy for thread-local event loops and child watchers. - Ensures each event loop is capable of spawning and watching subprocesses, - even when not running on the main thread. - - Windows: Uses :class:`~asyncio.ProactorEventLoop`. - - Unix: Uses :class:`~asyncio.SelectorEventLoop`. If available, - :class:`~asyncio.PidfdChildWatcher` is used to detect subprocess - termination (Python 3.9+ on Linux 5.3+). Otherwise, the default child - watcher is used on the main thread and relatively slow eager polling - is used on all other threads. - """ - class _Local(threading.local): - loop: Optional[asyncio.AbstractEventLoop] = None - set_called = False - watcher: Optional[asyncio.AbstractChildWatcher] = None - - def __init__(self) -> None: - self._local = self._Local() - - def get_event_loop(self) -> asyncio.AbstractEventLoop: - if self._local.loop is None and not self._local.set_called and threading.current_thread() is threading.main_thread(): - self.set_event_loop(self.new_event_loop()) - if self._local.loop is None: - raise RuntimeError(f"no current event loop in thread {threading.current_thread().name!r}") - return self._local.loop - - def set_event_loop(self, loop: Optional[asyncio.AbstractEventLoop]) -> None: - assert loop is None or isinstance(loop, asyncio.AbstractEventLoop) - self._local.set_called = True - self._local.loop = loop - if self._local.watcher is not None: - self._local.watcher.attach_loop(loop) - - def new_event_loop(self) -> asyncio.AbstractEventLoop: - return asyncio.ProactorEventLoop() if sys.platform == "win32" else asyncio.SelectorEventLoop() # type: ignore - - def get_child_watcher(self) -> asyncio.AbstractChildWatcher: - if self._local.watcher is None: - self._local.watcher = self._init_watcher() - self._local.watcher.attach_loop(self._local.loop) - return self._local.watcher - - def set_child_watcher(self, watcher: Optional[asyncio.AbstractChildWatcher]) -> None: - assert watcher is None or isinstance(watcher, asyncio.AbstractChildWatcher) - if self._local.watcher is not None: - self._local.watcher.close() - self._local.watcher = watcher - - def _init_watcher(self) -> asyncio.AbstractChildWatcher: - if sys.platform == "win32": - raise NotImplementedError - - try: - os.close(os.pidfd_open(os.getpid())) - watcher: asyncio.AbstractChildWatcher = asyncio.PidfdChildWatcher() - LOGGER.debug("Using PidfdChildWatcher") - return watcher - except (AttributeError, OSError): - # Before Python 3.9 or before Linux 5.3 or the syscall is not - # permitted. - pass - - if threading.current_thread() is threading.main_thread(): - LOGGER.debug("Using SafeChildWatcher") - return asyncio.SafeChildWatcher() - else: - LOGGER.debug("Using ThreadedChildWatcher") - return asyncio.ThreadedChildWatcher() +# No longer needed, but alias kept around for compatibility. +EventLoopPolicy = asyncio.DefaultEventLoopPolicy def run_in_background(coroutine: Callable[[concurrent.futures.Future[T]], Coroutine[Any, Any, None]], *, name: Optional[str] = None, debug: bool = False, _policy_lock: threading.Lock = threading.Lock()) -> T: @@ -122,16 +53,9 @@ def run_in_background(coroutine: Callable[[concurrent.futures.Future[T]], Corout Blocks on *future* and returns the result as soon as it is resolved. The coroutine and all remaining tasks continue running in the background until complete. - - Note: This installs a :class:`chess.engine.EventLoopPolicy` for the entire - process. """ assert asyncio.iscoroutinefunction(coroutine) - with _policy_lock: - if not isinstance(asyncio.get_event_loop_policy(), EventLoopPolicy): - asyncio.set_event_loop_policy(EventLoopPolicy()) - future: concurrent.futures.Future[T] = concurrent.futures.Future() def background() -> None: diff --git a/docs/engine.rst b/docs/engine.rst index 02dd4548f..a326430e0 100644 --- a/docs/engine.rst +++ b/docs/engine.rst @@ -58,7 +58,6 @@ Example: Let Stockfish play against itself, 100 milliseconds per move. await engine.quit() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) .. autoclass:: chess.engine.Protocol @@ -126,7 +125,6 @@ Example: await engine.quit() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) .. autoclass:: chess.engine.Protocol @@ -189,7 +187,6 @@ Example: Stream information from the engine and stop on an arbitrary condition. await engine.quit() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) .. autoclass:: chess.engine.Protocol @@ -243,7 +240,6 @@ Options # [...] - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) .. autoclass:: chess.engine.Protocol @@ -315,5 +311,3 @@ Reference .. autoclass:: chess.engine.SimpleAnalysisResult :members: - -.. autofunction:: chess.engine.EventLoopPolicy diff --git a/examples/bratko_kopec/bratko_kopec.py b/examples/bratko_kopec/bratko_kopec.py index 62e3655a6..2553f4399 100755 --- a/examples/bratko_kopec/bratko_kopec.py +++ b/examples/bratko_kopec/bratko_kopec.py @@ -152,5 +152,4 @@ async def main() -> None: if __name__ == "__main__": - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) diff --git a/fuzz/engine.py b/fuzz/engine.py index a08d08bf3..ad0cb9fe9 100644 --- a/fuzz/engine.py +++ b/fuzz/engine.py @@ -7,7 +7,6 @@ logging.getLogger("chess.engine").setLevel(logging.CRITICAL) -asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) @PythonFuzz diff --git a/test.py b/test.py index 6419eb89f..73b96f71c 100755 --- a/test.py +++ b/test.py @@ -3114,7 +3114,6 @@ async def main(): await protocol.ping() mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_uci_debug(self): @@ -3130,7 +3129,6 @@ async def main(): protocol.debug(False) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_uci_go(self): @@ -3173,7 +3171,6 @@ async def main(): self.assertEqual(result.ponder, None) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_iota_log(self): @@ -3196,7 +3193,6 @@ async def main(): await protocol.play(board, chess.engine.Limit(time=5.0)) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_uci_analyse_mode(self): @@ -3243,7 +3239,6 @@ async def main(): self.assertEqual(best.ponder, chess.Move.from_uci("e7e5")) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_uci_play_after_analyse(self): @@ -3271,7 +3266,6 @@ async def main(): mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_uci_ponderhit(self): @@ -3387,7 +3381,6 @@ async def main(): mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_uci_info(self): @@ -3474,7 +3467,6 @@ async def main(): await protocol.send_game_result(checkmate_board) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_hiarcs_bestmove(self): @@ -3499,7 +3491,6 @@ async def main(): self.assertEqual(result.info["string"], "keep double space") mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_xboard_options(self): @@ -3562,7 +3553,6 @@ async def main(): await protocol.configure({"buttonvar": None}) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_xboard_replay(self): @@ -3624,7 +3614,6 @@ async def main(): self.assertEqual(result.move, board.parse_san("d4")) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_xboard_opponent(self): @@ -3682,7 +3671,6 @@ async def main(): result = await protocol.play(board, limit, game="bad game", opponent=bad_opponent) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_xboard_result(self): @@ -3772,7 +3760,6 @@ async def main(): await protocol.send_game_result(material_board) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_xboard_analyse(self): @@ -3810,7 +3797,6 @@ async def main(): self.assertEqual(info["pv"], [chess.Move.from_uci(move) for move in ["f7f6", "e2e4", "e7e6"]]) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_xboard_level(self): @@ -3855,7 +3841,6 @@ async def main(): self.assertEqual(result.move, chess.Move.from_uci("d2d4")) mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) def test_xboard_error(self): @@ -3874,7 +3859,6 @@ async def main(): mock.assert_done() - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) @catchAndSkip(FileNotFoundError, "need /bin/bash") @@ -3886,7 +3870,6 @@ async def main(): self.assertNotEqual(results[0], None) self.assertNotEqual(results[1], None) - asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy()) asyncio.run(main()) @catchAndSkip(FileNotFoundError, "need /bin/bash")