Commit 86804ab0 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

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.
parent 2b56d691
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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):
+14 −75
Original line number Diff line number Diff line
@@ -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():
+1 −1
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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.
+1 −1
Original line number Diff line number Diff line
@@ -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,
Loading