Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

If TWISTED_REACTOR is None, reuse any pre-installed reactor #5528

Merged
merged 7 commits into from Jun 17, 2022
7 changes: 4 additions & 3 deletions docs/topics/settings.rst
Expand Up @@ -1638,9 +1638,10 @@ which raises :exc:`Exception`, becomes::


The default value of the :setting:`TWISTED_REACTOR` setting is ``None``, which
means that Scrapy will install the default reactor defined by Twisted for the
current platform. This is to maintain backward compatibility and avoid possible
problems caused by using a non-default reactor.
means that Scrapy will use the existing reactor if one is already installed, or
install the default reactor defined by Twisted for the current platform. This
is to maintain backward compatibility and avoid possible problems caused by
using a non-default reactor.

For additional information, see :doc:`core/howto/choosing-reactor`.

Expand Down
3 changes: 1 addition & 2 deletions scrapy/crawler.py
Expand Up @@ -78,8 +78,7 @@ def __init__(self, spidercls, settings=None, init_reactor: bool = False):
if reactor_class:
install_reactor(reactor_class, self.settings["ASYNCIO_EVENT_LOOP"])
else:
from twisted.internet import default
default.install()
from twisted.internet import reactor
log_reactor_info()
if reactor_class:
verify_installed_reactor(reactor_class)
Expand Down
17 changes: 17 additions & 0 deletions tests/CrawlerProcess/reactor_default.py
@@ -0,0 +1,17 @@
import scrapy
from scrapy.crawler import CrawlerProcess
from twisted.internet import reactor


class NoRequestsSpider(scrapy.Spider):
name = 'no_request'

def start_requests(self):
return []


process = CrawlerProcess(settings={})

process.crawl(NoRequestsSpider)
process.start()

20 changes: 20 additions & 0 deletions tests/CrawlerProcess/reactor_default_twisted_reactor_select.py
@@ -0,0 +1,20 @@
import scrapy
from scrapy.crawler import CrawlerProcess
from twisted.internet import reactor


class NoRequestsSpider(scrapy.Spider):
name = 'no_request'

def start_requests(self):
return []


process = CrawlerProcess(settings={
"TWISTED_REACTOR": "twisted.internet.selectreactor.SelectReactor",
})

process.crawl(NoRequestsSpider)
process.start()


19 changes: 19 additions & 0 deletions tests/CrawlerProcess/reactor_select.py
@@ -0,0 +1,19 @@
import scrapy
from scrapy.crawler import CrawlerProcess
from twisted.internet import selectreactor
selectreactor.install()


class NoRequestsSpider(scrapy.Spider):
name = 'no_request'

def start_requests(self):
return []


process = CrawlerProcess(settings={})

process.crawl(NoRequestsSpider)
process.start()


22 changes: 22 additions & 0 deletions tests/CrawlerProcess/reactor_select_twisted_reactor_select.py
@@ -0,0 +1,22 @@
import scrapy
from scrapy.crawler import CrawlerProcess
from twisted.internet import selectreactor
selectreactor.install()


class NoRequestsSpider(scrapy.Spider):
name = 'no_request'

def start_requests(self):
return []


process = CrawlerProcess(settings={
"TWISTED_REACTOR": "twisted.internet.selectreactor.SelectReactor",
})

process.crawl(NoRequestsSpider)
process.start()



39 changes: 33 additions & 6 deletions tests/test_crawler.py
Expand Up @@ -308,6 +308,33 @@ def test_multi(self):
self.assertNotIn("Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor", log)
self.assertNotIn("ReactorAlreadyInstalledError", log)

def test_reactor_default(self):
log = self.run_script('reactor_default.py')
self.assertIn('Spider closed (finished)', log)
self.assertNotIn("Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor", log)
self.assertNotIn("ReactorAlreadyInstalledError", log)

def test_reactor_default_twisted_reactor_select(self):
log = self.run_script('reactor_default_twisted_reactor_select.py')
self.assertNotIn('Spider closed (finished)', log)
self.assertIn(
(
"does not match the requested one "
"(twisted.internet.selectreactor.SelectReactor)"
),
log,
)

def test_reactor_select(self):
log = self.run_script('reactor_select.py')
self.assertIn('Spider closed (finished)', log)
self.assertNotIn("ReactorAlreadyInstalledError", log)

def test_reactor_select_twisted_reactor_select(self):
log = self.run_script('reactor_select_twisted_reactor_select.py')
self.assertIn('Spider closed (finished)', log)
self.assertNotIn("ReactorAlreadyInstalledError", log)

def test_asyncio_enabled_no_reactor(self):
log = self.run_script('asyncio_enabled_no_reactor.py')
self.assertIn('Spider closed (finished)', log)
Expand Down Expand Up @@ -340,33 +367,33 @@ def test_caching_hostname_resolver_finite_execution(self):
self.assertNotIn("TimeoutError", log)
self.assertNotIn("twisted.internet.error.DNSLookupError", log)

def test_reactor_select(self):
def test_twisted_reactor_select(self):
log = self.run_script("twisted_reactor_select.py")
self.assertIn("Spider closed (finished)", log)
self.assertIn("Using reactor: twisted.internet.selectreactor.SelectReactor", log)

@mark.skipif(platform.system() == 'Windows', reason="PollReactor is not supported on Windows")
def test_reactor_poll(self):
def test_twisted_reactor_poll(self):
log = self.run_script("twisted_reactor_poll.py")
self.assertIn("Spider closed (finished)", log)
self.assertIn("Using reactor: twisted.internet.pollreactor.PollReactor", log)

def test_reactor_asyncio(self):
def test_twisted_reactor_asyncio(self):
log = self.run_script("twisted_reactor_asyncio.py")
self.assertIn("Spider closed (finished)", log)
self.assertIn("Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor", log)

def test_reactor_asyncio_custom_settings(self):
def test_twisted_reactor_asyncio_custom_settings(self):
log = self.run_script("twisted_reactor_custom_settings.py")
self.assertIn("Spider closed (finished)", log)
self.assertIn("Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor", log)

def test_reactor_asyncio_custom_settings_same(self):
def test_twisted_reactor_asyncio_custom_settings_same(self):
log = self.run_script("twisted_reactor_custom_settings_same.py")
self.assertIn("Spider closed (finished)", log)
self.assertIn("Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor", log)

def test_reactor_asyncio_custom_settings_conflict(self):
def test_twisted_reactor_asyncio_custom_settings_conflict(self):
log = self.run_script("twisted_reactor_custom_settings_conflict.py")
self.assertIn("Using reactor: twisted.internet.selectreactor.SelectReactor", log)
self.assertIn("(twisted.internet.selectreactor.SelectReactor) does not match the requested one", log)
Expand Down