Commit 32be1180 authored by Malcolm Tredinnick's avatar Malcolm Tredinnick
Browse files

Removed some import-time dependencies on Django's settings.

Now you can import the file storage stuff and still call settings.configure()
afterwards. There is still one import-time usage of settings in
django.contrib.comments, but that's unavoidable.

Backport of r9945 and r9946 from trunk (this is needed in order to fix #8193 in
a clean fashion, which is why it's being backported).

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@9947 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 95763538
Loading
Loading
Loading
Loading
+7 −28
Original line number Diff line number Diff line
@@ -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):
+22 −8
Original line number Diff line number Diff line
@@ -4,11 +4,12 @@ import urlparse

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
from django.core.files import locks, File
from django.core.files.move import file_move_safe
from django.utils.encoding import force_unicode
from django.utils.functional import LazyObject
from django.utils.text import get_valid_filename
from django.utils._os import safe_join
from django.core.files import locks, File
from django.core.files.move import file_move_safe

__all__ = ('Storage', 'FileSystemStorage', 'DefaultStorage', 'default_storage')

@@ -116,12 +117,20 @@ class Storage(object):
        """
        raise NotImplementedError()

    # Needed by django.utils.functional.LazyObject (via DefaultStorage).
    def get_all_members(self):
        return self.__members__

class FileSystemStorage(Storage):
    """
    Standard filesystem storage
    """

    def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL):
    def __init__(self, location=None, base_url=None):
        if location is None:
            location = settings.MEDIA_ROOT
        if base_url is None:
            base_url = settings.MEDIA_URL
        self.location = os.path.abspath(location)
        self.base_url = base_url

@@ -212,7 +221,9 @@ class FileSystemStorage(Storage):
            raise ValueError("This file is not accessible via a URL.")
        return urlparse.urljoin(self.base_url, name).replace('\\', '/')

def get_storage_class(import_path):
def get_storage_class(import_path=None):
    if import_path is None:
        import_path = settings.DEFAULT_FILE_STORAGE
    try:
        dot = import_path.rindex('.')
    except ValueError:
@@ -227,5 +238,8 @@ def get_storage_class(import_path):
    except AttributeError:
        raise ImproperlyConfigured('Storage module "%s" does not define a "%s" class.' % (module, classname))

DefaultStorage = get_storage_class(settings.DEFAULT_FILE_STORAGE)
class DefaultStorage(LazyObject):
    def _setup(self):
        self._wrapped = get_storage_class()()

default_storage = DefaultStorage()
+36 −0
Original line number Diff line number Diff line
@@ -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