Ticket #14264: 14264.2.diff

File 14264.2.diff, 13.7 KB (added by Gary Wilson, 13 years ago)
  • django/conf/__init__.py

    === modified file 'django/conf/__init__.py'
     
    99import os
    1010import re
    1111import time     # Needed for Windows
     12import types
    1213import warnings
    1314
    1415from django.conf import global_settings
     
    4950        """
    5051        if self._wrapped is not empty:
    5152            raise RuntimeError('Settings already configured.')
    52         holder = UserSettingsHolder(default_settings)
    53         for name, value in options.items():
    54             setattr(holder, name, value)
     53        holder = UserSettingsHolder(default_settings=default_settings,
     54                                    extra_settings=options)
    5555        self._wrapped = holder
    5656
    5757    @property
     
    6262        return self._wrapped is not empty
    6363
    6464
    65 class BaseSettings(object):
     65class Settings(object):
    6666    """
    6767    Common logic for settings whether set by a module or by the user.
    6868    """
     69
     70    # Settings that should be converted into tuples if they're mistakenly
     71    # entered as strings.
     72    tuple_settings = ("INSTALLED_APPS", "TEMPLATE_DIRS")
     73    # Flag to specify whether or not changes should be made to environment.
     74    environ_changes_allowed = True
     75
     76    def __init__(self, settings_module=None, default_settings=global_settings,
     77                 extra_settings=None):
     78        if settings_module:
     79            # store the settings module in case someone later cares
     80            self.SETTINGS_MODULE = settings_module
     81        self.initialize([default_settings, settings_module, extra_settings])
     82        self.post_setup()
     83
    6984    def __setattr__(self, name, value):
    7085        if name in ("MEDIA_URL", "STATIC_URL") and value and not value.endswith('/'):
    7186            warnings.warn("If set, %s must end with a slash" % name,
     
    7590                          "use STATIC_URL instead.", DeprecationWarning)
    7691        object.__setattr__(self, name, value)
    7792
    78 
    79 class Settings(BaseSettings):
    80     def __init__(self, settings_module):
    81         # update this dict from global settings (but only for ALL_CAPS settings)
    82         for setting in dir(global_settings):
    83             if setting == setting.upper():
    84                 setattr(self, setting, getattr(global_settings, setting))
    85 
    86         # store the settings module in case someone later cares
    87         self.SETTINGS_MODULE = settings_module
    88 
    89         try:
    90             mod = importlib.import_module(self.SETTINGS_MODULE)
    91         except ImportError, e:
    92             raise ImportError("Could not import settings '%s' (Is it on sys.path?): %s" % (self.SETTINGS_MODULE, e))
    93 
    94         # Settings that should be converted into tuples if they're mistakenly entered
    95         # as strings.
    96         tuple_settings = ("INSTALLED_APPS", "TEMPLATE_DIRS")
    97 
    98         for setting in dir(mod):
    99             if setting == setting.upper():
    100                 setting_value = getattr(mod, setting)
    101                 if setting in tuple_settings and type(setting_value) == str:
    102                     setting_value = (setting_value,) # In case the user forgot the comma.
    103                 setattr(self, setting, setting_value)
    104 
    105         # Expand entries in INSTALLED_APPS like "django.contrib.*" to a list
    106         # of all those apps.
     93    def initialize(self, settings_objs):
     94        """
     95        Initializes this settings object based on attributes/keys from the
     96        objects in the settings_objs list.
     97        """
     98        for obj in settings_objs:
     99            if not obj:
     100                continue
     101            for setting, value in dict_from_object(obj).iteritems():
     102                    setattr(self, setting, value)
     103
     104    def post_setup(self):
     105        self._correct_tuple_settings()
     106        # Must remain after call to correct tuple settings, because
     107        # INSTALLED_APPS is a tuple setting.
     108        self._expand_installed_apps()
     109        self._set_tz()
     110        self._setup_logging()
     111
     112    def _expand_installed_apps(self):
     113        """
     114        Expand glob entries in INSTALLED_APPS, e.g. "django.contrib.*", to a
     115        list of all those apps.
     116        """
    107117        new_installed_apps = []
    108118        for app in self.INSTALLED_APPS:
    109119            if app.endswith('.*'):
     
    119129                new_installed_apps.append(app)
    120130        self.INSTALLED_APPS = new_installed_apps
    121131
     132    def _correct_tuple_settings(self):
     133        """
     134        For settings that are meant to be tuples, auto correct if user forgot
     135        trailing comma on a single value.
     136        """
     137        for setting in self.tuple_settings:
     138            value = getattr(self, setting, None)
     139            if isinstance(value, basestring):
     140                setattr(self, setting, (value,))
     141
     142    def _set_tz(self):
     143        if not self.environ_changes_allowed:
     144            return
    122145        if hasattr(time, 'tzset') and self.TIME_ZONE:
    123146            # When we can, attempt to validate the timezone. If we can't find
    124147            # this file, no check happens and it's harmless.
     
    131154            os.environ['TZ'] = self.TIME_ZONE
    132155            time.tzset()
    133156
     157    def _setup_logging(self):
    134158        # Settings are configured, so we can set up the logger if required
    135159        if self.LOGGING_CONFIG:
    136160            # First find the logging configuration function ...
     
    145169            logging_config_func(self.LOGGING)
    146170
    147171
    148 class UserSettingsHolder(BaseSettings):
     172class UserSettingsHolder(Settings):
    149173    """
    150174    Holder for user configured settings.
    151175    """
    152176    # SETTINGS_MODULE doesn't make much sense in the manually configured
    153177    # (standalone) case.
    154178    SETTINGS_MODULE = None
    155 
    156     def __init__(self, default_settings):
    157         """
    158         Requests for configuration variables not in this class are satisfied
    159         from the module specified in default_settings (if possible).
    160         """
    161         self.default_settings = default_settings
    162 
    163     def __getattr__(self, name):
    164         return getattr(self.default_settings, name)
    165 
    166     def __dir__(self):
    167         return self.__dict__.keys() + dir(self.default_settings)
    168 
    169     # For Python < 2.6:
    170     __members__ = property(lambda self: self.__dir__())
     179    # Don't make any modifications to the process environment variables.
     180    environ_changes_allowed = False
     181
    171182
    172183settings = LazySettings()
    173184
    174185
     186def import_module(name):
     187    try:
     188        mod = importlib.import_module(name)
     189    except ImportError, e:
     190        raise ImportError(
     191            "Could not import settings '%s' (Is it on sys.path?): %s"
     192            % (name, e))
     193    return mod
     194
     195
     196def dict_from_object(obj):
     197    """
     198    Return a dictionary of obj's attributes, where obj can be one of:
     199
     200    * A dictionary
     201    * A string containing the name of a settings module
     202    * A Settings instance
     203    * A module
     204
     205    Only attributes/keys that are ALL CAPS are returned.
     206    """
     207    if isinstance(obj, basestring):
     208        obj = import_module(obj)
     209    if not isinstance(obj, dict):
     210        obj = dict([(attr, getattr(obj, attr)) for attr in dir(obj)])
     211
     212    # Only keep attributes that are (ALL CAPS).
     213    keep = lambda k: k == k.upper()
     214    return dict([(k, v) for k, v in obj.iteritems() if keep(k)])
     215
    175216
    176217def compat_patch_logging_config(logging_config):
    177218    """
  • django/core/management/commands/diffsettings.py

    === modified file 'django/core/management/commands/diffsettings.py'
     
    11from django.core.management.base import NoArgsCommand
    22
    3 def module_to_dict(module, omittable=lambda k: k.startswith('_')):
    4     "Converts a module namespace to a Python dictionary. Used by get_settings_diff."
    5     return dict([(k, repr(v)) for k, v in module.__dict__.items() if not omittable(k)])
    63
    74class Command(NoArgsCommand):
    85    help = """Displays differences between the current settings.py and Django's
     
    1310
    1411    def handle_noargs(self, **options):
    1512        # Inspired by Postfix's "postconf -n".
    16         from django.conf import settings, global_settings
     13        from django.conf import settings, global_settings, dict_from_object
    1714
    1815        # Because settings are imported lazily, we need to explicitly load them.
    1916        settings._setup()
    2017
    21         user_settings = module_to_dict(settings._wrapped)
    22         default_settings = module_to_dict(global_settings)
     18        user_settings = dict_from_object(settings._wrapped)
     19        default_settings = dict_from_object(global_settings)
    2320
    2421        output = []
    2522        keys = user_settings.keys()
    2623        keys.sort()
    2724        for key in keys:
    2825            if key not in default_settings:
    29                 output.append("%s = %s  ###" % (key, user_settings[key]))
     26                output.append("%s = %r  ###" % (key, user_settings[key]))
    3027            elif user_settings[key] != default_settings[key]:
    31                 output.append("%s = %s" % (key, user_settings[key]))
     28                output.append("%s = %r" % (key, user_settings[key]))
    3229        return '\n'.join(output)
  • django/test/utils.py

    === modified file 'django/test/utils.py'
     
    221221        return inner
    222222
    223223    def enable(self):
    224         override = OverrideSettingsHolder(settings._wrapped)
    225         for key, new_value in self.options.items():
    226             setattr(override, key, new_value)
     224        override = OverrideSettingsHolder(default_settings=settings._wrapped,
     225                                          extra_settings=self.options)
    227226        settings._wrapped = override
    228227
    229228    def disable(self):
  • tests/regressiontests/app_loading/test_settings.py

    === modified file 'tests/regressiontests/app_loading/test_settings.py'
     
    11INSTALLED_APPS = (
    22    'parent.*',
    33)
     4
     5TIME_ZONE='Europe/London'
  • tests/regressiontests/app_loading/tests.py

    === modified file 'tests/regressiontests/app_loading/tests.py'
     
    33import sys
    44import time
    55
    6 from django.conf import Settings
     6from django.conf import Settings, LazySettings
    77from django.db.models.loading import cache, load_app, get_model, get_models
    88from django.utils.unittest import TestCase
    99
    1010
    11 class InstalledAppsGlobbingTest(TestCase):
    12     def setUp(self):
    13         self.OLD_SYS_PATH = sys.path[:]
    14         sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    15         self.OLD_TZ = os.environ.get("TZ")
    16 
    17     def test_globbing(self):
    18         settings = Settings('test_settings')
    19         self.assertEqual(settings.INSTALLED_APPS, ['parent.app', 'parent.app1', 'parent.app_2'])
    20 
    21     def tearDown(self):
    22         sys.path = self.OLD_SYS_PATH
    23         if hasattr(time, "tzset") and self.OLD_TZ:
    24             os.environ["TZ"] = self.OLD_TZ
    25             time.tzset()
    26 
    27 
    2811class EggLoadingTest(TestCase):
    2912
    3013    def setUp(self):
     
    123106        self.assertEqual(
    124107            set(NotInstalledModel._meta.get_all_field_names()),
    125108            set(["id", "relatedmodel", "m2mrelatedmodel"]))
     109
     110
     111class SettingsMixin(object):
     112
     113    def setUp(self):
     114        self.OLD_SYS_PATH = sys.path[:]
     115        sys.path.append(os.path.dirname(os.path.abspath(__file__)))
     116        self.OLD_TZ = os.environ.get("TZ")
     117
     118    def tearDown(self):
     119        sys.path = self.OLD_SYS_PATH
     120        if hasattr(time, "tzset") and self.OLD_TZ:
     121            os.environ["TZ"] = self.OLD_TZ
     122            time.tzset()
     123
     124
     125class SettingsConfigureTest(SettingsMixin, TestCase):
     126    """
     127    Tests for settings using settings.configure().
     128    """
     129
     130    def test_installed_app_globbing(self):
     131        settings = LazySettings()
     132        settings.configure(INSTALLED_APPS=('parent.*',))
     133        self.assertEqual(settings.INSTALLED_APPS, ['parent.app', 'parent.app1', 'parent.app_2'])
     134
     135    def test_tuple_settings(self):
     136        settings = LazySettings()
     137        settings.configure(INSTALLED_APPS="parent.app", TEMPLATE_DIRS="foo")
     138        self.assertEqual(settings.INSTALLED_APPS, ['parent.app'])
     139        self.assertEqual(settings.TEMPLATE_DIRS, ('foo',))
     140
     141    def test_time_zone_environ(self):
     142        """
     143        Time zone environment variable shouldn't get altered.
     144        """
     145        orig_tz = os.environ.get('TZ')
     146        settings = LazySettings()
     147        settings.configure(TIME_ZONE='Europe/London')
     148        # Time zone updated, but not environment variable.
     149        self.assertEqual(settings.TIME_ZONE, 'Europe/London')
     150        self.assertEqual(os.environ.get('TZ'), orig_tz)
     151
     152
     153class SettingsTest(SettingsMixin, TestCase):
     154    """
     155    Tests for settings using Settings object.
     156    """
     157
     158    def test_installed_app_globbing(self):
     159        settings = Settings('test_settings')
     160        self.assertEqual(settings.INSTALLED_APPS, ['parent.app', 'parent.app1', 'parent.app_2'])
     161
     162    def test_tuple_settings(self):
     163        settings = Settings('tuple_settings')
     164        self.assertEqual(settings.INSTALLED_APPS, ['parent.app'])
     165        self.assertEqual(settings.TEMPLATE_DIRS, ('foo',))
     166
     167    def test_time_zone_environ(self):
     168        """
     169        Time zone environment variable shouldn't get altered.
     170        """
     171        orig_tz = os.environ.get('TZ')
     172        settings = Settings('test_settings')
     173        self.assertEqual(settings.TIME_ZONE, 'Europe/London')
     174        self.assertEqual(os.environ.get('TZ'), 'Europe/London')
  • tests/regressiontests/app_loading/tuple_settings.py

    === added file 'tests/regressiontests/app_loading/tuple_settings.py'
     
     1# Settings file for testing correction of tuple settings.
     2INSTALLED_APPS="parent.app"
     3TEMPLATE_DIRS="foo"
Back to Top