diff --git a/docs/topics/api.rst b/docs/topics/api.rst index f0a12dd03..341340c2a 100644 --- a/docs/topics/api.rst +++ b/docs/topics/api.rst @@ -190,6 +190,21 @@ Settings API :attr:`~scrapy.settings.SETTINGS_PRIORITIES` or an integer :type priority: string or int + .. method:: setmodule(module, priority='project') + + Store settings from a module with a given priority. + + This is a helper function that calls + :meth:`~scrapy.settings.Settings.set` for every globally declared + uppercase variable of ``module`` with the provided ``priority``. + + :param module: the module or the path of the module + :type module: module object or string + + :param priority: the priority of the settings. Should be a key of + :attr:`~scrapy.settings.SETTINGS_PRIORITIES` or an integer + :type priority: string or int + .. method:: get(name, default=None) Get a setting value without affecting its original type. diff --git a/scrapy/settings/__init__.py b/scrapy/settings/__init__.py index 4fed21899..60af208d6 100644 --- a/scrapy/settings/__init__.py +++ b/scrapy/settings/__init__.py @@ -1,5 +1,6 @@ import six import json +from importlib import import_module from scrapy.utils.deprecate import create_deprecated_class @@ -43,8 +44,7 @@ class Settings(object): def __init__(self, values=None, priority='project'): self.attributes = {} - for name, defvalue in iter_default_settings(): - self.set(name, defvalue, 'default') + self.setmodule(default_settings, priority='default') if values is not None: self.setdict(values, priority) @@ -101,6 +101,13 @@ class Settings(object): for name, value in six.iteritems(values): self.set(name, value, priority) + def setmodule(self, module, priority='project'): + if isinstance(module, six.string_types): + module = import_module(module) + for key in dir(module): + if key.isupper(): + self.set(key, getattr(module, key), priority) + class CrawlerSettings(Settings): diff --git a/scrapy/tests/test_settings/__init__.py b/scrapy/tests/test_settings/__init__.py index 37cedfc33..34521a3eb 100644 --- a/scrapy/tests/test_settings/__init__.py +++ b/scrapy/tests/test_settings/__init__.py @@ -101,6 +101,41 @@ class SettingsTest(unittest.TestCase): mock.call('TEST_2', 'value2', 10)] mock_set.assert_has_calls(calls, any_order=True) + def test_setmodule_only_load_uppercase_vars(self): + class ModuleMock(): + UPPERCASE_VAR = 'value' + MIXEDcase_VAR = 'othervalue' + lowercase_var = 'anothervalue' + + self.settings.attributes = {} + self.settings.setmodule(ModuleMock(), 10) + self.assertIn('UPPERCASE_VAR', self.settings.attributes) + self.assertNotIn('MIXEDcase_VAR', self.settings.attributes) + self.assertNotIn('lowercase_var', self.settings.attributes) + self.assertEqual(len(self.settings.attributes), 1) + + def test_setmodule_alias(self): + with mock.patch.object(self.settings, 'set') as mock_set: + self.settings.setmodule(default_settings, 10) + mock_set.assert_called_with('TEST_DEFAULT', 'defvalue', 10) + + def test_setmodule_by_path(self): + self.settings.attributes = {} + self.settings.setmodule(default_settings, 10) + ctrl_attributes = self.settings.attributes.copy() + + self.settings.attributes = {} + self.settings.setmodule( + 'scrapy.tests.test_settings.default_settings', 10) + + self.assertItemsEqual(six.iterkeys(self.settings.attributes), + six.iterkeys(ctrl_attributes)) + + for attr, ctrl_attr in zip(six.itervalues(self.settings.attributes), + six.itervalues(ctrl_attributes)): + self.assertEqual(attr.value, ctrl_attr.value) + self.assertEqual(attr.priority, ctrl_attr.priority) + def test_get(self): test_configuration = { 'TEST_ENABLED1': '1',