Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/apps/config.py: 74%
132 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1import inspect
2import os
3import warnings
4from importlib import import_module
6from django.core.exceptions import ImproperlyConfigured
7from django.utils.deprecation import RemovedInDjango41Warning
8from django.utils.functional import cached_property
9from django.utils.module_loading import import_string, module_has_submodule
11APPS_MODULE_NAME = "apps"
12MODELS_MODULE_NAME = "models"
15class AppConfig:
16 """Class representing a Django application and its configuration."""
18 def __init__(self, app_name, app_module):
19 # Full Python path to the application e.g. 'django.contrib.admin'.
20 self.name = app_name
22 # Root module for the application e.g. <module 'django.contrib.admin'
23 # from 'django/contrib/admin/__init__.py'>.
24 self.module = app_module
26 # Reference to the Apps registry that holds this AppConfig. Set by the
27 # registry when it registers the AppConfig instance.
28 self.apps = None
30 # The following attributes could be defined at the class level in a
31 # subclass, hence the test-and-set pattern.
33 # Last component of the Python path to the application e.g. 'admin'.
34 # This value must be unique across a Django project.
35 if not hasattr(self, "label"): 35 ↛ 37line 35 didn't jump to line 37, because the condition on line 35 was never false
36 self.label = app_name.rpartition(".")[2]
37 if not self.label.isidentifier(): 37 ↛ 38line 37 didn't jump to line 38, because the condition on line 37 was never true
38 raise ImproperlyConfigured(
39 "The app label '%s' is not a valid Python identifier." % self.label
40 )
42 # Human-readable name for the application e.g. "Admin".
43 if not hasattr(self, "verbose_name"):
44 self.verbose_name = self.label.title()
46 # Filesystem path to the application directory e.g.
47 # '/path/to/django/contrib/admin'.
48 if not hasattr(self, "path"): 48 ↛ 54line 48 didn't jump to line 54, because the condition on line 48 was never false
49 self.path = self._path_from_module(app_module)
51 # Module containing models e.g. <module 'django.contrib.admin.models'
52 # from 'django/contrib/admin/models.py'>. Set by import_models().
53 # None if the application doesn't have a models module.
54 self.models_module = None
56 # Mapping of lowercase model names to model classes. Initially set to
57 # None to prevent accidental access before import_models() runs.
58 self.models = None
60 def __repr__(self):
61 return "<%s: %s>" % (self.__class__.__name__, self.label)
63 @cached_property
64 def default_auto_field(self):
65 from django.conf import settings
67 return settings.DEFAULT_AUTO_FIELD
69 @property
70 def _is_default_auto_field_overridden(self):
71 return self.__class__.default_auto_field is not AppConfig.default_auto_field
73 def _path_from_module(self, module):
74 """Attempt to determine app's filesystem path from its module."""
75 # See #21874 for extended discussion of the behavior of this method in
76 # various cases.
77 # Convert to list because __path__ may not support indexing.
78 paths = list(getattr(module, "__path__", []))
79 if len(paths) != 1: 79 ↛ 80line 79 didn't jump to line 80, because the condition on line 79 was never true
80 filename = getattr(module, "__file__", None)
81 if filename is not None:
82 paths = [os.path.dirname(filename)]
83 else:
84 # For unknown reasons, sometimes the list returned by __path__
85 # contains duplicates that must be removed (#25246).
86 paths = list(set(paths))
87 if len(paths) > 1: 87 ↛ 88line 87 didn't jump to line 88, because the condition on line 87 was never true
88 raise ImproperlyConfigured(
89 "The app module %r has multiple filesystem locations (%r); "
90 "you must configure this app with an AppConfig subclass "
91 "with a 'path' class attribute." % (module, paths)
92 )
93 elif not paths: 93 ↛ 94line 93 didn't jump to line 94, because the condition on line 93 was never true
94 raise ImproperlyConfigured(
95 "The app module %r has no filesystem location, "
96 "you must configure this app with an AppConfig subclass "
97 "with a 'path' class attribute." % module
98 )
99 return paths[0]
101 @classmethod
102 def create(cls, entry):
103 """
104 Factory that creates an app config from an entry in INSTALLED_APPS.
105 """
106 # create() eventually returns app_config_class(app_name, app_module).
107 app_config_class = None
108 app_config_name = None
109 app_name = None
110 app_module = None
112 # If import_module succeeds, entry points to the app module.
113 try:
114 app_module = import_module(entry)
115 except Exception:
116 pass
117 else:
118 # If app_module has an apps submodule that defines a single
119 # AppConfig subclass, use it automatically.
120 # To prevent this, an AppConfig subclass can declare a class
121 # variable default = False.
122 # If the apps module defines more than one AppConfig subclass,
123 # the default one can declare default = True.
124 if module_has_submodule(app_module, APPS_MODULE_NAME):
125 mod_path = "%s.%s" % (entry, APPS_MODULE_NAME)
126 mod = import_module(mod_path)
127 # Check if there's exactly one AppConfig candidate,
128 # excluding those that explicitly define default = False.
129 app_configs = [
130 (name, candidate)
131 for name, candidate in inspect.getmembers(mod, inspect.isclass)
132 if (
133 issubclass(candidate, cls)
134 and candidate is not cls
135 and getattr(candidate, "default", True)
136 )
137 ]
138 if len(app_configs) == 1:
139 app_config_class = app_configs[0][1]
140 app_config_name = "%s.%s" % (mod_path, app_configs[0][0])
141 else:
142 # Check if there's exactly one AppConfig subclass,
143 # among those that explicitly define default = True.
144 app_configs = [
145 (name, candidate)
146 for name, candidate in app_configs
147 if getattr(candidate, "default", False)
148 ]
149 if len(app_configs) > 1: 149 ↛ 150line 149 didn't jump to line 150, because the condition on line 149 was never true
150 candidates = [repr(name) for name, _ in app_configs]
151 raise RuntimeError(
152 "%r declares more than one default AppConfig: "
153 "%s." % (mod_path, ", ".join(candidates))
154 )
155 elif len(app_configs) == 1: 155 ↛ 163line 155 didn't jump to line 163, because the condition on line 155 was never false
156 app_config_class = app_configs[0][1]
157 app_config_name = "%s.%s" % (mod_path, app_configs[0][0])
159 # If app_module specifies a default_app_config, follow the link.
160 # default_app_config is deprecated, but still takes over the
161 # automatic detection for backwards compatibility during the
162 # deprecation period.
163 try:
164 new_entry = app_module.default_app_config
165 except AttributeError:
166 # Use the default app config class if we didn't find anything.
167 if app_config_class is None:
168 app_config_class = cls
169 app_name = entry
170 else:
171 message = "%r defines default_app_config = %r. " % (entry, new_entry)
172 if new_entry == app_config_name: 172 ↛ 178line 172 didn't jump to line 178, because the condition on line 172 was never false
173 message += (
174 "Django now detects this configuration automatically. "
175 "You can remove default_app_config."
176 )
177 else:
178 message += (
179 "However, Django's automatic detection %s. You should "
180 "move the default config class to the apps submodule "
181 "of your application and, if this module defines "
182 "several config classes, mark the default one with "
183 "default = True."
184 % (
185 "picked another configuration, %r" % app_config_name
186 if app_config_name
187 else "did not find this configuration"
188 )
189 )
190 warnings.warn(message, RemovedInDjango41Warning, stacklevel=2)
191 entry = new_entry
192 app_config_class = None
194 # If import_string succeeds, entry is an app config class.
195 if app_config_class is None:
196 try:
197 app_config_class = import_string(entry)
198 except Exception:
199 pass
200 # If both import_module and import_string failed, it means that entry
201 # doesn't have a valid value.
202 if app_module is None and app_config_class is None: 202 ↛ 206line 202 didn't jump to line 206, because the condition on line 202 was never true
203 # If the last component of entry starts with an uppercase letter,
204 # then it was likely intended to be an app config class; if not,
205 # an app module. Provide a nice error message in both cases.
206 mod_path, _, cls_name = entry.rpartition(".")
207 if mod_path and cls_name[0].isupper():
208 # We could simply re-trigger the string import exception, but
209 # we're going the extra mile and providing a better error
210 # message for typos in INSTALLED_APPS.
211 # This may raise ImportError, which is the best exception
212 # possible if the module at mod_path cannot be imported.
213 mod = import_module(mod_path)
214 candidates = [
215 repr(name)
216 for name, candidate in inspect.getmembers(mod, inspect.isclass)
217 if issubclass(candidate, cls) and candidate is not cls
218 ]
219 msg = "Module '%s' does not contain a '%s' class." % (
220 mod_path,
221 cls_name,
222 )
223 if candidates:
224 msg += " Choices are: %s." % ", ".join(candidates)
225 raise ImportError(msg)
226 else:
227 # Re-trigger the module import exception.
228 import_module(entry)
230 # Check for obvious errors. (This check prevents duck typing, but
231 # it could be removed if it became a problem in practice.)
232 if not issubclass(app_config_class, AppConfig): 232 ↛ 233line 232 didn't jump to line 233, because the condition on line 232 was never true
233 raise ImproperlyConfigured("'%s' isn't a subclass of AppConfig." % entry)
235 # Obtain app name here rather than in AppClass.__init__ to keep
236 # all error checking for entries in INSTALLED_APPS in one place.
237 if app_name is None:
238 try:
239 app_name = app_config_class.name
240 except AttributeError:
241 raise ImproperlyConfigured("'%s' must supply a name attribute." % entry)
243 # Ensure app_name points to a valid module.
244 try:
245 app_module = import_module(app_name)
246 except ImportError:
247 raise ImproperlyConfigured(
248 "Cannot import '%s'. Check that '%s.%s.name' is correct."
249 % (
250 app_name,
251 app_config_class.__module__,
252 app_config_class.__qualname__,
253 )
254 )
256 # Entry is a path to an app config class.
257 return app_config_class(app_name, app_module)
259 def get_model(self, model_name, require_ready=True):
260 """
261 Return the model with the given case-insensitive model_name.
263 Raise LookupError if no model exists with this name.
264 """
265 if require_ready:
266 self.apps.check_models_ready()
267 else:
268 self.apps.check_apps_ready()
269 try:
270 return self.models[model_name.lower()]
271 except KeyError:
272 raise LookupError(
273 "App '%s' doesn't have a '%s' model." % (self.label, model_name)
274 )
276 def get_models(self, include_auto_created=False, include_swapped=False):
277 """
278 Return an iterable of models.
280 By default, the following models aren't included:
282 - auto-created models for many-to-many relations without
283 an explicit intermediate table,
284 - models that have been swapped out.
286 Set the corresponding keyword argument to True to include such models.
287 Keyword arguments aren't documented; they're a private API.
288 """
289 self.apps.check_models_ready()
290 for model in self.models.values():
291 if model._meta.auto_created and not include_auto_created:
292 continue
293 if model._meta.swapped and not include_swapped:
294 continue
295 yield model
297 def import_models(self):
298 # Dictionary of models for this app, primarily maintained in the
299 # 'all_models' attribute of the Apps this AppConfig is attached to.
300 self.models = self.apps.all_models[self.label]
302 if module_has_submodule(self.module, MODELS_MODULE_NAME):
303 models_module_name = "%s.%s" % (self.name, MODELS_MODULE_NAME)
304 self.models_module = import_module(models_module_name)
306 def ready(self):
307 """
308 Override this method in subclasses to run code when Django starts.
309 """