|
16 | 16 | 'load_app', 'app_cache_ready')
|
17 | 17 |
|
18 | 18 |
|
19 |
| -class AppCache(object): |
| 19 | +def _initialize(): |
20 | 20 | """
|
21 |
| - A cache that stores installed applications and their models. Used to |
22 |
| - provide reverse-relations and for app introspection (e.g. admin). |
| 21 | + Returns a dictionary to be used as the initial value of the |
| 22 | + [shared] state of the app cache. |
23 | 23 | """
|
24 |
| - |
25 |
| - def __init__(self): |
| 24 | + return dict( |
26 | 25 | # Keys of app_store are the model modules for each application.
|
27 |
| - self.app_store = SortedDict() |
| 26 | + app_store = SortedDict(), |
| 27 | + |
28 | 28 | # Mapping of installed app_labels to model modules for that app.
|
29 |
| - self.app_labels = {} |
| 29 | + app_labels = {}, |
| 30 | + |
30 | 31 | # Mapping of app_labels to a dictionary of model names to model code.
|
31 | 32 | # May contain apps that are not installed.
|
32 |
| - self.app_models = SortedDict() |
| 33 | + app_models = SortedDict(), |
| 34 | + |
33 | 35 | # Mapping of app_labels to errors raised when trying to import the app.
|
34 |
| - self.app_errors = {} |
| 36 | + app_errors = {}, |
| 37 | + |
35 | 38 | # -- Everything below here is only used when populating the cache --
|
36 |
| - self.loaded = False |
37 |
| - self.handled = {} |
38 |
| - self.postponed = [] |
39 |
| - self.nesting_level = 0 |
40 |
| - self._get_models_cache = {} |
| 39 | + loaded = False, |
| 40 | + handled = {}, |
| 41 | + postponed = [], |
| 42 | + nesting_level = 0, |
| 43 | + _get_models_cache = {}, |
| 44 | + ) |
| 45 | + |
| 46 | + |
| 47 | +class BaseAppCache(object): |
| 48 | + """ |
| 49 | + A cache that stores installed applications and their models. Used to |
| 50 | + provide reverse-relations and for app introspection (e.g. admin). |
| 51 | +
|
| 52 | + This provides the base (non-Borg) AppCache class - the AppCache |
| 53 | + subclass adds borg-like behaviour for the few cases where it's needed, |
| 54 | + and adds the code that auto-loads from INSTALLED_APPS. |
| 55 | + """ |
| 56 | + |
| 57 | + def __init__(self): |
| 58 | + self.__dict__ = _initialize() |
41 | 59 |
|
42 | 60 | def _populate(self):
|
43 | 61 | """
|
44 |
| - Fill in all the cache information. This method is threadsafe, in the |
45 |
| - sense that every caller will see the same state upon return, and if the |
46 |
| - cache is already initialised, it does no work. |
| 62 | + Stub method - this base class does no auto-loading. |
47 | 63 | """
|
48 |
| - if self.loaded: |
49 |
| - return |
50 |
| - # Note that we want to use the import lock here - the app loading is |
51 |
| - # in many cases initiated implicitly by importing, and thus it is |
52 |
| - # possible to end up in deadlock when one thread initiates loading |
53 |
| - # without holding the importer lock and another thread then tries to |
54 |
| - # import something which also launches the app loading. For details of |
55 |
| - # this situation see #18251. |
56 |
| - imp.acquire_lock() |
57 |
| - try: |
58 |
| - if self.loaded: |
59 |
| - return |
60 |
| - for app_name in settings.INSTALLED_APPS: |
61 |
| - if app_name in self.handled: |
62 |
| - continue |
63 |
| - self.load_app(app_name, True) |
64 |
| - if not self.nesting_level: |
65 |
| - for app_name in self.postponed: |
66 |
| - self.load_app(app_name) |
67 |
| - self.loaded = True |
68 |
| - finally: |
69 |
| - imp.release_lock() |
| 64 | + self.loaded = True |
70 | 65 |
|
71 | 66 | def _label_for(self, app_mod):
|
72 | 67 | """
|
@@ -253,42 +248,58 @@ def copy_from(self, other):
|
253 | 248 | self.register_models(app_label, *models.values())
|
254 | 249 |
|
255 | 250 |
|
256 |
| -class AppCacheWrapper(object): |
257 |
| - """ |
258 |
| - As AppCache can be changed at runtime, this class wraps it so any |
259 |
| - imported references to 'cache' are changed along with it. |
| 251 | +class AppCache(BaseAppCache): |
260 | 252 | """
|
| 253 | + A cache that stores installed applications and their models. Used to |
| 254 | + provide reverse-relations and for app introspection (e.g. admin). |
261 | 255 |
|
262 |
| - def __init__(self, cache): |
263 |
| - self._cache = cache |
| 256 | + Borg version of the BaseAppCache class. |
| 257 | + """ |
264 | 258 |
|
265 |
| - def set_cache(self, cache): |
266 |
| - self._cache = cache |
| 259 | + __shared_state = _initialize() |
267 | 260 |
|
268 |
| - def __getattr__(self, attr): |
269 |
| - if attr in ("_cache", "set_cache"): |
270 |
| - return self.__dict__[attr] |
271 |
| - return getattr(self._cache, attr) |
| 261 | + def __init__(self): |
| 262 | + self.__dict__ = self.__shared_state |
272 | 263 |
|
273 |
| - def __setattr__(self, attr, value): |
274 |
| - if attr in ("_cache", "set_cache"): |
275 |
| - self.__dict__[attr] = value |
| 264 | + def _populate(self): |
| 265 | + """ |
| 266 | + Fill in all the cache information. This method is threadsafe, in the |
| 267 | + sense that every caller will see the same state upon return, and if the |
| 268 | + cache is already initialised, it does no work. |
| 269 | + """ |
| 270 | + if self.loaded: |
276 | 271 | return
|
277 |
| - return setattr(self._cache, attr, value) |
278 |
| - |
| 272 | + # Note that we want to use the import lock here - the app loading is |
| 273 | + # in many cases initiated implicitly by importing, and thus it is |
| 274 | + # possible to end up in deadlock when one thread initiates loading |
| 275 | + # without holding the importer lock and another thread then tries to |
| 276 | + # import something which also launches the app loading. For details of |
| 277 | + # this situation see #18251. |
| 278 | + imp.acquire_lock() |
| 279 | + try: |
| 280 | + if self.loaded: |
| 281 | + return |
| 282 | + for app_name in settings.INSTALLED_APPS: |
| 283 | + if app_name in self.handled: |
| 284 | + continue |
| 285 | + self.load_app(app_name, True) |
| 286 | + if not self.nesting_level: |
| 287 | + for app_name in self.postponed: |
| 288 | + self.load_app(app_name) |
| 289 | + self.loaded = True |
| 290 | + finally: |
| 291 | + imp.release_lock() |
279 | 292 |
|
280 |
| -default_cache = AppCache() |
281 |
| -cache = AppCacheWrapper(default_cache) |
| 293 | +cache = AppCache() |
282 | 294 |
|
283 | 295 |
|
284 | 296 | # These methods were always module level, so are kept that way for backwards
|
285 |
| -# compatibility. These are wrapped with lambdas to stop the attribute |
286 |
| -# access resolving directly to a method on a single cache instance. |
287 |
| -get_apps = lambda *x, **y: cache.get_apps(*x, **y) |
288 |
| -get_app = lambda *x, **y: cache.get_app(*x, **y) |
289 |
| -get_app_errors = lambda *x, **y: cache.get_app_errors(*x, **y) |
290 |
| -get_models = lambda *x, **y: cache.get_models(*x, **y) |
291 |
| -get_model = lambda *x, **y: cache.get_model(*x, **y) |
292 |
| -register_models = lambda *x, **y: cache.register_models(*x, **y) |
293 |
| -load_app = lambda *x, **y: cache.load_app(*x, **y) |
294 |
| -app_cache_ready = lambda *x, **y: cache.app_cache_ready(*x, **y) |
| 297 | +# compatibility. |
| 298 | +get_apps = cache.get_apps |
| 299 | +get_app = cache.get_app |
| 300 | +get_app_errors = cache.get_app_errors |
| 301 | +get_models = cache.get_models |
| 302 | +get_model = cache.get_model |
| 303 | +register_models = cache.register_models |
| 304 | +load_app = cache.load_app |
| 305 | +app_cache_ready = cache.app_cache_ready |
0 commit comments