diff --git a/configurations/base.py b/configurations/base.py index 4e4fbf8..c9a678a 100644 --- a/configurations/base.py +++ b/configurations/base.py @@ -58,4 +58,6 @@ class Settings(six.with_metaclass(SettingsBase)): to the name of the class. """ - pass + @classmethod + def setup(cls): + pass diff --git a/configurations/importer.py b/configurations/importer.py index 72f3803..ada3411 100644 --- a/configurations/importer.py +++ b/configurations/importer.py @@ -132,6 +132,22 @@ class SettingsImporter(object): return None +def reraise(exc, prefix=None, suffix=None): + args = exc.args + if not args: + args = ('',) + if prefix is None: + prefix = '' + elif not prefix.endswith((':', ': ')): + prefix = prefix + ': ' + if suffix is None: + suffix = '' + elif not (suffix.startswith('(') and suffix.endswith(')')): + suffix = '(' + suffix + ')' + exc.args = ('%s %s %s' % (prefix, exc.args[0], suffix),) + args[1:] + raise + + class SettingsLoader(object): def __init__(self, name, location): @@ -143,31 +159,38 @@ class SettingsLoader(object): mod = sys.modules[fullname] # pragma: no cover else: mod = imp.load_module(fullname, *self.location) + cls_path = '%s.%s' % (mod.__name__, self.name) try: cls = getattr(mod, self.name) - except AttributeError: # pragma: no cover - raise ImproperlyConfigured("Couldn't find settings '%s' in " - "module '%s'" % - (self.name, mod.__package__)) + except AttributeError as err: # pragma: no cover + reraise(err, + "While trying to find the '%s' settings in module '%s'" % + (self.name, mod.__package__)) + try: + cls.setup() + except Exception as err: + reraise(err, "While calling '%s.setup()'" % cls_path) try: obj = cls() except Exception as err: - raise ImproperlyConfigured("Couldn't load settings '%s.%s': %s" % - (mod.__name__, self.name, err)) + reraise(err, + "While loading the '%s' settings" % cls_path) + try: attributes = uppercase_attributes(obj).items() except Exception as err: - raise ImproperlyConfigured("Couldn't get items of settings " - "'%s.%s': %s" % - (mod.__name__, self.name, err)) + reraise(err, + "While getting the items of the '%s' settings" % + cls_path) + for name, value in attributes: if callable(value) and not getattr(value, 'pristine', False): try: value = value() except Exception as err: - raise ImproperlyConfigured( - "Couldn't call '%s' in '%s.%s': %s" % - (value, mod.__name__, self.name, err)) + reraise(err, + "While calling '%s.%s'" % (cls_path, value)) setattr(mod, name, value) + setattr(mod, 'CONFIGURATION', '%s.%s' % (fullname, self.name)) return mod diff --git a/configurations/tests/settings/main.py b/configurations/tests/settings/main.py index ec4bda9..4e74070 100644 --- a/configurations/tests/settings/main.py +++ b/configurations/tests/settings/main.py @@ -30,6 +30,10 @@ class Test(Settings): TEST_RUNNER = 'discover_runner.DiscoverRunner' + def TEMPLATE_CONTEXT_PROCESSORS(self): + return Settings.TEMPLATE_CONTEXT_PROCESSORS + ( + 'configurations.tests.settings.base.test_callback',) + ATTRIBUTE_SETTING = True _PRIVATE_SETTING = 'ryan' @@ -49,6 +53,6 @@ class Test(Settings): def PRISTINE_FUNCTION_SETTING(): return 5 - def TEMPLATE_CONTEXT_PROCESSORS(self): - return Settings.TEMPLATE_CONTEXT_PROCESSORS + ( - 'configurations.tests.settings.base.test_callback',) + @classmethod + def setup(cls): + cls.SETUP_TEST_SETTING = 6 diff --git a/configurations/tests/test_main.py b/configurations/tests/test_main.py index ec1ad8a..751a79c 100644 --- a/configurations/tests/test_main.py +++ b/configurations/tests/test_main.py @@ -25,6 +25,7 @@ class MainTests(TestCase): global_settings.TEMPLATE_CONTEXT_PROCESSORS + ( 'configurations.tests.settings.base.test_callback', )) + self.assertEqual(main.SETUP_TEST_SETTING, 6) def test_global_arrival(self): from django.conf import settings @@ -34,6 +35,7 @@ class MainTests(TestCase): self.assertTrue(lambda: callable(settings.PRISTINE_LAMBDA_SETTING)) self.assertNotEqual(settings.PRISTINE_FUNCTION_SETTING, 5) self.assertTrue(lambda: callable(settings.PRISTINE_FUNCTION_SETTING)) + self.assertEqual(settings.SETUP_TEST_SETTING, 6) @patch.dict(os.environ, clear=True, DJANGO_CONFIGURATION='Test') def test_empty_module_var(self): diff --git a/docs/index.rst b/docs/index.rst index 865e5f7..af66dcf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -155,6 +155,36 @@ Lambdas work, too:: class Prod(Settings): ACCESS_FUNCTION = pristine(lamda user: user.is_staff) +Setup methods +^^^^^^^^^^^^^ + +.. versionadded:: 0.3 + +If there is something required to be set up after all the settings have been +loaded please override the ``setup`` class method like so (don't forget +to apply the Python ``@classmethod`` decorator:: + + from configurations import Settings + + class Prod(Settings): + # ... + + @classmethod + def setup(cls): + if something.completely.different(): + cls.DEBUG = True + +Or do something unrelated to your settings, like connecting to a database:: + + from configurations import Settings + + class Prod(Settings): + # ... + + @classmethod + def setup(cls): + import mango + mango.connect('enterprise') Alternatives ------------