summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2014-01-01 18:01:06 +0100
committerAymeric Augustin <aymeric.augustin@m4x.org>2014-01-01 18:11:09 +0100
commitf17d00278ee4a22421dc8148588d94fe7fa17427 (patch)
treeecd9266faea7bc75ff8f53f20027e914e26ffd3c
parent8a2f304a798497373a19530e58d7290d13e35e6d (diff)
Wiped get_commands() cache when INSTALLED_APPS changes.
Refs #21018, #21688.
-rw-r--r--django/core/management/__init__.py54
-rw-r--r--django/test/signals.py3
2 files changed, 28 insertions, 29 deletions
diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py
index 3d6482f236..1b9597ee3d 100644
--- a/django/core/management/__init__.py
+++ b/django/core/management/__init__.py
@@ -11,15 +11,12 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import BaseCommand, CommandError, handle_default_options
from django.core.management.color import color_style
+from django.utils import lru_cache
from django.utils import six
# For backwards compatibility: get_version() used to be in this module.
from django import get_version
-# A cache of loaded commands, so that call_command
-# doesn't have to reload every time it's called.
-_commands = None
-
def find_commands(management_dir):
"""
@@ -85,6 +82,7 @@ def load_command_class(app_name, name):
return module.Command()
+@lru_cache.lru_cache(maxsize=None)
def get_commands():
"""
Returns a dictionary mapping command names to their callback applications.
@@ -107,34 +105,32 @@ def get_commands():
The dictionary is cached on the first call and reused on subsequent
calls.
"""
- global _commands
- if _commands is None:
- _commands = dict((name, 'django.core') for name in find_commands(__path__[0]))
+ commands = {name: 'django.core' for name in find_commands(__path__[0])}
- # Find the installed apps
- try:
- settings.INSTALLED_APPS
- except ImproperlyConfigured:
- # Still useful for commands that do not require functional
- # settings, like startproject or help.
- app_names = []
- else:
- # Setup Django outside of the try/except block to avoid catching
- # ImproperlyConfigured errors that aren't caused by the absence of
- # a settings module.
- django.setup()
- app_configs = apps.get_app_configs()
- app_names = [app_config.name for app_config in app_configs]
+ # Find the installed apps
+ try:
+ settings.INSTALLED_APPS
+ except ImproperlyConfigured:
+ # Still useful for commands that do not require functional
+ # settings, like startproject or help.
+ app_names = []
+ else:
+ # Setup Django outside of the try/except block to avoid catching
+ # ImproperlyConfigured errors that aren't caused by the absence of
+ # a settings module.
+ django.setup()
+ app_configs = apps.get_app_configs()
+ app_names = [app_config.name for app_config in app_configs]
- # Find and load the management module for each installed app.
- for app_name in app_names:
- try:
- path = find_management_module(app_name)
- _commands.update(dict((name, app_name) for name in find_commands(path)))
- except ImportError:
- pass # No management module - ignore this app
+ # Find and load the management module for each installed app.
+ for app_name in app_names:
+ try:
+ path = find_management_module(app_name)
+ commands.update({name: app_name for name in find_commands(path)})
+ except ImportError:
+ pass # No management module - ignore this app
- return _commands
+ return commands
def call_command(name, *args, **options):
diff --git a/django/test/signals.py b/django/test/signals.py
index b806592919..d5b98081b9 100644
--- a/django/test/signals.py
+++ b/django/test/signals.py
@@ -39,6 +39,9 @@ def update_installed_apps(**kwargs):
# Rebuild templatetags module cache.
from django.template import base
base.templatetags_modules[:] = []
+ # Rebuild management commands cache
+ from django.core.management import get_commands
+ get_commands.cache_clear()
@receiver(setting_changed)