diff --git a/scrapy/crawler.py b/scrapy/crawler.py index 974754964..32a1d0988 100644 --- a/scrapy/crawler.py +++ b/scrapy/crawler.py @@ -71,6 +71,7 @@ class Crawler: self.spidercls: Type[Spider] = spidercls self.settings: Settings = settings.copy() + self.spidercls.update_settings(self.settings) self.addons: AddonManager = AddonManager(self) self.addons.load_settings(self.settings) @@ -104,10 +105,10 @@ class Crawler: crawler=self, ) - self._init_reactor = init_reactor - + self._init_reactor: bool = init_reactor self.crawling: bool = False self._started: bool = False + self.extensions: Optional[ExtensionManager] = None self.spider: Optional[Spider] = None self.engine: Optional[ExecutionEngine] = None @@ -125,7 +126,6 @@ class Crawler: try: self.spider = self._create_spider(*args, **kwargs) - self.spider.update_settings(self.settings) reactor_class: str = self.settings["TWISTED_REACTOR"] event_loop: str = self.settings["ASYNCIO_EVENT_LOOP"] @@ -142,7 +142,7 @@ class Crawler: if is_asyncio_reactor_installed() and event_loop: verify_installed_asyncio_event_loop(event_loop) - self.extensions: ExtensionManager = ExtensionManager.from_crawler(self) + self.extensions = ExtensionManager.from_crawler(self) self.settings.freeze() self.engine = self._create_engine() diff --git a/scrapy/spiders/__init__.py b/scrapy/spiders/__init__.py index 590158d1d..e16d71727 100644 --- a/scrapy/spiders/__init__.py +++ b/scrapy/spiders/__init__.py @@ -83,8 +83,9 @@ class Spider(object_ref): f"{self.__class__.__name__}.parse callback is not defined" ) - def update_settings(self, settings: BaseSettings) -> None: - settings.setdict(self.custom_settings or {}, priority="spider") + @classmethod + def update_settings(cls, settings: BaseSettings) -> None: + settings.setdict(cls.custom_settings or {}, priority="spider") @classmethod def handles_request(cls, request: Request) -> bool: diff --git a/tests/test_spider.py b/tests/test_spider.py index ba61d2402..a88d9b505 100644 --- a/tests/test_spider.py +++ b/tests/test_spider.py @@ -2,13 +2,16 @@ import gzip import inspect import warnings from io import BytesIO +from typing import Any from unittest import mock from testfixtures import LogCapture +from twisted.internet.defer import inlineCallbacks from twisted.trial import unittest from w3lib.url import safe_url_string from scrapy import signals +from scrapy.crawler import Crawler from scrapy.http import HtmlResponse, Request, Response, TextResponse, XmlResponse from scrapy.linkextractors import LinkExtractor from scrapy.settings import Settings @@ -91,16 +94,32 @@ class SpiderTest(unittest.TestCase): self.spider_class.custom_settings = spider_settings settings = Settings(project_settings, priority="project") - spider = self.spider_class("example.com") - spider.update_settings(settings) + self.spider_class.update_settings(settings) self.assertEqual(settings.get("TEST1"), "spider") self.assertEqual(settings.get("TEST2"), "spider") self.assertEqual(settings.get("TEST3"), "project") - spider_instance_settings = {"TEST1": "spider_instance"} - spider.custom_settings = spider_instance_settings - spider.update_settings(settings) - self.assertEqual(settings.get("TEST1"), "spider_instance") + @inlineCallbacks + def test_settings_in_from_crawler(self): + spider_settings = {"TEST1": "spider", "TEST2": "spider"} + project_settings = {"TEST1": "project", "TEST3": "project"} + + class TestSpider(self.spider_class): + name = "test" + custom_settings = spider_settings + + @classmethod + def from_crawler(cls, crawler: Crawler, *args: Any, **kwargs: Any): + spider = super().from_crawler(crawler, *args, **kwargs) + spider.settings.set("TEST1", "spider_instance", priority="spider") + return spider + + crawler = get_crawler(TestSpider, settings_dict=project_settings) + self.assertEqual(crawler.settings.get("TEST1"), "spider") + self.assertEqual(crawler.settings.get("TEST2"), "spider") + self.assertEqual(crawler.settings.get("TEST3"), "project") + yield crawler.crawl() + self.assertEqual(crawler.settings.get("TEST1"), "spider_instance") def test_logger(self): spider = self.spider_class("example.com")