From fc566a7ff9913be5fce2b961a6599f861e1ef59a Mon Sep 17 00:00:00 2001 From: Andrey Rakhmatullin Date: Fri, 14 Mar 2025 23:46:17 +0400 Subject: [PATCH] Don't install the reactor in is_asyncio_reactor_installed(). (#6732) --- docs/topics/asyncio.rst | 2 ++ scrapy/utils/reactor.py | 13 ++++++++ .../asyncio_enabled_no_reactor.py | 8 +++++ .../CrawlerProcess/asyncio_enabled_reactor.py | 33 ++++++++++++++++++- tests/test_crawler.py | 2 ++ 5 files changed, 57 insertions(+), 1 deletion(-) diff --git a/docs/topics/asyncio.rst b/docs/topics/asyncio.rst index 35afdc11b..b61a6e4a8 100644 --- a/docs/topics/asyncio.rst +++ b/docs/topics/asyncio.rst @@ -116,6 +116,8 @@ example: f"of Scrapy for more information." ) +.. autofunction:: scrapy.utils.reactor.is_asyncio_reactor_installed + .. _asyncio-windows: diff --git a/scrapy/utils/reactor.py b/scrapy/utils/reactor.py index 679e38206..099c81f0e 100644 --- a/scrapy/utils/reactor.py +++ b/scrapy/utils/reactor.py @@ -175,7 +175,20 @@ def verify_installed_asyncio_event_loop(loop_path: str) -> None: ) +def is_reactor_installed() -> bool: + return "twisted.internet.reactor" in sys.modules + + def is_asyncio_reactor_installed() -> bool: + """Check whether the installed reactor is :class:`~twisted.internet.asyncioreactor.AsyncioSelectorReactor`. + + Raise a :exc:`RuntimeError` if no reactor is installed. + """ + if not is_reactor_installed(): + raise RuntimeError( + "is_asyncio_reactor_installed() called without an installed reactor." + ) + from twisted.internet import reactor return isinstance(reactor, asyncioreactor.AsyncioSelectorReactor) diff --git a/tests/CrawlerProcess/asyncio_enabled_no_reactor.py b/tests/CrawlerProcess/asyncio_enabled_no_reactor.py index 6df6d76fa..6f82cf589 100644 --- a/tests/CrawlerProcess/asyncio_enabled_no_reactor.py +++ b/tests/CrawlerProcess/asyncio_enabled_no_reactor.py @@ -1,5 +1,12 @@ import scrapy from scrapy.crawler import CrawlerProcess +from scrapy.utils.reactor import is_asyncio_reactor_installed + + +class ReactorCheckExtension: + def __init__(self): + if not is_asyncio_reactor_installed(): + raise RuntimeError("ReactorCheckExtension requires the asyncio reactor.") class NoRequestsSpider(scrapy.Spider): @@ -12,6 +19,7 @@ class NoRequestsSpider(scrapy.Spider): process = CrawlerProcess( settings={ "TWISTED_REACTOR": "twisted.internet.asyncioreactor.AsyncioSelectorReactor", + "EXTENSIONS": {ReactorCheckExtension: 0}, } ) process.crawl(NoRequestsSpider) diff --git a/tests/CrawlerProcess/asyncio_enabled_reactor.py b/tests/CrawlerProcess/asyncio_enabled_reactor.py index 0c380610d..a8bf1bc3c 100644 --- a/tests/CrawlerProcess/asyncio_enabled_reactor.py +++ b/tests/CrawlerProcess/asyncio_enabled_reactor.py @@ -1,9 +1,39 @@ import scrapy from scrapy.crawler import CrawlerProcess -from scrapy.utils.reactor import install_reactor +from scrapy.utils.reactor import ( + install_reactor, + is_asyncio_reactor_installed, + is_reactor_installed, +) + +if is_reactor_installed(): + raise RuntimeError( + "Reactor already installed before is_asyncio_reactor_installed()." + ) + +try: + is_asyncio_reactor_installed() +except RuntimeError: + pass +else: + raise RuntimeError("is_asyncio_reactor_installed() did not raise RuntimeError.") + +if is_reactor_installed(): + raise RuntimeError( + "Reactor already installed after is_asyncio_reactor_installed()." + ) install_reactor("twisted.internet.asyncioreactor.AsyncioSelectorReactor") +if not is_asyncio_reactor_installed(): + raise RuntimeError("Wrong reactor installed after install_reactor().") + + +class ReactorCheckExtension: + def __init__(self): + if not is_asyncio_reactor_installed(): + raise RuntimeError("ReactorCheckExtension requires the asyncio reactor.") + class NoRequestsSpider(scrapy.Spider): name = "no_request" @@ -15,6 +45,7 @@ class NoRequestsSpider(scrapy.Spider): process = CrawlerProcess( settings={ "TWISTED_REACTOR": "twisted.internet.asyncioreactor.AsyncioSelectorReactor", + "EXTENSIONS": {ReactorCheckExtension: 0}, } ) process.crawl(NoRequestsSpider) diff --git a/tests/test_crawler.py b/tests/test_crawler.py index 98352b66e..6c465f000 100644 --- a/tests/test_crawler.py +++ b/tests/test_crawler.py @@ -747,6 +747,7 @@ class TestCrawlerProcessSubprocess(ScriptRunnerMixin, unittest.TestCase): "Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor" in log ) + assert "RuntimeError" not in log def test_asyncio_enabled_reactor(self): log = self.run_script("asyncio_enabled_reactor.py") @@ -755,6 +756,7 @@ class TestCrawlerProcessSubprocess(ScriptRunnerMixin, unittest.TestCase): "Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor" in log ) + assert "RuntimeError" not in log @pytest.mark.skipif( parse_version(w3lib_version) >= parse_version("2.0.0"),