summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2013-12-18 14:49:29 +0100
committerAymeric Augustin <aymeric.augustin@m4x.org>2013-12-22 11:39:17 +0100
commit86804ab063cb9666b69638372abfe4174a2472b2 (patch)
tree8335e3dcf7781f0065457491e368d2981e80d36b
parent2b56d6910269ace19e3007d2b4a3445f7886314f (diff)
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.
-rw-r--r--django/contrib/admin/validation.py2
-rw-r--r--django/core/apps/cache.py89
-rw-r--r--django/core/serializers/base.py2
-rw-r--r--django/core/serializers/python.py2
-rw-r--r--django/db/models/base.py2
-rw-r--r--django/db/models/loading.py2
-rw-r--r--django/db/models/options.py2
-rw-r--r--tests/app_loading/tests.py5
-rwxr-xr-xtests/runtests.py2
9 files changed, 24 insertions, 84 deletions
diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py
index a15967e024..ee945f73e5 100644
--- a/django/contrib/admin/validation.py
+++ b/django/contrib/admin/validation.py
@@ -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):
diff --git a/django/core/apps/cache.py b/django/core/apps/cache.py
index cf2489185e..e9a4215440 100644
--- a/django/core/apps/cache.py
+++ b/django/core/apps/cache.py
@@ -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():
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index db4b79a020..b0bc8138ab 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -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
diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
index 13d6f01a4e..1e4c37e2ae 100644
--- a/django/core/serializers/python.py
+++ b/django/core/serializers/python.py
@@ -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.
diff --git a/django/db/models/base.py b/django/db/models/base.py
index a694350b97..d1e7323d17 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -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,
diff --git a/django/db/models/loading.py b/django/db/models/loading.py
index 5745dbed4d..00fae36769 100644
--- a/django/db/models/loading.py
+++ b/django/db/models/loading.py
@@ -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
diff --git a/django/db/models/options.py b/django/db/models/options.py
index f7be089539..fec9aa4fc4 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -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
diff --git a/tests/app_loading/tests.py b/tests/app_loading/tests.py
index e282306ec0..45f6cd6c31 100644
--- a/tests/app_loading/tests.py
+++ b/tests/app_loading/tests.py
@@ -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')
diff --git a/tests/runtests.py b/tests/runtests.py
index badbd6fbec..77cf005267 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -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()