Code

Ticket #14297: settings_optimization.diff

File settings_optimization.diff, 6.3 KB (added by akaariai, 4 years ago)
Line 
1diff --git a/django/conf/__init__.py b/django/conf/__init__.py
2index bda0ef3..f6ae4e0 100644
3--- a/django/conf/__init__.py
4+++ b/django/conf/__init__.py
5@@ -16,18 +16,48 @@ from django.utils import importlib
6 
7 ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
8 
9-class LazySettings(LazyObject):
10-    """
11-    A lazy proxy for either global Django settings or a custom settings object.
12-    The user can manually configure settings prior to using them. Otherwise,
13-    Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE.
14-    """
15+
16+class LazySettings(object):
17+    def __init__(self):
18+        self._is_configured = False
19+        # Uqly hack to make the test pass. Should patch
20+        # the tests instead. (Backwards compatibility
21+        # should not be a problem here).
22+        self._wrapped = self
23+        self._has_real_wrapped = False
24+   
25+    def __getattr__(self, name):
26+        if not self._is_configured:
27+            self._setup()
28+            self._is_configured = True
29+            if name in self.__dict__:
30+                return self.__dict__[name]
31+            else:
32+                raise AttributeError
33+        else:
34+            if self._has_real_wrapped:
35+                return getattr(self._wrapped, name)
36+            raise AttributeError
37+     
38+    def __delattr__(self, name):
39+         if name == "_wrapped":
40+             raise TypeError("can't delete _wrapped.")
41+         # Need more checks here for _is_configured etc?
42+        if not self._is_configured:
43+            self._setup()
44+        del self.__dict__[name]
45+   
46     def _setup(self):
47-        """
48-        Load the settings module pointed to by the environment variable. This
49-        is used the first time we need any settings at all, if the user has not
50-        previously configured the settings manually.
51-        """
52+        # NOTE: we are working inside __getattr__ here, so be careful to not
53+        # refer to any missing value in self.
54+
55+        # We want to store the actual settings only if the whole _real_init
56+        # is succesfull. This way either the settings will be all set up
57+        # or none at all, just like with LazySettings. So we store all the
58+        # settings in new_dict and copy it to self.__dict__ when
59+        # we are done.
60+       
61+        new_dict = {}
62         try:
63             settings_module = os.environ[ENVIRONMENT_VARIABLE]
64             if not settings_module: # If it's set but is an empty string.
65@@ -37,7 +67,60 @@ class LazySettings(LazyObject):
66             # problems with Python's interactive help.
67             raise ImportError("Settings cannot be imported, because environment variable %s is undefined." % ENVIRONMENT_VARIABLE)
68 
69-        self._wrapped = Settings(settings_module)
70+        # update this dict from global settings (but only for ALL_CAPS settings)
71+        for setting in dir(global_settings):
72+            if setting == setting.upper():
73+                new_dict[setting] = getattr(global_settings, setting)
74+
75+        # store the settings module in case someone later cares
76+        new_dict["SETTINGS_MODULE"] = settings_module
77+
78+        try:
79+            mod = importlib.import_module(settings_module)
80+        except ImportError, e:
81+            raise ImportError("Could not import settings '%s' (Is it on sys.path? Does it have syntax errors?): %s" % (settings_module, e))
82+
83+        # Settings that should be converted into tuples if they're mistakenly entered
84+        # as strings.
85+        tuple_settings = ("INSTALLED_APPS", "TEMPLATE_DIRS")
86+
87+        for setting in dir(mod):
88+            if setting == setting.upper():
89+                setting_value = getattr(mod, setting)
90+                if setting in tuple_settings and type(setting_value) == str:
91+                    setting_value = (setting_value,) # In case the user forgot the comma.
92+                new_dict[setting] = setting_value
93+
94+        # Expand entries in INSTALLED_APPS like "django.contrib.*" to a list
95+        # of all those apps.
96+        new_installed_apps = []
97+        for app in new_dict['INSTALLED_APPS']:
98+            if app.endswith('.*'):
99+                app_mod = importlib.import_module(app[:-2])
100+                appdir = os.path.dirname(app_mod.__file__)
101+                app_subdirs = os.listdir(appdir)
102+                app_subdirs.sort()
103+                name_pattern = re.compile(r'[a-zA-Z]\w*')
104+                for d in app_subdirs:
105+                    if name_pattern.match(d) and os.path.isdir(os.path.join(appdir, d)):
106+                        new_installed_apps.append('%s.%s' % (app[:-2], d))
107+            else:
108+                new_installed_apps.append(app)
109+        new_dict["INSTALLED_APPS"] = new_installed_apps
110+
111+        if hasattr(time, 'tzset') and new_dict.get('TIME_ZONE'):
112+            # When we can, attempt to validate the timezone. If we can't find
113+            # this file, no check happens and it's harmless.
114+            zoneinfo_root = '/usr/share/zoneinfo'
115+            if (os.path.exists(zoneinfo_root) and not
116+                    os.path.exists(os.path.join(zoneinfo_root, *(new_dict['TIME_ZONE'].split('/'))))):
117+                raise ValueError("Incorrect timezone setting: %s" % new_dict['TIME_ZONE'])
118+            # Move the time zone info into os.environ. See ticket #2315 for why
119+            # we don't do this unconditionally (breaks Windows).
120+            os.environ['TZ'] = new_dict['TIME_ZONE']
121+            time.tzset()
122+        # Everything passed. Now it is safe to make the settings visible.
123+        self.__dict__.update(new_dict)
124 
125     def configure(self, default_settings=global_settings, **options):
126         """
127@@ -45,18 +128,19 @@ class LazySettings(LazyObject):
128         parameter sets where to retrieve any unspecified values from (its
129         argument must support attribute access (__getattr__)).
130         """
131-        if self._wrapped != None:
132+        if self._is_configured:
133             raise RuntimeError('Settings already configured.')
134         holder = UserSettingsHolder(default_settings)
135         for name, value in options.items():
136             setattr(holder, name, value)
137         self._wrapped = holder
138+        self._has_real_wrapped = True
139 
140     def configured(self):
141         """
142         Returns True if the settings have already been configured.
143         """
144-        return bool(self._wrapped)
145+        return self._is_configured
146     configured = property(configured)
147 
148 class Settings(object):