Ticket #14264: 14264.diff

File 14264.diff, 11.4 KB (added by gwilson, 3 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
     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        If the item is a string, then attempt to import a module by name.
     99        Once we have a module object, or if a module object was passed, then
     100        then construct a dictionary from the attributes in the module.
     101        Once we have a dictionary, or if a dictionary was passed, then set all
     102        ALL CAPS keys and values as attributes on this settings object.
     103        """
     104        for obj in settings_objs:
     105            if not obj:
     106                continue
     107            if isinstance(obj, basestring):
     108                obj = self._import_module(obj)
     109            if isinstance(obj, types.ModuleType):
     110                obj = self._dict_from_module(obj)
     111            # obj should now be a dictionary.
     112            for setting, value in obj.iteritems():
     113                # Only set attributes that are (ALL CAPS).
     114                if setting == setting.upper():
     115                    setattr(self, setting, value)
     116
     117    def _import_module(self, name):
    88118
    89119        try:
    90120            mod = importlib.import_module(self.SETTINGS_MODULE)
    91121        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.
     122            raise ImportError(
     123                "Could not import settings '%s' (Is it on sys.path?): %s"
     124                % (self.SETTINGS_MODULE, e))
     125        return mod
     126
     127    def _dict_from_module(self, module):
     128        d = {}
     129        for setting in dir(module):
     130            d[setting] = getattr(module, setting)
     131        return d
     132
     133    def post_setup(self):
     134        self._correct_tuple_settings()
     135        # Must remain after call to correct tuple settings, because
     136        # INSTALLED_APPS is a tuple setting.
     137        self._expand_installed_apps()
     138        self._set_tz()
     139        self._setup_logging()
     140
     141    def _expand_installed_apps(self):
     142        """
     143        Expand glob entries in INSTALLED_APPS, e.g. "django.contrib.*", to a
     144        list of all those apps.
     145        """
    107146        new_installed_apps = []
    108147        for app in self.INSTALLED_APPS:
    109148            if app.endswith('.*'):
     
    119158                new_installed_apps.append(app)
    120159        self.INSTALLED_APPS = new_installed_apps
    121160
     161    def _correct_tuple_settings(self):
     162        """
     163        For settings that are meant to be tuples, auto correct if user forgot
     164        trailing comma on a single value.
     165        """
     166        for setting in self.tuple_settings:
     167            value = getattr(self, setting, None)
     168            if isinstance(value, basestring):
     169                setattr(self, setting, (value,))
     170
     171    def _set_tz(self):
     172        if not self.environ_changes_allowed:
     173            return
    122174        if hasattr(time, 'tzset') and self.TIME_ZONE:
    123175            # When we can, attempt to validate the timezone. If we can't find
    124176            # this file, no check happens and it's harmless.
     
    131183            os.environ['TZ'] = self.TIME_ZONE
    132184            time.tzset()
    133185
     186    def _setup_logging(self):
    134187        # Settings are configured, so we can set up the logger if required
    135188        if self.LOGGING_CONFIG:
    136189            # First find the logging configuration function ...
     
    145198            logging_config_func(self.LOGGING)
    146199
    147200
    148 class UserSettingsHolder(BaseSettings):
     201class UserSettingsHolder(Settings):
    149202    """
    150203    Holder for user configured settings.
    151204    """
    152205    # SETTINGS_MODULE doesn't make much sense in the manually configured
    153206    # (standalone) case.
    154207    SETTINGS_MODULE = None
     208    # Don't make any modifications to the process environment variables.
     209    environ_changes_allowed = False
    155210
    156     def __init__(self, default_settings):
     211    def __init__(self, *args, **kwargs):
    157212        """
    158213        Requests for configuration variables not in this class are satisfied
    159214        from the module specified in default_settings (if possible).
    160215        """
    161         self.default_settings = default_settings
     216        self.default_settings = kwargs['default_settings']
     217        super(UserSettingsHolder, self).__init__(*args, **kwargs)
    162218
    163219    def __getattr__(self, name):
    164220        return getattr(self.default_settings, name)
  • 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