diff options
| author | Aymeric Augustin <aymeric.augustin@m4x.org> | 2013-12-24 12:25:17 +0100 |
|---|---|---|
| committer | Aymeric Augustin <aymeric.augustin@m4x.org> | 2013-12-24 12:25:17 +0100 |
| commit | 1716b7ce5a02b2a77188dfea2d41e25dfa58821c (patch) | |
| tree | f3422c0d6725f45756a3b9deb32406d319b8a72e /django/apps/cache.py | |
| parent | e9e522a8e7455900a775678e5d6bc518a289b8d1 (diff) | |
Renamed AppCache to Apps.
Also renamed app_cache to apps and "app cache" to "app registry".
Deprecated AppCache.app_cache_ready() in favor of Apps.ready().
Diffstat (limited to 'django/apps/cache.py')
| -rw-r--r-- | django/apps/cache.py | 449 |
1 files changed, 0 insertions, 449 deletions
diff --git a/django/apps/cache.py b/django/apps/cache.py deleted file mode 100644 index 92007f637a..0000000000 --- a/django/apps/cache.py +++ /dev/null @@ -1,449 +0,0 @@ -"Utilities for loading models and the modules that contain them." - -from collections import defaultdict, OrderedDict -import os -import sys -import warnings - -from django.conf import settings -from django.core.exceptions import ImproperlyConfigured -from django.utils import lru_cache -from django.utils.module_loading import import_lock -from django.utils._os import upath - -from .base import AppConfig - - -class AppCache(object): - """ - A cache that stores installed applications and their models. Used to - provide reverse-relations and for app introspection. - """ - - def __init__(self, master=False): - # Only one master of the app-cache may exist at a given time, and it - # shall be the app_cache variable defined at the end of this module. - if master and hasattr(sys.modules[__name__], 'app_cache'): - raise RuntimeError("You may create only one master app cache.") - - # When master is set to False, the app cache isn't populated from - # INSTALLED_APPS and ignores the only_installed arguments to - # get_model[s]. - self.master = master - - # Mapping of app labels => model names => model classes. Used to - # register models before the app cache is populated and also for - # applications that aren't installed. - self.all_models = defaultdict(OrderedDict) - - # Mapping of labels to AppConfig instances for installed apps. - self.app_configs = OrderedDict() - - # Stack of app_configs. Used to store the current state in - # set_available_apps and set_installed_apps. - self.stored_app_configs = [] - - # Internal flags used when populating the master cache. - self._apps_loaded = not self.master - self._models_loaded = not self.master - - # Pending lookups for lazy relations. - self._pending_lookups = {} - - def populate_apps(self, installed_apps=None): - """ - Populate app-related information. - - This method imports each application module. - - It is thread safe and idempotent, but not reentrant. - """ - if self._apps_loaded: - return - # Since populate_apps() may be a side effect of imports, and since - # it will itself import modules, an ABBA deadlock between threads - # would be possible if we didn't take the import lock. See #18251. - with import_lock(): - if self._apps_loaded: - return - - # app_config should be pristine, otherwise the code below won't - # guarantee that the order matches the order in INSTALLED_APPS. - if self.app_configs: - raise RuntimeError("populate_apps() isn't reentrant") - - # Application modules aren't expected to import anything, and - # especially not other application modules, even indirectly. - # Therefore we simply import them sequentially. - if installed_apps is None: - installed_apps = settings.INSTALLED_APPS - for app_name in installed_apps: - app_config = AppConfig.create(app_name) - self.app_configs[app_config.label] = app_config - - self.get_models.cache_clear() - self._apps_loaded = True - - def populate_models(self): - """ - Populate model-related information. - - This method imports each models module. - - It is thread safe, idempotent and reentrant. - """ - if self._models_loaded: - return - # Since populate_models() may be a side effect of imports, and since - # it will itself import modules, an ABBA deadlock between threads - # would be possible if we didn't take the import lock. See #18251. - with import_lock(): - if self._models_loaded: - return - - self.populate_apps() - - # Models modules are likely to import other models modules, for - # example to reference related objects. As a consequence: - # - we deal with import loops by postponing affected modules. - # - we provide reentrancy by making import_models() idempotent. - - outermost = not hasattr(self, '_postponed') - if outermost: - self._postponed = [] - - for app_config in self.app_configs.values(): - - if app_config.models is not None: - continue - - try: - all_models = self.all_models[app_config.label] - app_config.import_models(all_models) - except ImportError: - self._postponed.append(app_config) - - if outermost: - for app_config in self._postponed: - all_models = self.all_models[app_config.label] - app_config.import_models(all_models) - - del self._postponed - - self.get_models.cache_clear() - self._models_loaded = True - - def app_cache_ready(self): - """ - Returns true if the model cache is fully populated. - - Useful for code that wants to cache the results of get_models() for - themselves once it is safe to do so. - """ - return self._models_loaded # implies self._apps_loaded. - - def get_app_configs(self, only_with_models_module=False): - """ - Imports applications and returns an iterable of app configs. - - If only_with_models_module in True (non-default), imports models and - considers only applications containing a models module. - """ - if only_with_models_module: - self.populate_models() - else: - self.populate_apps() - - for app_config in self.app_configs.values(): - if only_with_models_module and app_config.models_module is None: - continue - yield app_config - - def get_app_config(self, app_label, only_with_models_module=False): - """ - Imports applications and returns an app config for the given label. - - Raises LookupError if no application exists with this label. - - If only_with_models_module in True (non-default), imports models and - considers only applications containing a models module. - """ - if only_with_models_module: - self.populate_models() - else: - self.populate_apps() - - app_config = self.app_configs.get(app_label) - if app_config is None: - raise LookupError("No installed app with label %r." % app_label) - if only_with_models_module and app_config.models_module is None: - raise LookupError("App with label %r doesn't have a models module." % app_label) - return app_config - - # This method is performance-critical at least for Django's test suite. - @lru_cache.lru_cache(maxsize=None) - def get_models(self, app_mod=None, - include_auto_created=False, include_deferred=False, - only_installed=True, include_swapped=False): - """ - Given a module containing models, returns a list of the models. - Otherwise returns a list of all installed models. - - By default, auto-created models (i.e., m2m models without an - explicit intermediate table) are not included. However, if you - specify include_auto_created=True, they will be. - - By default, models created to satisfy deferred attribute - queries are *not* included in the list of models. However, if - you specify include_deferred, they will be. - - By default, models that aren't part of installed apps will *not* - be included in the list of models. However, if you specify - only_installed=False, they will be. If you're using a non-default - AppCache, this argument does nothing - all models will be included. - - By default, models that have been swapped out will *not* be - included in the list of models. However, if you specify - include_swapped, they will be. - """ - if not self.master: - only_installed = False - model_list = None - self.populate_models() - if app_mod: - app_label = app_mod.__name__.split('.')[-2] - if only_installed: - try: - model_dicts = [self.app_configs[app_label].models] - except KeyError: - model_dicts = [] - else: - model_dicts = [self.all_models[app_label]] - else: - if only_installed: - model_dicts = [app_config.models for app_config in self.app_configs.values()] - else: - model_dicts = self.all_models.values() - model_list = [] - for model_dict in model_dicts: - model_list.extend( - model for model in model_dict.values() - if ((not model._deferred or include_deferred) and - (not model._meta.auto_created or include_auto_created) and - (not model._meta.swapped or include_swapped)) - ) - return model_list - - def get_model(self, app_label, model_name, only_installed=True): - """ - Returns the model matching the given app_label and case-insensitive - model_name. - - Returns None if no model is found. - """ - if not self.master: - only_installed = False - self.populate_models() - if only_installed: - app_config = self.app_configs.get(app_label) - if app_config is None: - return None - return self.all_models[app_label].get(model_name.lower()) - - def register_model(self, app_label, model): - # Since this method is called when models are imported, it cannot - # perform imports because of the risk of import loops. It mustn't - # call get_app_config(). - model_name = model._meta.model_name - models = self.all_models[app_label] - if model_name in models: - # The same model may be imported via different paths (e.g. - # appname.models and project.appname.models). We use the source - # filename as a means to detect identity. - fname1 = os.path.abspath(upath(sys.modules[model.__module__].__file__)) - fname2 = os.path.abspath(upath(sys.modules[models[model_name].__module__].__file__)) - # Since the filename extension could be .py the first time and - # .pyc or .pyo the second time, ignore the extension when - # comparing. - if os.path.splitext(fname1)[0] == os.path.splitext(fname2)[0]: - return - models[model_name] = model - self.get_models.cache_clear() - - def has_app(self, app_name): - """ - Checks whether an application with this name exists in the app cache. - - app_name is the full name of the app eg. 'django.contrib.admin'. - - It's safe to call this method at import time, even while the app cache - is being populated. It returns None for apps that aren't loaded yet. - """ - app_config = self.app_configs.get(app_name.rpartition(".")[2]) - return app_config is not None and app_config.name == app_name - - def get_registered_model(self, app_label, model_name): - """ - Returns the model class if one is registered and None otherwise. - - It's safe to call this method at import time, even while the app cache - is being populated. It returns None for models that aren't loaded yet. - """ - return self.all_models[app_label].get(model_name.lower()) - - def set_available_apps(self, available): - """ - Restricts the set of installed apps used by get_app_config[s]. - - available must be an iterable of application names. - - set_available_apps() must be balanced with unset_available_apps(). - - Primarily used for performance optimization in TransactionTestCase. - - This method is safe is the sense that it doesn't trigger any imports. - """ - available = set(available) - installed = set(app_config.name for app_config in self.get_app_configs()) - if not available.issubset(installed): - raise ValueError("Available apps isn't a subset of installed " - "apps, extra apps: %s" % ", ".join(available - installed)) - - self.stored_app_configs.append(self.app_configs) - self.app_configs = OrderedDict( - (label, app_config) - for label, app_config in self.app_configs.items() - if app_config.name in available) - self.get_models.cache_clear() - - def unset_available_apps(self): - """ - Cancels a previous call to set_available_apps(). - """ - self.app_configs = self.stored_app_configs.pop() - self.get_models.cache_clear() - - def set_installed_apps(self, installed): - """ - Enables a different set of installed apps for get_app_config[s]. - - installed must be an iterable in the same format as INSTALLED_APPS. - - set_installed_apps() must be balanced with unset_installed_apps(), - even if it exits with an exception. - - Primarily used as a receiver of the setting_changed signal in tests. - - This method may trigger new imports, which may add new models to the - registry of all imported models. They will stay in the registry even - after unset_installed_apps(). Since it isn't possible to replay - imports safely (eg. that could lead to registering listeners twice), - models are registered when they're imported and never removed. - """ - self.stored_app_configs.append(self.app_configs) - self.app_configs = OrderedDict() - self.get_models.cache_clear() - self._apps_loaded = False - self.populate_apps(installed) - self._models_loaded = False - self.populate_models() - - def unset_installed_apps(self): - """ - Cancels a previous call to set_installed_apps(). - """ - self.app_configs = self.stored_app_configs.pop() - self.get_models.cache_clear() - - ### DEPRECATED METHODS GO BELOW THIS LINE ### - - def load_app(self, app_name): - """ - Loads the app with the provided fully qualified name, and returns the - model module. - """ - warnings.warn( - "load_app(app_name) is deprecated.", - PendingDeprecationWarning, stacklevel=2) - app_config = AppConfig.create(app_name) - app_config.import_models(self.all_models[app_config.label]) - self.app_configs[app_config.label] = app_config - self.get_models.cache_clear() - return app_config.models_module - - def get_app(self, app_label): - """ - Returns the module containing the models for the given app_label. - """ - warnings.warn( - "get_app_config(app_label).models_module supersedes get_app(app_label).", - PendingDeprecationWarning, stacklevel=2) - try: - return self.get_app_config( - app_label, only_with_models_module=True).models_module - except LookupError as exc: - # Change the exception type for backwards compatibility. - raise ImproperlyConfigured(*exc.args) - - def get_apps(self): - """ - Returns a list of all installed modules that contain models. - """ - warnings.warn( - "[a.models_module for a in get_app_configs()] supersedes get_apps().", - PendingDeprecationWarning, stacklevel=2) - app_configs = self.get_app_configs(only_with_models_module=True) - return [app_config.models_module for app_config in app_configs] - - def _get_app_package(self, app): - return '.'.join(app.__name__.split('.')[:-1]) - - def get_app_package(self, app_label): - warnings.warn( - "get_app_config(label).name supersedes get_app_package(label).", - PendingDeprecationWarning, stacklevel=2) - return self._get_app_package(self.get_app(app_label)) - - def _get_app_path(self, app): - if hasattr(app, '__path__'): # models/__init__.py package - app_path = app.__path__[0] - else: # models.py module - app_path = app.__file__ - return os.path.dirname(upath(app_path)) - - def get_app_path(self, app_label): - warnings.warn( - "get_app_config(label).path supersedes get_app_path(label).", - PendingDeprecationWarning, stacklevel=2) - return self._get_app_path(self.get_app(app_label)) - - def get_app_paths(self): - """ - Returns a list of paths to all installed apps. - - Useful for discovering files at conventional locations inside apps - (static files, templates, etc.) - """ - warnings.warn( - "[a.path for a in get_app_configs()] supersedes get_app_paths().", - PendingDeprecationWarning, stacklevel=2) - - self.populate_models() - - app_paths = [] - for app in self.get_apps(): - app_paths.append(self._get_app_path(app)) - return app_paths - - def register_models(self, app_label, *models): - """ - Register a set of models as belonging to an app. - """ - warnings.warn( - "register_models(app_label, *models) is deprecated.", - PendingDeprecationWarning, stacklevel=2) - for model in models: - self.register_model(app_label, model) - - -app_cache = AppCache(master=True) |
