From 16aae35ca8508dff2e0b94da67b8c880fe98e9d1 Mon Sep 17 00:00:00 2001
From: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Fri, 20 Dec 2013 10:39:12 +0100
Subject: [PATCH] Improved set_available_apps() in several ways.

- Tested consistency the current app_configs instead of INSTALLED_APPS.
- Considered applications added with _with_app as available.
- Added docstrings.
---
 django/core/apps/cache.py              | 19 ++++++++++++++++++-
 tests/proxy_model_inheritance/tests.py |  5 +----
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/django/core/apps/cache.py b/django/core/apps/cache.py
index 697cf9e516..92b9f8d8d4 100644
--- a/django/core/apps/cache.py
+++ b/django/core/apps/cache.py
@@ -320,14 +320,27 @@ class AppCache(object):
         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.
+
+        Primarily used for performance optimization in TransactionTestCase.
+        """
+        if self.available_apps is not None:
+            raise RuntimeError("set_available_apps() may be called only once "
+                "in a row; make sure it's paired with unset_available_apps()")
         available = set(available)
-        installed = set(settings.INSTALLED_APPS)
+        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.available_apps = available
 
     def unset_available_apps(self):
+        """
+        Cancels a previous call to set_available_apps().
+        """
         self.available_apps = None
 
     ### DANGEROUS METHODS ### (only used to preserve existing tests)
@@ -340,11 +353,15 @@ class AppCache(object):
         else:
             app_config.import_models(self.all_models[app_config.label])
             self.app_configs[app_config.label] = app_config
+            if self.available_apps is not None:
+                self.available_apps.add(app_config.name)
             return app_config
 
     def _end_with_app(self, app_config):
         if app_config is not None:
             del self.app_configs[app_config.label]
+            if self.available_apps is not None:
+                self.available_apps.discard(app_config.name)
 
     @contextmanager
     def _with_app(self, app_name):
diff --git a/tests/proxy_model_inheritance/tests.py b/tests/proxy_model_inheritance/tests.py
index 91fcbbcc09..5f33ec2fee 100644
--- a/tests/proxy_model_inheritance/tests.py
+++ b/tests/proxy_model_inheritance/tests.py
@@ -6,22 +6,19 @@ import sys
 from django.core.apps import app_cache
 from django.core.management import call_command
 from django.test import TestCase, TransactionTestCase
-from django.test.utils import override_settings
 from django.utils._os import upath
 
 from .models import (ConcreteModel, ConcreteModelSubclass,
     ConcreteModelSubclassProxy)
 
 
-# Required for available_apps.
-@override_settings(INSTALLED_APPS=['app1', 'app2'])
 class ProxyModelInheritanceTests(TransactionTestCase):
     """
     Proxy model inheritance across apps can result in migrate not creating the table
     for the proxied model (as described in #12286).  This test creates two dummy
     apps and calls migrate, then verifies that the table has been created.
     """
-    available_apps = ['app1', 'app2']
+    available_apps = []
 
     def setUp(self):
         self.old_sys_path = sys.path[:]