Terminated AppCache._populate() with extreme prejudice.

It was called _populate() before I renamed it to populate(). Since it
has been superseded by populate_models() there's no reason to keep it.

Removed the can_postpone argument of load_app() as it was only used by
populate(). It's a private API and there's no replacement. Simplified
load_app() accordingly. Then new version behaves exactly like the old
one even though it's much shorter.
This commit is contained in:
Aymeric Augustin 2013-12-18 14:49:29 +01:00
parent 2b56d69102
commit 86804ab063
9 changed files with 24 additions and 84 deletions

View File

@ -18,7 +18,7 @@ class BaseValidator(object):
def __init__(self):
# Before we can introspect models, they need the app cache to be fully
# loaded so that inter-relations are set up correctly.
app_cache.populate()
app_cache.populate_models()
def validate(self, cls, model):
for m in dir(self):

View File

@ -2,17 +2,16 @@
from collections import defaultdict, OrderedDict
from contextlib import contextmanager
from importlib import import_module
import os
import sys
import warnings
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.module_loading import import_lock, module_has_submodule
from django.utils.module_loading import import_lock
from django.utils._os import upath
from .base import AppConfig, MODELS_MODULE_NAME
from .base import AppConfig
class UnavailableApp(Exception):
@ -51,15 +50,11 @@ class AppCache(object):
# Used by TransactionTestCase.available_apps for performance reasons.
self.available_apps = None
# Internal flags used when populating the cache.
self._apps_loaded = False
self._models_loaded = False
# Internal flags used when populating the master cache.
self._apps_loaded = not self.master
self._models_loaded = not self.master
# -- Everything below here is only used when populating the cache --
self.loaded = False
self.handled = set()
self.postponed = []
self.nesting_level = 0
# Cache for get_models.
self._get_models_cache = {}
def populate_apps(self):
@ -138,71 +133,15 @@ class AppCache(object):
self._models_loaded = True
def populate(self):
"""
Fill in all the cache information. This method is threadsafe, in the
sense that every caller will see the same state upon return, and if the
cache is already initialised, it does no work.
"""
if self.loaded:
return
if not self.master:
self.loaded = True
return
# Note that we want to use the import lock here - the app loading is
# in many cases initiated implicitly by importing, and thus it is
# possible to end up in deadlock when one thread initiates loading
# without holding the importer lock and another thread then tries to
# import something which also launches the app loading. For details of
# this situation see #18251.
with import_lock():
if self.loaded:
return
for app_name in settings.INSTALLED_APPS:
if app_name in self.handled:
continue
self.load_app(app_name, can_postpone=True)
if not self.nesting_level:
for app_name in self.postponed:
self.load_app(app_name)
self.loaded = True
def load_app(self, app_name, can_postpone=False):
def load_app(self, app_name):
"""
Loads the app with the provided fully qualified name, and returns the
model module.
"""
app_module = import_module(app_name)
self.handled.add(app_name)
self.nesting_level += 1
try:
models_module = import_module('%s.%s' % (app_name, MODELS_MODULE_NAME))
except ImportError:
# If the app doesn't have a models module, we can just swallow the
# ImportError and return no models for this app.
if not module_has_submodule(app_module, MODELS_MODULE_NAME):
models_module = None
# But if the app does have a models module, we need to figure out
# whether to suppress or propagate the error. If can_postpone is
# True then it may be that the package is still being imported by
# Python and the models module isn't available yet. So we add the
# app to the postponed list and we'll try it again after all the
# recursion has finished (in populate). If can_postpone is False
# then it's time to raise the ImportError.
else:
if can_postpone:
self.postponed.append(app_name)
return
else:
raise
finally:
self.nesting_level -= 1
app_config = AppConfig(app_name)
app_config.import_models(self.all_models[app_config.label])
self.app_configs[app_config.label] = app_config
return models_module
return app_config.models_module
def app_cache_ready(self):
"""
@ -211,7 +150,7 @@ class AppCache(object):
Useful for code that wants to cache the results of get_models() for
themselves once it is safe to do so.
"""
return self.loaded
return self._models_loaded # implies self._apps_loaded.
def get_app_configs(self, only_with_models_module=False):
"""
@ -220,7 +159,7 @@ class AppCache(object):
If only_with_models_module in True (non-default), only applications
containing a models module are considered.
"""
self.populate()
self.populate_models()
for app_config in self.app_configs.values():
if only_with_models_module and app_config.models_module is None:
continue
@ -240,7 +179,7 @@ class AppCache(object):
If only_with_models_module in True (non-default), only applications
containing a models module are considered.
"""
self.populate()
self.populate_models()
app_config = self.app_configs.get(app_label)
if app_config is None:
raise LookupError("No installed app with label %r." % app_label)
@ -288,7 +227,7 @@ class AppCache(object):
return model_list
except KeyError:
pass
self.populate()
self.populate_models()
if app_mod:
app_label = app_mod.__name__.split('.')[-2]
if only_installed:
@ -331,7 +270,7 @@ class AppCache(object):
"""
if not self.master:
only_installed = False
self.populate()
self.populate_models()
if only_installed:
app_config = self.app_configs.get(app_label)
if app_config is None:
@ -496,7 +435,7 @@ class AppCache(object):
"[a.path for a in get_app_configs()] supersedes get_app_paths().",
PendingDeprecationWarning, stacklevel=2)
self.populate()
self.populate_models()
app_paths = []
for app in self.get_apps():

View File

@ -139,7 +139,7 @@ class Deserializer(six.Iterator):
self.stream = stream_or_string
# Make sure the app cache is loaded before deserialization starts
# (otherwise subclass calls to get_model() and friends might fail...)
app_cache.populate()
app_cache.populate_models()
def __iter__(self):
return self

View File

@ -88,7 +88,7 @@ def Deserializer(object_list, **options):
db = options.pop('using', DEFAULT_DB_ALIAS)
ignore = options.pop('ignorenonexistent', False)
app_cache.populate()
app_cache.populate_models()
for d in object_list:
# Look up the model and starting build a dict of data for it.

View File

@ -6,7 +6,7 @@ from functools import update_wrapper
from django.utils.six.moves import zip
from django.core.apps import app_cache
from django.core.apps.cache import MODELS_MODULE_NAME
from django.core.apps.base import MODELS_MODULE_NAME
import django.db.models.manager # NOQA: Imported to register signal handler.
from django.conf import settings
from django.core.exceptions import (ObjectDoesNotExist,

View File

@ -30,6 +30,6 @@ def get_app_errors():
try:
return app_cache.app_errors
except AttributeError:
app_cache.populate()
app_cache.populate_models()
app_cache.app_errors = {}
return app_cache.app_errors

View File

@ -93,7 +93,7 @@ class Options(object):
@property
def app_config(self):
# Don't go through get_app_config to avoid triggering populate().
# Don't go through get_app_config to avoid triggering imports.
return self.app_cache.app_configs.get(self.app_label)
@property

View File

@ -77,8 +77,9 @@ class EggLoadingTest(TestCase):
error. Refs #17667.
"""
app_cache = AppCache()
# Pretend we're the master app cache to test populate().
app_cache.master = True
# Pretend we're the master app cache to test the population process.
app_cache._apps_loaded = False
app_cache._models_loaded = False
with override_settings(INSTALLED_APPS=('notexists',)):
with self.assertRaises(ImportError):
app_cache.get_model('notexists', 'nomodel')

View File

@ -128,7 +128,7 @@ def setup(verbosity, test_labels):
# Load all the ALWAYS_INSTALLED_APPS.
with warnings.catch_warnings():
warnings.filterwarnings('ignore', 'django.contrib.comments is deprecated and will be removed before Django 1.8.', DeprecationWarning)
app_cache.populate()
app_cache.populate_models()
# Load all the test model apps.
test_modules = get_test_modules()