From d718f91aa175bcc85113e1c6d46f8578a7f09771 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 25 Jul 2024 13:43:49 -0700 Subject: [PATCH 1/8] PYTHON-4537 - Use selector asyncio loop on windows tests --- test/__init__.py | 6 ++++++ test/asynchronous/__init__.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/test/__init__.py b/test/__init__.py index b8bd185110..900befe6f1 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -79,6 +79,12 @@ _IS_SYNC = True +# The default asyncio loop implementation on Windows +# has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) +# We explicitly use a different loop implementation here to prevent that issue +if not _IS_SYNC and sys.platform == "win32": + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + class ClientContext: client: MongoClient diff --git a/test/asynchronous/__init__.py b/test/asynchronous/__init__.py index 6f0785c4ff..d9aba4e989 100644 --- a/test/asynchronous/__init__.py +++ b/test/asynchronous/__init__.py @@ -79,6 +79,12 @@ _IS_SYNC = False +# The default asyncio loop implementation on Windows +# has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) +# We explicitly use a different loop implementation here to prevent that issue +if not _IS_SYNC and sys.platform == "win32": + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + class AsyncClientContext: client: AsyncMongoClient From 4b81b3cb64ab2ddaef90221288aa31e3549086f6 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 25 Jul 2024 14:15:08 -0700 Subject: [PATCH 2/8] Use selector event loop in all background threads --- pymongo/asynchronous/periodic_executor.py | 6 ++++++ pymongo/synchronous/periodic_executor.py | 6 ++++++ test/__init__.py | 6 ------ test/asynchronous/__init__.py | 6 ------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pymongo/asynchronous/periodic_executor.py b/pymongo/asynchronous/periodic_executor.py index 337d10f133..7918e09f6f 100644 --- a/pymongo/asynchronous/periodic_executor.py +++ b/pymongo/asynchronous/periodic_executor.py @@ -27,6 +27,12 @@ _IS_SYNC = False +# The default asyncio loop implementation on Windows +# has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) +# We explicitly use a different loop implementation here to prevent that issue +if not _IS_SYNC and sys.platform == "win32": + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + class PeriodicExecutor: def __init__( diff --git a/pymongo/synchronous/periodic_executor.py b/pymongo/synchronous/periodic_executor.py index 43125016bc..289f8b2f45 100644 --- a/pymongo/synchronous/periodic_executor.py +++ b/pymongo/synchronous/periodic_executor.py @@ -27,6 +27,12 @@ _IS_SYNC = True +# The default asyncio loop implementation on Windows +# has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) +# We explicitly use a different loop implementation here to prevent that issue +if not _IS_SYNC and sys.platform == "win32": + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + class PeriodicExecutor: def __init__( diff --git a/test/__init__.py b/test/__init__.py index 900befe6f1..b8bd185110 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -79,12 +79,6 @@ _IS_SYNC = True -# The default asyncio loop implementation on Windows -# has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) -# We explicitly use a different loop implementation here to prevent that issue -if not _IS_SYNC and sys.platform == "win32": - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - class ClientContext: client: MongoClient diff --git a/test/asynchronous/__init__.py b/test/asynchronous/__init__.py index d9aba4e989..6f0785c4ff 100644 --- a/test/asynchronous/__init__.py +++ b/test/asynchronous/__init__.py @@ -79,12 +79,6 @@ _IS_SYNC = False -# The default asyncio loop implementation on Windows -# has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) -# We explicitly use a different loop implementation here to prevent that issue -if not _IS_SYNC and sys.platform == "win32": - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - class AsyncClientContext: client: AsyncMongoClient From 168a67b641e9c897737f64b7965eede9350a5482 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 25 Jul 2024 14:19:45 -0700 Subject: [PATCH 3/8] Fix typing errors --- pymongo/asynchronous/periodic_executor.py | 2 +- pymongo/synchronous/periodic_executor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pymongo/asynchronous/periodic_executor.py b/pymongo/asynchronous/periodic_executor.py index 7918e09f6f..34e21445c7 100644 --- a/pymongo/asynchronous/periodic_executor.py +++ b/pymongo/asynchronous/periodic_executor.py @@ -31,7 +31,7 @@ # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) # We explicitly use a different loop implementation here to prevent that issue if not _IS_SYNC and sys.platform == "win32": - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # type: ignore[attr-defined] class PeriodicExecutor: diff --git a/pymongo/synchronous/periodic_executor.py b/pymongo/synchronous/periodic_executor.py index 289f8b2f45..10e45778c7 100644 --- a/pymongo/synchronous/periodic_executor.py +++ b/pymongo/synchronous/periodic_executor.py @@ -31,7 +31,7 @@ # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) # We explicitly use a different loop implementation here to prevent that issue if not _IS_SYNC and sys.platform == "win32": - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # type: ignore[attr-defined] class PeriodicExecutor: From f05f43471612a191169471dbd9327ae9f65b16b3 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 25 Jul 2024 14:31:46 -0700 Subject: [PATCH 4/8] Use set_event_loop inside PeriodicExecutor.open() --- pymongo/asynchronous/periodic_executor.py | 12 ++++++------ pymongo/synchronous/periodic_executor.py | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pymongo/asynchronous/periodic_executor.py b/pymongo/asynchronous/periodic_executor.py index 34e21445c7..a6d1c6243f 100644 --- a/pymongo/asynchronous/periodic_executor.py +++ b/pymongo/asynchronous/periodic_executor.py @@ -27,12 +27,6 @@ _IS_SYNC = False -# The default asyncio loop implementation on Windows -# has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) -# We explicitly use a different loop implementation here to prevent that issue -if not _IS_SYNC and sys.platform == "win32": - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # type: ignore[attr-defined] - class PeriodicExecutor: def __init__( @@ -78,6 +72,12 @@ def open(self) -> None: Not safe to call from multiple threads at once. """ + # The default asyncio loop implementation on Windows + # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) + # We explicitly use a different loop implementation here to prevent that issue + if not _IS_SYNC and sys.platform == "win32": + asyncio.set_event_loop(asyncio.SelectorEventLoop()) + with self._lock: if self._thread_will_exit: # If the background thread has read self._stopped as True diff --git a/pymongo/synchronous/periodic_executor.py b/pymongo/synchronous/periodic_executor.py index 10e45778c7..49ebb43788 100644 --- a/pymongo/synchronous/periodic_executor.py +++ b/pymongo/synchronous/periodic_executor.py @@ -27,12 +27,6 @@ _IS_SYNC = True -# The default asyncio loop implementation on Windows -# has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) -# We explicitly use a different loop implementation here to prevent that issue -if not _IS_SYNC and sys.platform == "win32": - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # type: ignore[attr-defined] - class PeriodicExecutor: def __init__( @@ -78,6 +72,12 @@ def open(self) -> None: Not safe to call from multiple threads at once. """ + # The default asyncio loop implementation on Windows + # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) + # We explicitly use a different loop implementation here to prevent that issue + if not _IS_SYNC and sys.platform == "win32": + asyncio.set_event_loop(asyncio.SelectorEventLoop()) + with self._lock: if self._thread_will_exit: # If the background thread has read self._stopped as True From 38fe592c57a891a31fd6ae9578391812ac4d3a27 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 25 Jul 2024 15:08:13 -0700 Subject: [PATCH 5/8] Fix tests --- pymongo/asynchronous/periodic_executor.py | 18 +++++++++++++++--- pymongo/synchronous/periodic_executor.py | 18 +++++++++++++++--- test/__init__.py | 6 ++++++ test/asynchronous/__init__.py | 6 ++++++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/pymongo/asynchronous/periodic_executor.py b/pymongo/asynchronous/periodic_executor.py index a6d1c6243f..22a19d0723 100644 --- a/pymongo/asynchronous/periodic_executor.py +++ b/pymongo/asynchronous/periodic_executor.py @@ -65,7 +65,21 @@ def __repr__(self) -> str: return f"<{self.__class__.__name__}(name={self._name}) object at 0x{id(self):x}>" def _run_async(self) -> None: - asyncio.run(self._run()) # type: ignore[func-returns-value] + # The default asyncio loop implementation on Windows + # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) + # We explicitly use a different loop implementation here to prevent that issue + if ( + not _IS_SYNC + and sys.platform == "win32" + and asyncio.get_event_loop_policy() == asyncio.WindowsProactorEventLoopPolicy + ): + loop = asyncio.WindowsSelectorEventLoopPolicy().new_event_loop() # type: ignore[attr-defined] + try: + loop.run_until_complete(self._run()) # type: ignore[func-returns-value] + finally: + loop.close() + else: + asyncio.run(self._run()) # type: ignore[func-returns-value] def open(self) -> None: """Start. Multiple calls have no effect. @@ -75,8 +89,6 @@ def open(self) -> None: # The default asyncio loop implementation on Windows # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) # We explicitly use a different loop implementation here to prevent that issue - if not _IS_SYNC and sys.platform == "win32": - asyncio.set_event_loop(asyncio.SelectorEventLoop()) with self._lock: if self._thread_will_exit: diff --git a/pymongo/synchronous/periodic_executor.py b/pymongo/synchronous/periodic_executor.py index 49ebb43788..33e54ef219 100644 --- a/pymongo/synchronous/periodic_executor.py +++ b/pymongo/synchronous/periodic_executor.py @@ -65,7 +65,21 @@ def __repr__(self) -> str: return f"<{self.__class__.__name__}(name={self._name}) object at 0x{id(self):x}>" def _run_async(self) -> None: - asyncio.run(self._run()) # type: ignore[func-returns-value] + # The default asyncio loop implementation on Windows + # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) + # We explicitly use a different loop implementation here to prevent that issue + if ( + not _IS_SYNC + and sys.platform == "win32" + and asyncio.get_event_loop_policy() == asyncio.WindowsProactorEventLoopPolicy + ): + loop = asyncio.WindowsSelectorEventLoopPolicy().new_event_loop() # type: ignore[attr-defined] + try: + loop.run_until_complete(self._run()) # type: ignore[func-returns-value] + finally: + loop.close() + else: + asyncio.run(self._run()) # type: ignore[func-returns-value] def open(self) -> None: """Start. Multiple calls have no effect. @@ -75,8 +89,6 @@ def open(self) -> None: # The default asyncio loop implementation on Windows # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) # We explicitly use a different loop implementation here to prevent that issue - if not _IS_SYNC and sys.platform == "win32": - asyncio.set_event_loop(asyncio.SelectorEventLoop()) with self._lock: if self._thread_will_exit: diff --git a/test/__init__.py b/test/__init__.py index b8bd185110..2f6c1eca2c 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -79,6 +79,12 @@ _IS_SYNC = True +# The default asyncio loop implementation on Windows +# has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) +# We explicitly use a different loop implementation here to prevent that issue +if not _IS_SYNC and sys.platform == "win32": + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # type: ignore[attr-defined] + class ClientContext: client: MongoClient diff --git a/test/asynchronous/__init__.py b/test/asynchronous/__init__.py index 6f0785c4ff..2d03a88cf4 100644 --- a/test/asynchronous/__init__.py +++ b/test/asynchronous/__init__.py @@ -79,6 +79,12 @@ _IS_SYNC = False +# The default asyncio loop implementation on Windows +# has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) +# We explicitly use a different loop implementation here to prevent that issue +if not _IS_SYNC and sys.platform == "win32": + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # type: ignore[attr-defined] + class AsyncClientContext: client: AsyncMongoClient From 044f5a0b90e440dae2b9581eab901eaada4031d8 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 25 Jul 2024 15:12:44 -0700 Subject: [PATCH 6/8] Cleanup --- pymongo/asynchronous/periodic_executor.py | 4 ---- pymongo/synchronous/periodic_executor.py | 4 ---- test/__init__.py | 6 +++++- test/asynchronous/__init__.py | 6 +++++- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pymongo/asynchronous/periodic_executor.py b/pymongo/asynchronous/periodic_executor.py index 22a19d0723..d08d362b7e 100644 --- a/pymongo/asynchronous/periodic_executor.py +++ b/pymongo/asynchronous/periodic_executor.py @@ -86,10 +86,6 @@ def open(self) -> None: Not safe to call from multiple threads at once. """ - # The default asyncio loop implementation on Windows - # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) - # We explicitly use a different loop implementation here to prevent that issue - with self._lock: if self._thread_will_exit: # If the background thread has read self._stopped as True diff --git a/pymongo/synchronous/periodic_executor.py b/pymongo/synchronous/periodic_executor.py index 33e54ef219..e8e5fb94b8 100644 --- a/pymongo/synchronous/periodic_executor.py +++ b/pymongo/synchronous/periodic_executor.py @@ -86,10 +86,6 @@ def open(self) -> None: Not safe to call from multiple threads at once. """ - # The default asyncio loop implementation on Windows - # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) - # We explicitly use a different loop implementation here to prevent that issue - with self._lock: if self._thread_will_exit: # If the background thread has read self._stopped as True diff --git a/test/__init__.py b/test/__init__.py index 2f6c1eca2c..b603ae0a5b 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -82,7 +82,11 @@ # The default asyncio loop implementation on Windows # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) # We explicitly use a different loop implementation here to prevent that issue -if not _IS_SYNC and sys.platform == "win32": +if ( + not _IS_SYNC + and sys.platform == "win32" + and asyncio.get_event_loop_policy() == asyncio.WindowsProactorEventLoopPolicy +): asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # type: ignore[attr-defined] diff --git a/test/asynchronous/__init__.py b/test/asynchronous/__init__.py index 2d03a88cf4..878ef81bae 100644 --- a/test/asynchronous/__init__.py +++ b/test/asynchronous/__init__.py @@ -82,7 +82,11 @@ # The default asyncio loop implementation on Windows # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) # We explicitly use a different loop implementation here to prevent that issue -if not _IS_SYNC and sys.platform == "win32": +if ( + not _IS_SYNC + and sys.platform == "win32" + and asyncio.get_event_loop_policy() == asyncio.WindowsProactorEventLoopPolicy +): asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # type: ignore[attr-defined] From fdca0aacbeedc68b3462ff7293ab66e537ae1fe4 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 25 Jul 2024 15:55:44 -0700 Subject: [PATCH 7/8] Address review --- pymongo/asynchronous/periodic_executor.py | 8 ++------ pymongo/synchronous/periodic_executor.py | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/pymongo/asynchronous/periodic_executor.py b/pymongo/asynchronous/periodic_executor.py index d08d362b7e..a162071d31 100644 --- a/pymongo/asynchronous/periodic_executor.py +++ b/pymongo/asynchronous/periodic_executor.py @@ -68,12 +68,8 @@ def _run_async(self) -> None: # The default asyncio loop implementation on Windows # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) # We explicitly use a different loop implementation here to prevent that issue - if ( - not _IS_SYNC - and sys.platform == "win32" - and asyncio.get_event_loop_policy() == asyncio.WindowsProactorEventLoopPolicy - ): - loop = asyncio.WindowsSelectorEventLoopPolicy().new_event_loop() # type: ignore[attr-defined] + if not _IS_SYNC and sys.platform == "win32": + loop = asyncio.SelectorEventLoop() try: loop.run_until_complete(self._run()) # type: ignore[func-returns-value] finally: diff --git a/pymongo/synchronous/periodic_executor.py b/pymongo/synchronous/periodic_executor.py index e8e5fb94b8..5280ec94e3 100644 --- a/pymongo/synchronous/periodic_executor.py +++ b/pymongo/synchronous/periodic_executor.py @@ -68,12 +68,8 @@ def _run_async(self) -> None: # The default asyncio loop implementation on Windows # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) # We explicitly use a different loop implementation here to prevent that issue - if ( - not _IS_SYNC - and sys.platform == "win32" - and asyncio.get_event_loop_policy() == asyncio.WindowsProactorEventLoopPolicy - ): - loop = asyncio.WindowsSelectorEventLoopPolicy().new_event_loop() # type: ignore[attr-defined] + if not _IS_SYNC and sys.platform == "win32": + loop = asyncio.SelectorEventLoop() try: loop.run_until_complete(self._run()) # type: ignore[func-returns-value] finally: From 14e81834c19bd6786c7094bcce175b432823d318 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Fri, 26 Jul 2024 07:07:32 -0700 Subject: [PATCH 8/8] Remove unneeded _IS_SYNC --- pymongo/asynchronous/periodic_executor.py | 2 +- pymongo/synchronous/periodic_executor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pymongo/asynchronous/periodic_executor.py b/pymongo/asynchronous/periodic_executor.py index a162071d31..f3d2fddba3 100644 --- a/pymongo/asynchronous/periodic_executor.py +++ b/pymongo/asynchronous/periodic_executor.py @@ -68,7 +68,7 @@ def _run_async(self) -> None: # The default asyncio loop implementation on Windows # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) # We explicitly use a different loop implementation here to prevent that issue - if not _IS_SYNC and sys.platform == "win32": + if sys.platform == "win32": loop = asyncio.SelectorEventLoop() try: loop.run_until_complete(self._run()) # type: ignore[func-returns-value] diff --git a/pymongo/synchronous/periodic_executor.py b/pymongo/synchronous/periodic_executor.py index 5280ec94e3..525268b14b 100644 --- a/pymongo/synchronous/periodic_executor.py +++ b/pymongo/synchronous/periodic_executor.py @@ -68,7 +68,7 @@ def _run_async(self) -> None: # The default asyncio loop implementation on Windows # has issues with sharing sockets across loops (https://github.com/python/cpython/issues/122240) # We explicitly use a different loop implementation here to prevent that issue - if not _IS_SYNC and sys.platform == "win32": + if sys.platform == "win32": loop = asyncio.SelectorEventLoop() try: loop.run_until_complete(self._run()) # type: ignore[func-returns-value]