Code

Ticket #14264: 14264.diff

File 14264.diff, 11.4 KB (added by gwilson, 3 years ago)
Line 
1=== modified file 'django/conf/__init__.py'
2--- django/conf/__init__.py     2011-06-30 08:06:19 +0000
3+++ django/conf/__init__.py     2011-09-10 06:47:02 +0000
4@@ -9,6 +9,7 @@
5 import os
6 import re
7 import time     # Needed for Windows
8+import types
9 import warnings
10 
11 from django.conf import global_settings
12@@ -49,9 +50,8 @@
13         """
14         if self._wrapped is not empty:
15             raise RuntimeError('Settings already configured.')
16-        holder = UserSettingsHolder(default_settings)
17-        for name, value in options.items():
18-            setattr(holder, name, value)
19+        holder = UserSettingsHolder(default_settings=default_settings,
20+                                    extra_settings=options)
21         self._wrapped = holder
22 
23     @property
24@@ -62,10 +62,25 @@
25         return self._wrapped is not empty
26 
27 
28-class BaseSettings(object):
29+class Settings(object):
30     """
31     Common logic for settings whether set by a module or by the user.
32     """
33+
34+    # Settings that should be converted into tuples if they're mistakenly
35+    # entered as strings.
36+    tuple_settings = ("INSTALLED_APPS", "TEMPLATE_DIRS")
37+    # Flag to specify whether or not changes should be made to environment.
38+    environ_changes_allowed = True
39+
40+    def __init__(self, settings_module=None, default_settings=global_settings,
41+                 extra_settings=None):
42+        if settings_module:
43+            # store the settings module in case someone later cares
44+            self.SETTINGS_MODULE = settings_module
45+        self.initialize([default_settings, settings_module, extra_settings])
46+        self.post_setup()
47+
48     def __setattr__(self, name, value):
49         if name in ("MEDIA_URL", "STATIC_URL") and value and not value.endswith('/'):
50             warnings.warn("If set, %s must end with a slash" % name,
51@@ -75,35 +90,59 @@
52                           "use STATIC_URL instead.", DeprecationWarning)
53         object.__setattr__(self, name, value)
54 
55-
56-class Settings(BaseSettings):
57-    def __init__(self, settings_module):
58-        # update this dict from global settings (but only for ALL_CAPS settings)
59-        for setting in dir(global_settings):
60-            if setting == setting.upper():
61-                setattr(self, setting, getattr(global_settings, setting))
62-
63-        # store the settings module in case someone later cares
64-        self.SETTINGS_MODULE = settings_module
65+    def initialize(self, settings_objs):
66+        """
67+        Initializes this settings object based on attributes/keys from the
68+        objects in the settings_objs list.
69+
70+        If the item is a string, then attempt to import a module by name.
71+        Once we have a module object, or if a module object was passed, then
72+        then construct a dictionary from the attributes in the module.
73+        Once we have a dictionary, or if a dictionary was passed, then set all
74+        ALL CAPS keys and values as attributes on this settings object.
75+        """
76+        for obj in settings_objs:
77+            if not obj:
78+                continue
79+            if isinstance(obj, basestring):
80+                obj = self._import_module(obj)
81+            if isinstance(obj, types.ModuleType):
82+                obj = self._dict_from_module(obj)
83+            # obj should now be a dictionary.
84+            for setting, value in obj.iteritems():
85+                # Only set attributes that are (ALL CAPS).
86+                if setting == setting.upper():
87+                    setattr(self, setting, value)
88+
89+    def _import_module(self, name):
90 
91         try:
92             mod = importlib.import_module(self.SETTINGS_MODULE)
93         except ImportError, e:
94-            raise ImportError("Could not import settings '%s' (Is it on sys.path?): %s" % (self.SETTINGS_MODULE, e))
95-
96-        # Settings that should be converted into tuples if they're mistakenly entered
97-        # as strings.
98-        tuple_settings = ("INSTALLED_APPS", "TEMPLATE_DIRS")
99-
100-        for setting in dir(mod):
101-            if setting == setting.upper():
102-                setting_value = getattr(mod, setting)
103-                if setting in tuple_settings and type(setting_value) == str:
104-                    setting_value = (setting_value,) # In case the user forgot the comma.
105-                setattr(self, setting, setting_value)
106-
107-        # Expand entries in INSTALLED_APPS like "django.contrib.*" to a list
108-        # of all those apps.
109+            raise ImportError(
110+                "Could not import settings '%s' (Is it on sys.path?): %s"
111+                % (self.SETTINGS_MODULE, e))
112+        return mod
113+
114+    def _dict_from_module(self, module):
115+        d = {}
116+        for setting in dir(module):
117+            d[setting] = getattr(module, setting)
118+        return d
119+
120+    def post_setup(self):
121+        self._correct_tuple_settings()
122+        # Must remain after call to correct tuple settings, because
123+        # INSTALLED_APPS is a tuple setting.
124+        self._expand_installed_apps()
125+        self._set_tz()
126+        self._setup_logging()
127+
128+    def _expand_installed_apps(self):
129+        """
130+        Expand glob entries in INSTALLED_APPS, e.g. "django.contrib.*", to a
131+        list of all those apps.
132+        """
133         new_installed_apps = []
134         for app in self.INSTALLED_APPS:
135             if app.endswith('.*'):
136@@ -119,6 +158,19 @@
137                 new_installed_apps.append(app)
138         self.INSTALLED_APPS = new_installed_apps
139 
140+    def _correct_tuple_settings(self):
141+        """
142+        For settings that are meant to be tuples, auto correct if user forgot
143+        trailing comma on a single value.
144+        """
145+        for setting in self.tuple_settings:
146+            value = getattr(self, setting, None)
147+            if isinstance(value, basestring):
148+                setattr(self, setting, (value,))
149+
150+    def _set_tz(self):
151+        if not self.environ_changes_allowed:
152+            return
153         if hasattr(time, 'tzset') and self.TIME_ZONE:
154             # When we can, attempt to validate the timezone. If we can't find
155             # this file, no check happens and it's harmless.
156@@ -131,6 +183,7 @@
157             os.environ['TZ'] = self.TIME_ZONE
158             time.tzset()
159 
160+    def _setup_logging(self):
161         # Settings are configured, so we can set up the logger if required
162         if self.LOGGING_CONFIG:
163             # First find the logging configuration function ...
164@@ -145,20 +198,23 @@
165             logging_config_func(self.LOGGING)
166 
167 
168-class UserSettingsHolder(BaseSettings):
169+class UserSettingsHolder(Settings):
170     """
171     Holder for user configured settings.
172     """
173     # SETTINGS_MODULE doesn't make much sense in the manually configured
174     # (standalone) case.
175     SETTINGS_MODULE = None
176+    # Don't make any modifications to the process environment variables.
177+    environ_changes_allowed = False
178 
179-    def __init__(self, default_settings):
180+    def __init__(self, *args, **kwargs):
181         """
182         Requests for configuration variables not in this class are satisfied
183         from the module specified in default_settings (if possible).
184         """
185-        self.default_settings = default_settings
186+        self.default_settings = kwargs['default_settings']
187+        super(UserSettingsHolder, self).__init__(*args, **kwargs)
188 
189     def __getattr__(self, name):
190         return getattr(self.default_settings, name)
191
192=== modified file 'tests/regressiontests/app_loading/test_settings.py'
193--- tests/regressiontests/app_loading/test_settings.py  2009-03-08 08:39:48 +0000
194+++ tests/regressiontests/app_loading/test_settings.py  2011-09-10 03:23:11 +0000
195@@ -1,3 +1,5 @@
196 INSTALLED_APPS = (
197     'parent.*',
198 )
199+
200+TIME_ZONE='Europe/London'
201
202=== modified file 'tests/regressiontests/app_loading/tests.py'
203--- tests/regressiontests/app_loading/tests.py  2011-04-27 16:51:43 +0000
204+++ tests/regressiontests/app_loading/tests.py  2011-09-10 03:45:40 +0000
205@@ -3,28 +3,11 @@
206 import sys
207 import time
208 
209-from django.conf import Settings
210+from django.conf import Settings, LazySettings
211 from django.db.models.loading import cache, load_app, get_model, get_models
212 from django.utils.unittest import TestCase
213 
214 
215-class InstalledAppsGlobbingTest(TestCase):
216-    def setUp(self):
217-        self.OLD_SYS_PATH = sys.path[:]
218-        sys.path.append(os.path.dirname(os.path.abspath(__file__)))
219-        self.OLD_TZ = os.environ.get("TZ")
220-
221-    def test_globbing(self):
222-        settings = Settings('test_settings')
223-        self.assertEqual(settings.INSTALLED_APPS, ['parent.app', 'parent.app1', 'parent.app_2'])
224-
225-    def tearDown(self):
226-        sys.path = self.OLD_SYS_PATH
227-        if hasattr(time, "tzset") and self.OLD_TZ:
228-            os.environ["TZ"] = self.OLD_TZ
229-            time.tzset()
230-
231-
232 class EggLoadingTest(TestCase):
233 
234     def setUp(self):
235@@ -123,3 +106,69 @@
236         self.assertEqual(
237             set(NotInstalledModel._meta.get_all_field_names()),
238             set(["id", "relatedmodel", "m2mrelatedmodel"]))
239+
240+
241+class SettingsMixin(object):
242+
243+    def setUp(self):
244+        self.OLD_SYS_PATH = sys.path[:]
245+        sys.path.append(os.path.dirname(os.path.abspath(__file__)))
246+        self.OLD_TZ = os.environ.get("TZ")
247+
248+    def tearDown(self):
249+        sys.path = self.OLD_SYS_PATH
250+        if hasattr(time, "tzset") and self.OLD_TZ:
251+            os.environ["TZ"] = self.OLD_TZ
252+            time.tzset()
253+
254+
255+class SettingsConfigureTest(SettingsMixin, TestCase):
256+    """
257+    Tests for settings using settings.configure().
258+    """
259+
260+    def test_installed_app_globbing(self):
261+        settings = LazySettings()
262+        settings.configure(INSTALLED_APPS=('parent.*',))
263+        self.assertEqual(settings.INSTALLED_APPS, ['parent.app', 'parent.app1', 'parent.app_2'])
264+
265+    def test_tuple_settings(self):
266+        settings = LazySettings()
267+        settings.configure(INSTALLED_APPS="parent.app", TEMPLATE_DIRS="foo")
268+        self.assertEqual(settings.INSTALLED_APPS, ['parent.app'])
269+        self.assertEqual(settings.TEMPLATE_DIRS, ('foo',))
270+
271+    def test_time_zone_environ(self):
272+        """
273+        Time zone environment variable shouldn't get altered.
274+        """
275+        orig_tz = os.environ.get('TZ')
276+        settings = LazySettings()
277+        settings.configure(TIME_ZONE='Europe/London')
278+        # Time zone updated, but not environment variable.
279+        self.assertEqual(settings.TIME_ZONE, 'Europe/London')
280+        self.assertEqual(os.environ.get('TZ'), orig_tz)
281+
282+
283+class SettingsTest(SettingsMixin, TestCase):
284+    """
285+    Tests for settings using Settings object.
286+    """
287+
288+    def test_installed_app_globbing(self):
289+        settings = Settings('test_settings')
290+        self.assertEqual(settings.INSTALLED_APPS, ['parent.app', 'parent.app1', 'parent.app_2'])
291+
292+    def test_tuple_settings(self):
293+        settings = Settings('tuple_settings')
294+        self.assertEqual(settings.INSTALLED_APPS, ['parent.app'])
295+        self.assertEqual(settings.TEMPLATE_DIRS, ('foo',))
296+
297+    def test_time_zone_environ(self):
298+        """
299+        Time zone environment variable shouldn't get altered.
300+        """
301+        orig_tz = os.environ.get('TZ')
302+        settings = Settings('test_settings')
303+        self.assertEqual(settings.TIME_ZONE, 'Europe/London')
304+        self.assertEqual(os.environ.get('TZ'), 'Europe/London')
305
306=== added file 'tests/regressiontests/app_loading/tuple_settings.py'
307--- tests/regressiontests/app_loading/tuple_settings.py 1970-01-01 00:00:00 +0000
308+++ tests/regressiontests/app_loading/tuple_settings.py 2011-09-10 06:48:24 +0000
309@@ -0,0 +1,3 @@
310+# Settings file for testing correction of tuple settings.
311+INSTALLED_APPS="parent.app"
312+TEMPLATE_DIRS="foo"
313