Repository: scrapy/scrapy
Pull request #2519 (2017-01-30 15:34:35)

[MRG+1] Support 'True' and 'False' strings as boolean settings values
Revisiting https://github.com/scrapy/scrapy/pull/743

Fixes https://github.com/scrapy/scrapy/issues/2517


Patch:
diff --git a/scrapy/settings/__init__.py b/scrapy/settings/__init__.py
index 7b7808959..28446a372 100644
--- a/scrapy/settings/__init__.py
+++ b/scrapy/settings/__init__.py
@@ -114,8 +114,8 @@ class BaseSettings(MutableMapping):
         """
         Get a setting value as a boolean.
 
-        ``1``, ``'1'``, and ``True`` return ``True``, while ``0``, ``'0'``,
-        ``False`` and ``None`` return ``False``.
+        ``1``, ``'1'``, `True`` and ``'True'`` return ``True``,
+        while ``0``, ``'0'``, ``False``, ``'False'`` and ``None`` return ``False``.
 
         For example, settings populated through environment variables set to
         ``'0'`` will return ``False`` when using this method.
@@ -126,7 +126,17 @@ class BaseSettings(MutableMapping):
         :param default: the value to return if no setting is found
         :type default: any
         """
-        return bool(int(self.get(name, default)))
+        got = self.get(name, default)
+        try:
+            return bool(int(got))
+        except ValueError:
+            if got in ("True", "true"):
+                return True
+            if got in ("False", "false"):
+                return False
+            raise ValueError("Supported values for boolean settings "
+                             "are 0/1, True/False, '0'/'1', "
+                             "'True'/'False' and 'true'/'false'")
 
     def getint(self, name, default=0):
         """


Test patch:
diff --git a/tests/test_settings/__init__.py b/tests/test_settings/__init__.py
index 4acf22cba..863684075 100644
--- a/tests/test_settings/__init__.py
+++ b/tests/test_settings/__init__.py
@@ -211,9 +211,15 @@ class BaseSettingsTest(unittest.TestCase):
             'TEST_ENABLED1': '1',
             'TEST_ENABLED2': True,
             'TEST_ENABLED3': 1,
+            'TEST_ENABLED4': 'True',
+            'TEST_ENABLED5': 'true',
+            'TEST_ENABLED_WRONG': 'on',
             'TEST_DISABLED1': '0',
             'TEST_DISABLED2': False,
             'TEST_DISABLED3': 0,
+            'TEST_DISABLED4': 'False',
+            'TEST_DISABLED5': 'false',
+            'TEST_DISABLED_WRONG': 'off',
             'TEST_INT1': 123,
             'TEST_INT2': '123',
             'TEST_FLOAT1': 123.45,
@@ -231,11 +237,15 @@ class BaseSettingsTest(unittest.TestCase):
         self.assertTrue(settings.getbool('TEST_ENABLED1'))
         self.assertTrue(settings.getbool('TEST_ENABLED2'))
         self.assertTrue(settings.getbool('TEST_ENABLED3'))
+        self.assertTrue(settings.getbool('TEST_ENABLED4'))
+        self.assertTrue(settings.getbool('TEST_ENABLED5'))
         self.assertFalse(settings.getbool('TEST_ENABLEDx'))
         self.assertTrue(settings.getbool('TEST_ENABLEDx', True))
         self.assertFalse(settings.getbool('TEST_DISABLED1'))
         self.assertFalse(settings.getbool('TEST_DISABLED2'))
         self.assertFalse(settings.getbool('TEST_DISABLED3'))
+        self.assertFalse(settings.getbool('TEST_DISABLED4'))
+        self.assertFalse(settings.getbool('TEST_DISABLED5'))
         self.assertEqual(settings.getint('TEST_INT1'), 123)
         self.assertEqual(settings.getint('TEST_INT2'), 123)
         self.assertEqual(settings.getint('TEST_INTx'), 0)
@@ -258,6 +268,8 @@ class BaseSettingsTest(unittest.TestCase):
         self.assertEqual(settings.getdict('TEST_DICT3'), {})
         self.assertEqual(settings.getdict('TEST_DICT3', {'key1': 5}), {'key1': 5})
         self.assertRaises(ValueError, settings.getdict, 'TEST_LIST1')
+        self.assertRaises(ValueError, settings.getbool, 'TEST_ENABLED_WRONG')
+        self.assertRaises(ValueError, settings.getbool, 'TEST_DISABLED_WRONG')
 
     def test_getpriority(self):
         settings = BaseSettings({'key': 'value'}, priority=99)