Repository: scrapy/scrapy
Pull request #1156 (2015-04-14 18:29:05)

allow Crawler, CrawlerRunner and CrawlerProcess to accept dicts instead of Setting objects
I think that when you use Crawler, CrawlerRunner or CrawlerProcess Settings class is just an unnecessary import line. What about allowing dicts?


Patch:
diff --git a/docs/topics/practices.rst b/docs/topics/practices.rst
index 3ec7bc29b..2f848e6d9 100644
--- a/docs/topics/practices.rst
+++ b/docs/topics/practices.rst
@@ -52,16 +52,16 @@ the spider class as first argument in the :meth:`CrawlerRunner.crawl
 ::
 
     from twisted.internet import reactor
-    from scrapy.spider import Spider
+    import scrapy
     from scrapy.crawler import CrawlerRunner
-    from scrapy.settings import Settings
 
-    class MySpider(Spider):
+    class MySpider(scrapy.Spider):
         # Your spider definition
         ...
 
-    settings = Settings({'USER_AGENT': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)'})
-    runner = CrawlerRunner(settings)
+    runner = CrawlerRunner({
+        'USER_AGENT': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)'
+    })
 
     d = runner.crawl(MySpider)
     d.addBoth(lambda _: reactor.stop())
diff --git a/scrapy/crawler.py b/scrapy/crawler.py
index 3e4d0dc7f..1b5029219 100644
--- a/scrapy/crawler.py
+++ b/scrapy/crawler.py
@@ -9,6 +9,7 @@ from scrapy.core.engine import ExecutionEngine
 from scrapy.resolver import CachingThreadedResolver
 from scrapy.interfaces import ISpiderManager
 from scrapy.extension import ExtensionManager
+from scrapy.settings import Settings
 from scrapy.signalmanager import SignalManager
 from scrapy.exceptions import ScrapyDeprecationWarning
 from scrapy.utils.ossignal import install_shutdown_handlers, signal_names
@@ -19,6 +20,9 @@ from scrapy import log, signals
 class Crawler(object):
 
     def __init__(self, spidercls, settings):
+        if isinstance(settings, dict):
+            settings = Settings(settings)
+
         self.spidercls = spidercls
         self.settings = settings.copy()
 
@@ -78,6 +82,8 @@ class Crawler(object):
 class CrawlerRunner(object):
 
     def __init__(self, settings):
+        if isinstance(settings, dict):
+            settings = Settings(settings)
         self.settings = settings
         smcls = load_object(settings['SPIDER_MANAGER_CLASS'])
         verifyClass(ISpiderManager, smcls)


Test patch:
diff --git a/tests/test_crawler.py b/tests/test_crawler.py
index 2cf860593..20467833f 100644
--- a/tests/test_crawler.py
+++ b/tests/test_crawler.py
@@ -1,11 +1,10 @@
 import warnings
 import unittest
 
-from twisted.internet import defer
 from zope.interface.verify import DoesNotImplement
 
 from scrapy.crawler import Crawler, CrawlerRunner
-from scrapy.settings import Settings
+from scrapy.settings import Settings, default_settings
 from scrapy.utils.spider import DefaultSpider
 from scrapy.utils.misc import load_object
 
@@ -44,6 +43,16 @@ class CrawlerTestCase(unittest.TestCase):
         self.assertFalse(settings.frozen)
         self.assertTrue(crawler.settings.frozen)
 
+    def test_crawler_accepts_dict(self):
+        crawler = Crawler(DefaultSpider, {'foo': 'bar'})
+        self.assertEqual(crawler.settings['foo'], 'bar')
+        self.assertEqual(
+            crawler.settings['RETRY_ENABLED'],
+            default_settings.RETRY_ENABLED
+        )
+        self.assertIsInstance(crawler.settings, Settings)
+
+
 
 def SpiderManagerWithWrongInterface(object):
 
@@ -59,3 +68,13 @@ class CrawlerRunnerTestCase(unittest.TestCase):
         })
         with self.assertRaises(DoesNotImplement):
             CrawlerRunner(settings)
+
+    def test_crawler_runner_accepts_dict(self):
+        runner = CrawlerRunner({'foo': 'bar'})
+        self.assertEqual(runner.settings['foo'], 'bar')
+        self.assertEqual(
+            runner.settings['RETRY_ENABLED'],
+            default_settings.RETRY_ENABLED
+        )
+        self.assertIsInstance(runner.settings, Settings)
+