diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 3396635d8..20594926b 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -18,6 +18,7 @@ REST framework settings, checking for user settings first, then falling back to the defaults. """ from __future__ import unicode_literals +from collections import Iterable import warnings from django.conf import settings from django.utils import importlib, six @@ -190,13 +191,21 @@ class APISettings(object): # Fall back to defaults val = self.defaults[attr] else: - expected_class = self.defaults[attr].__class__ - if val.__class__ != expected_class: + # Verify that the user hasn't accidentally given a string instead + # of an iterable, or vice-versa + default = self.defaults[attr] + if issubclass(val.__class__, Iterable) \ + and (not issubclass(default.__class__, Iterable) + or isinstance(val, basestring)): warnings.warn( - "The `{val}` setting has the class {val.__class__}, " - "but the expected class was {expected_class}".format( - **locals() - ), + "The `{attr}` setting must be iterable".format(**locals()), + RESTFrameworkSettingHasUnexpectedClassWarning, + stacklevel=3 + ) + elif isinstance(default, basestring) and not \ + isinstance(val, basestring): + warnings.warn( + "The `{attr}` setting must be a string".format(**locals()), RESTFrameworkSettingHasUnexpectedClassWarning, stacklevel=3 ) diff --git a/tests/test_settings.py b/tests/test_settings.py index f5bde4b06..337759726 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -19,9 +19,10 @@ class TestSettings(TestCase): with self.assertRaises(ImportError): settings.DEFAULT_RENDERER_CLASSES - def test_bad_setting_class_raises_warning(self): + def test_bad_iterable_setting_class_raises_warning(self): """ - Make sure warnings are emitted when settings have an unexpected class. + Make sure warnings are emitted when settings which should be iterable + are not. """ settings = APISettings({ 'DEFAULT_RENDERER_CLASSES': 'rest_framework.renderers.JSONRenderer' @@ -35,3 +36,22 @@ class TestSettings(TestCase): assert issubclass( w[-1].category, RESTFrameworkSettingHasUnexpectedClassWarning ) + + def test_bad_string_setting_class_raises_warning(self): + """ + Make sure warnings are emitted when settings which should be strings are + not. + """ + settings = APISettings({ + 'DEFAULT_METADATA_CLASS': [] + }) + + with warnings.catch_warnings(record=True) as w: + # Trigger a warning. + settings.DEFAULT_METADATA_CLASS + + # Verify that a warning is thrown + assert len(w) == 1 + assert issubclass( + w[-1].category, RESTFrameworkSettingHasUnexpectedClassWarning + )