From cf3071242a289e20526ade81021b1f895fde706d Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Mon, 2 Mar 2009 04:48:22 +0000 Subject: [PATCH] Factor out some common pieces of django.conf.LazySettings. This is in preparation for some reuse elsewhere in the core code. git-svn-id: http://code.djangoproject.com/svn/django/trunk@9945 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/conf/__init__.py | 35 ++++-------------- django/utils/functional.py | 36 +++++++++++++++++++ .../comment_tests/tests/app_api_tests.py | 2 +- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/django/conf/__init__.py b/django/conf/__init__.py index fc69bed7c6..81787d63d5 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -8,40 +8,19 @@ a list of all possible variables. import os import time # Needed for Windows + from django.conf import global_settings +from django.utils.functional import LazyObject ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" -class LazySettings(object): +class LazySettings(LazyObject): """ A lazy proxy for either global Django settings or a custom settings object. The user can manually configure settings prior to using them. Otherwise, Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE. """ - def __init__(self): - # _target must be either None or something that supports attribute - # access (getattr, hasattr, etc). - self._target = None - - def __getattr__(self, name): - if self._target is None: - self._import_settings() - if name == '__members__': - # Used to implement dir(obj), for example. - return self._target.get_all_members() - return getattr(self._target, name) - - def __setattr__(self, name, value): - if name == '_target': - # Assign directly to self.__dict__, because otherwise we'd call - # __setattr__(), which would be an infinite loop. - self.__dict__['_target'] = value - else: - if self._target is None: - self._import_settings() - setattr(self._target, name, value) - - def _import_settings(self): + def _setup(self): """ Load the settings module pointed to by the environment variable. This is used the first time we need any settings at all, if the user has not @@ -56,7 +35,7 @@ class LazySettings(object): # problems with Python's interactive help. raise ImportError("Settings cannot be imported, because environment variable %s is undefined." % ENVIRONMENT_VARIABLE) - self._target = Settings(settings_module) + self._wrapped = Settings(settings_module) def configure(self, default_settings=global_settings, **options): """ @@ -69,13 +48,13 @@ class LazySettings(object): holder = UserSettingsHolder(default_settings) for name, value in options.items(): setattr(holder, name, value) - self._target = holder + self._wrapped = holder def configured(self): """ Returns True if the settings have already been configured. """ - return bool(self._target) + return bool(self._wrapped) configured = property(configured) class Settings(object): diff --git a/django/utils/functional.py b/django/utils/functional.py index 9a12eda5a1..23614d1712 100644 --- a/django/utils/functional.py +++ b/django/utils/functional.py @@ -251,3 +251,39 @@ def allow_lazy(func, *resultclasses): return func(*args, **kwargs) return lazy(func, *resultclasses)(*args, **kwargs) return wraps(func)(wrapper) + +class LazyObject(object): + """ + A wrapper for another class that can be used to delay instantiation of the + wrapped class. + + This is useful, for example, if the wrapped class needs to use Django + settings at creation time: we want to permit it to be imported without + accessing settings. + """ + def __init__(self): + self._wrapped = None + + def __getattr__(self, name): + if self._wrapped is None: + self._setup() + if name == "__members__": + # Used to implement dir(obj) + return self._wrapped.get_all_members() + return getattr(self._wrapped, name) + + def __setattr__(self, name, value): + if name == "_wrapped": + # Assign to __dict__ to avoid infinite __setattr__ loops. + self.__dict__["_wrapped"] = value + else: + if self._wrapped is None: + self._setup() + setattr(self._wrapped, name, value) + + def _setup(self): + """ + Must be implemented by subclasses to initialise the wrapped object. + """ + raise NotImplementedError + diff --git a/tests/regressiontests/comment_tests/tests/app_api_tests.py b/tests/regressiontests/comment_tests/tests/app_api_tests.py index 6a9eb1c637..c4d9ebfef3 100644 --- a/tests/regressiontests/comment_tests/tests/app_api_tests.py +++ b/tests/regressiontests/comment_tests/tests/app_api_tests.py @@ -41,7 +41,7 @@ class CustomCommentTest(CommentTestCase): del settings.INSTALLED_APPS[-1] settings.COMMENTS_APP = self.old_comments_app if settings.COMMENTS_APP is None: - delattr(settings._target, 'COMMENTS_APP') + delattr(settings._wrapped, 'COMMENTS_APP') def testGetCommentApp(self): from regressiontests.comment_tests import custom_comments