1"""
2Settings and configuration for Django.
3
4Read values from the module specified by the DJANGO_SETTINGS_MODULE environment
5variable, and then from django.conf.global_settings; see the global_settings.py
6for a list of all possible variables.
7"""
8
9import importlib
10import os
11import time
12import traceback
13import warnings
14from pathlib import Path
15
16import django
17from django.conf import global_settings
18from django.core.exceptions import ImproperlyConfigured
19from django.utils.functional import LazyObject, empty
20
21ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
22DEFAULT_STORAGE_ALIAS = "default"
23STATICFILES_STORAGE_ALIAS = "staticfiles"
24
25
26class SettingsReference(str):
27 """
28 String subclass which references a current settings value. It's treated as
29 the value in memory but serializes to a settings.NAME attribute reference.
30 """
31
32 def __new__(self, value, setting_name):
33 return str.__new__(self, value)
34
35 def __init__(self, value, setting_name):
36 self.setting_name = setting_name
37
38
39class LazySettings(LazyObject):
40 """
41 A lazy proxy for either global Django settings or a custom settings object.
42 The user can manually configure settings prior to using them. Otherwise,
43 Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE.
44 """
45
46 def _setup(self, name=None):
47 """
48 Load the settings module pointed to by the environment variable. This
49 is used the first time settings are needed, if the user hasn't
50 configured settings manually.
51 """
52 settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
53 if not settings_module:
54 desc = ("setting %s" % name) if name else "settings"
55 raise ImproperlyConfigured(
56 "Requested %s, but settings are not configured. "
57 "You must either define the environment variable %s "
58 "or call settings.configure() before accessing settings."
59 % (desc, ENVIRONMENT_VARIABLE)
60 )
61
62 self._wrapped = Settings(settings_module)
63
64 def __repr__(self):
65 # Hardcode the class name as otherwise it yields 'Settings'.
66 if self._wrapped is empty:
67 return "<LazySettings [Unevaluated]>"
68 return '<LazySettings "%(settings_module)s">' % {
69 "settings_module": self._wrapped.SETTINGS_MODULE,
70 }
71
72 def __getattr__(self, name):
73 """Return the value of a setting and cache it in self.__dict__."""
74 if (_wrapped := self._wrapped) is empty:
75 self._setup(name)
76 _wrapped = self._wrapped
77 val = getattr(_wrapped, name)
78
79 # Special case some settings which require further modification.
80 # This is done here for performance reasons so the modified value is cached.
81 if name in {"MEDIA_URL", "STATIC_URL"} and val is not None:
82 val = self._add_script_prefix(val)
83 elif name == "SECRET_KEY" and not val:
84 raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.")
85
86 self.__dict__[name] = val
87 return val
88
89 def __setattr__(self, name, value):
90 """
91 Set the value of setting. Clear all cached values if _wrapped changes
92 (@override_settings does this) or clear single values when set.
93 """
94 if name == "_wrapped":
95 self.__dict__.clear()
96 else:
97 self.__dict__.pop(name, None)
98 super().__setattr__(name, value)
99
100 def __delattr__(self, name):
101 """Delete a setting and clear it from cache if needed."""
102 super().__delattr__(name)
103 self.__dict__.pop(name, None)
104
105 def configure(self, default_settings=global_settings, **options):
106 """
107 Called to manually configure the settings. The 'default_settings'
108 parameter sets where to retrieve any unspecified values from (its
109 argument must support attribute access (__getattr__)).
110 """
111 if self._wrapped is not empty:
112 raise RuntimeError("Settings already configured.")
113 holder = UserSettingsHolder(default_settings)
114 for name, value in options.items():
115 if not name.isupper():
116 raise TypeError("Setting %r must be uppercase." % name)
117 setattr(holder, name, value)
118 self._wrapped = holder
119
120 @staticmethod
121 def _add_script_prefix(value):
122 """
123 Add SCRIPT_NAME prefix to relative paths.
124
125 Useful when the app is being served at a subpath and manually prefixing
126 subpath to STATIC_URL and MEDIA_URL in settings is inconvenient.
127 """
128 # Don't apply prefix to absolute paths and URLs.
129 if value.startswith(("http://", "https://", "/")):
130 return value
131 from django.urls import get_script_prefix
132
133 return "%s%s" % (get_script_prefix(), value)
134
135 @property
136 def configured(self):
137 """Return True if the settings have already been configured."""
138 return self._wrapped is not empty
139
140 def _show_deprecation_warning(self, message, category):
141 stack = traceback.extract_stack()
142 # Show a warning if the setting is used outside of Django.
143 # Stack index: -1 this line, -2 the property, -3 the
144 # LazyObject __getattribute__(), -4 the caller.
145 filename, _, _, _ = stack[-4]
146 if not filename.startswith(os.path.dirname(django.__file__)):
147 warnings.warn(message, category, stacklevel=2)
148
149
150class Settings:
151 def __init__(self, settings_module):
152 # update this dict from global settings (but only for ALL_CAPS settings)
153 for setting in dir(global_settings):
154 if setting.isupper():
155 setattr(self, setting, getattr(global_settings, setting))
156
157 # store the settings module in case someone later cares
158 self.SETTINGS_MODULE = settings_module
159
160 mod = importlib.import_module(self.SETTINGS_MODULE)
161
162 tuple_settings = (
163 "ALLOWED_HOSTS",
164 "INSTALLED_APPS",
165 "TEMPLATE_DIRS",
166 "LOCALE_PATHS",
167 "SECRET_KEY_FALLBACKS",
168 )
169 self._explicit_settings = set()
170 for setting in dir(mod):
171 if setting.isupper():
172 setting_value = getattr(mod, setting)
173
174 if setting in tuple_settings and not isinstance(
175 setting_value, (list, tuple)
176 ):
177 raise ImproperlyConfigured(
178 "The %s setting must be a list or a tuple." % setting
179 )
180 setattr(self, setting, setting_value)
181 self._explicit_settings.add(setting)
182
183 if hasattr(time, "tzset") and self.TIME_ZONE:
184 # When we can, attempt to validate the timezone. If we can't find
185 # this file, no check happens and it's harmless.
186 zoneinfo_root = Path("/usr/share/zoneinfo")
187 zone_info_file = zoneinfo_root.joinpath(*self.TIME_ZONE.split("/"))
188 if zoneinfo_root.exists() and not zone_info_file.exists():
189 raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE)
190 # Move the time zone info into os.environ. See ticket #2315 for why
191 # we don't do this unconditionally (breaks Windows).
192 os.environ["TZ"] = self.TIME_ZONE
193 time.tzset()
194
195 def is_overridden(self, setting):
196 return setting in self._explicit_settings
197
198 def __repr__(self):
199 return '<%(cls)s "%(settings_module)s">' % {
200 "cls": self.__class__.__name__,
201 "settings_module": self.SETTINGS_MODULE,
202 }
203
204
205class UserSettingsHolder:
206 """Holder for user configured settings."""
207
208 # SETTINGS_MODULE doesn't make much sense in the manually configured
209 # (standalone) case.
210 SETTINGS_MODULE = None
211
212 def __init__(self, default_settings):
213 """
214 Requests for configuration variables not in this class are satisfied
215 from the module specified in default_settings (if possible).
216 """
217 self.__dict__["_deleted"] = set()
218 self.default_settings = default_settings
219
220 def __getattr__(self, name):
221 if not name.isupper() or name in self._deleted:
222 raise AttributeError
223 return getattr(self.default_settings, name)
224
225 def __setattr__(self, name, value):
226 self._deleted.discard(name)
227 super().__setattr__(name, value)
228
229 def __delattr__(self, name):
230 self._deleted.add(name)
231 if hasattr(self, name):
232 super().__delattr__(name)
233
234 def __dir__(self):
235 return sorted(
236 s
237 for s in [*self.__dict__, *dir(self.default_settings)]
238 if s not in self._deleted
239 )
240
241 def is_overridden(self, setting):
242 deleted = setting in self._deleted
243 set_locally = setting in self.__dict__
244 set_on_default = getattr(
245 self.default_settings, "is_overridden", lambda s: False
246 )(setting)
247 return deleted or set_locally or set_on_default
248
249 def __repr__(self):
250 return "<%(cls)s>" % {
251 "cls": self.__class__.__name__,
252 }
253
254
255settings = LazySettings()