Index: django/test/simple.py
===================================================================
--- django/test/simple.py	(revision 9180)
+++ django/test/simple.py	(working copy)
@@ -63,7 +63,7 @@
             suite.addTest(test_module.suite())
         else:
             suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(test_module))
-            try:            
+            try:
                 suite.addTest(doctest.DocTestSuite(test_module, 
                                                    checker=doctestOutputChecker,
                                                    runner=DocTestRunner))
@@ -129,10 +129,14 @@
                 suite.addTest(build_test(label))
             else:
                 app = get_app(label)
-                suite.addTest(build_suite(app))
+                mod = app.models_module
+                if mod:
+                  suite.addTest(build_suite(mod))
     else:
         for app in get_apps():
-            suite.addTest(build_suite(app))
+            mod = app.models_module
+            if mod:
+                suite.addTest(build_suite(mod))
     
     for test in extra_tests:
         suite.addTest(test)
Index: django/test/client.py
===================================================================
--- django/test/client.py	(revision 9180)
+++ django/test/client.py	(working copy)
@@ -6,7 +6,7 @@
 except ImportError:
     from StringIO import StringIO
 
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 from django.contrib.auth import authenticate, login
 from django.core.handlers.base import BaseHandler
 from django.core.handlers.wsgi import WSGIRequest
@@ -169,7 +169,7 @@
         """
         Obtains the current session variables.
         """
-        if 'django.contrib.sessions' in settings.INSTALLED_APPS:
+        if 'django.contrib.sessions' in get_installed_app_paths():
             engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
             cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None)
             if cookie:
@@ -294,7 +294,7 @@
         """
         user = authenticate(**credentials)
         if user and user.is_active \
-                and 'django.contrib.sessions' in settings.INSTALLED_APPS:
+                and 'django.contrib.sessions' in get_installed_app_paths():
             engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
 
             # Create a fake request to store login details.
Index: django/conf/__init__.py
===================================================================
--- django/conf/__init__.py	(revision 9180)
+++ django/conf/__init__.py	(working copy)
@@ -108,15 +108,15 @@
         # of all those apps.
         new_installed_apps = []
         for app in self.INSTALLED_APPS:
-            if app.endswith('.*'):
+            if not isinstance(app, basestring) or not app.endswith('.*'):
+                new_installed_apps.append(app)
+            else:
                 appdir = os.path.dirname(__import__(app[:-2], {}, {}, ['']).__file__)
                 app_subdirs = os.listdir(appdir)
                 app_subdirs.sort()
                 for d in app_subdirs:
                     if d.isalpha() and os.path.isdir(os.path.join(appdir, d)):
                         new_installed_apps.append('%s.%s' % (app[:-2], d))
-            else:
-                new_installed_apps.append(app)
         self.INSTALLED_APPS = new_installed_apps
 
         if hasattr(time, 'tzset'):
@@ -151,3 +151,25 @@
 
 settings = LazySettings()
 
+class app(object):
+    """Configuration directive for specifying an app."""
+    def __init__(self, path, app_label=None, verbose_name=None):
+        self.path = path
+        # if name isn't specified, get the last part of the Python dotted path
+        self.label = app_label or path.split('.')[-1]
+        self.verbose_name = verbose_name or self.label
+        self.app_module = None    # will be filled in by loading.py
+        self.models_module = None # will be filled in by loading.py
+
+    def __repr__(self):
+        return "<app: %s>" % self.path
+
+def get_installed_app_paths():
+    "Return the paths of all entries in settings.INSTALLED_APPS."
+    rv = []
+    for a in settings.INSTALLED_APPS:
+        if isinstance(a, basestring):
+            rv.append(a)
+        else:
+            rv.append(a.path)
+    return rv
Index: django/db/models/base.py
===================================================================
--- django/db/models/base.py	(revision 9180)
+++ django/db/models/base.py	(working copy)
@@ -16,7 +16,7 @@
 from django.db.models.options import Options
 from django.db import connection, transaction, DatabaseError
 from django.db.models import signals
-from django.db.models.loading import register_models, get_model
+from django.db.models.loading import register_models, get_model, get_app_label
 from django.utils.functional import curry
 from django.utils.encoding import smart_str, force_unicode, smart_unicode
 from django.conf import settings
@@ -69,6 +69,9 @@
 
         if getattr(new_class, '_default_manager', None):
             new_class._default_manager = None
+        if getattr(new_class._meta, 'app_label', None) is None:
+            # Figure out the app_label.
+            new_class._meta.app_label = get_app_label(new_class)
 
         # Bail out early if we have already created this class.
         m = get_model(new_class._meta.app_label, name, False)
Index: django/db/models/options.py
===================================================================
--- django/db/models/options.py	(revision 9180)
+++ django/db/models/options.py	(working copy)
@@ -5,7 +5,7 @@
 except NameError:
     from sets import Set as set     # Python 2.3 fallback
 
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 from django.db.models.related import RelatedObject
 from django.db.models.fields.related import ManyToManyRel
 from django.db.models.fields import AutoField, FieldDoesNotExist
@@ -54,7 +54,7 @@
         from django.db.backends.util import truncate_name
 
         cls._meta = self
-        self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS
+        self.installed = re.sub('\.models$', '', cls.__module__) in get_installed_app_paths()
         # First, construct the default values for these options.
         self.object_name = cls.__name__
         self.module_name = self.object_name.lower()
Index: django/db/models/loading.py
===================================================================
--- django/db/models/loading.py	(revision 9180)
+++ django/db/models/loading.py	(working copy)
@@ -1,15 +1,15 @@
 "Utilities for loading models and the modules that contain them."
 
-from django.conf import settings
+from django.conf import settings, app
 from django.core.exceptions import ImproperlyConfigured
 from django.utils.datastructures import SortedDict
 
 import sys
-import os
+import os, os.path
 import threading
 
 __all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models',
-        'load_app', 'app_cache_ready')
+        'load_app', 'app_cache_ready', 'find_app')
 
 class AppCache(object):
     """
@@ -28,6 +28,16 @@
         # Mapping of app_labels to errors raised when trying to import the app.
         app_errors = {},
 
+
+        # Mapping of app_labels to app instances.
+        app_map = {},
+
+        # Mapping of app module names to app instances.
+        mod_map = {},
+
+        # List of app instances.
+        app_instances = [],
+
         # -- Everything below here is only used when populating the cache --
         loaded = False,
         handled = {},
@@ -51,13 +61,42 @@
         try:
             if self.loaded:
                 return
-            for app_name in settings.INSTALLED_APPS:
+            # We first loop through, setting up the app instances and
+            # the relevant maps so that we can find the instances by app_label
+            # or app module name. These need to be ready because we need to be
+            # able to get the app_label e.g. for applying to model classes.
+            # If a model class is loaded indirectly without being in an
+            # installed app, the app_label used will be what it is now - the
+            # last part of the app module name.
+            # Note that app_map and mod_map will contain entries even
+            # when an app cannot be loaded by load_app.
+            for app_entry in settings.INSTALLED_APPS:
+                if isinstance(app_entry, basestring):
+                    the_app = app(app_entry)
+                else:
+                    the_app = app_entry
+                self.app_map[the_app.label] = the_app
+                self.mod_map[the_app.path] = the_app
+
+            for app_entry in settings.INSTALLED_APPS:
+                if isinstance(app_entry, basestring):
+                    the_app = self.mod_map[app_entry]
+                else:
+                    the_app = app_entry
+                app_name = the_app.path
                 if app_name in self.handled:
                     continue
-                self.load_app(app_name, True)
+                try:
+                    self.load_app(app_name, True)
+                    self.app_instances.append(the_app)
+                except Exception, e:
+                    # Problem importing the app
+                    self.app_errors[app_name] = e
             if not self.nesting_level:
                 for app_name in self.postponed:
+                    the_app = self.mod_map[app_name]
                     self.load_app(app_name)
+                    self.app_instances.append(the_app)                    
                 self.loaded = True
         finally:
             self.write_lock.release()
@@ -71,6 +110,16 @@
         self.nesting_level += 1
         mod = __import__(app_name, {}, {}, ['models'])
         self.nesting_level -= 1
+        if app_name in self.mod_map:
+            the_app = self.mod_map[app_name]
+        else:
+            # An app can be loaded by load_app even before get_apps is
+            # called. In this case, we just make a new app and add it to the maps.
+            the_app = app(app_name)
+            self.app_map[the_app.label] = the_app
+            self.mod_map[app_name] = the_app
+            self.app_instances.append(the_app)
+        the_app.app_module = mod
         if not hasattr(mod, 'models'):
             if can_postpone:
                 # Either the app has no models, or the package is still being
@@ -78,10 +127,13 @@
                 # We will check again once all the recursion has finished (in
                 # populate).
                 self.postponed.append(app_name)
-            return None
-        if mod.models not in self.app_store:
-            self.app_store[mod.models] = len(self.app_store)
-        return mod.models
+            rv = None
+        else:
+            rv = mod.models
+            if rv not in self.app_store:
+                self.app_store[rv] = len(self.app_store)
+        the_app.models_module = rv
+        return rv
 
     def app_cache_ready(self):
         """
@@ -101,45 +153,61 @@
         # list page, for example.
         apps = [(v, k) for k, v in self.app_store.items()]
         apps.sort()
-        return [elt[1] for elt in apps]
+        #return [elt[1] for elt in apps]
+        return self.app_instances
 
+    def get_app_label(self, model_class):
+        "Returns the app label to be used for a model class."
+        key = model_class.__module__
+        i = key.rfind('.')
+        assert i > 0, "Model class must be defined in a package sub-module"
+        key = key[:i]
+        if key in self.mod_map:
+            rv = self.mod_map[key].label
+        else:
+            i = key.rfind('.')
+            if i < 0:
+                rv = key
+            else:
+                rv = key[i + 1:]
+        return rv
+    
+    def find_app(self, app_path):
+        "Find an app instance, given the application path."
+        self._populate()
+        return self.mod_map[app_path]
+
     def get_app(self, app_label, emptyOK=False):
         """
-        Returns the module containing the models for the given app_label. If
+        Returns the app instance for the given app_label. If
         the app has no models in it and 'emptyOK' is True, returns None.
         """
         self._populate()
-        self.write_lock.acquire()
-        try:
-            for app_name in settings.INSTALLED_APPS:
-                if app_label == app_name.split('.')[-1]:
-                    mod = self.load_app(app_name, False)
-                    if mod is None:
-                        if emptyOK:
-                            return None
-                    else:
-                        return mod
-            raise ImproperlyConfigured, "App with label %s could not be found" % app_label
-        finally:
-            self.write_lock.release()
+        if app_label not in self.app_map:
+            rv = None
+        else:
+            rv = self.app_map[app_label]
+        if emptyOK or rv is not None:
+            return rv
+        raise ImproperlyConfigured, "App with label %s could not be found" % app_label
 
     def get_app_errors(self):
         "Returns the map of known problems with the INSTALLED_APPS."
         self._populate()
         return self.app_errors
 
-    def get_models(self, app_mod=None):
+    def get_models(self, the_app=None):
         """
-        Given a module containing models, returns a list of the models.
+        Given an app instance, returns a list of its models.
         Otherwise returns a list of all installed models.
         """
         self._populate()
-        if app_mod:
-            return self.app_models.get(app_mod.__name__.split('.')[-2], SortedDict()).values()
+        if the_app:
+            return self.app_models.get(the_app.label, SortedDict()).values()
         else:
             model_list = []
-            for app_entry in self.app_models.itervalues():
-                model_list.extend(app_entry.values())
+            for the_app in self.app_instances:
+                model_list.extend(get_models(the_app))
             return model_list
 
     def get_model(self, app_label, model_name, seed_cache=True):
@@ -187,3 +255,5 @@
 register_models = cache.register_models
 load_app = cache.load_app
 app_cache_ready = cache.app_cache_ready
+find_app = cache.find_app
+get_app_label = cache.get_app_label
Index: django/db/models/__init__.py
===================================================================
--- django/db/models/__init__.py	(revision 9180)
+++ django/db/models/__init__.py	(working copy)
@@ -1,7 +1,7 @@
 from django.conf import settings
 from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
 from django.db import connection
-from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models
+from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models, find_app
 from django.db.models.query import Q
 from django.db.models.manager import Manager
 from django.db.models.base import Model
Index: django/core/management/commands/loaddata.py
===================================================================
--- django/core/management/commands/loaddata.py	(revision 9180)
+++ django/core/management/commands/loaddata.py	(working copy)
@@ -52,7 +52,7 @@
             transaction.enter_transaction_management()
             transaction.managed(True)
 
-        app_fixtures = [os.path.join(os.path.dirname(app.__file__), 'fixtures') for app in get_apps()]
+        app_fixtures = [os.path.join(os.path.dirname(app.app_module.__file__), 'fixtures') for app in get_apps() if app.app_module]
         for fixture_label in fixture_labels:
             parts = fixture_label.split('.')
             if len(parts) == 1:
Index: django/core/management/commands/flush.py
===================================================================
--- django/core/management/commands/flush.py	(revision 9180)
+++ django/core/management/commands/flush.py	(working copy)
@@ -1,3 +1,4 @@
+from django.conf import get_installed_app_paths
 from django.core.management.base import NoArgsCommand, CommandError
 from django.core.management.color import no_style
 from optparse import make_option
@@ -21,7 +22,7 @@
 
         # Import the 'management' module within each installed app, to register
         # dispatcher events.
-        for app_name in settings.INSTALLED_APPS:
+        for app_name in get_installed_app_paths():
             try:
                 __import__(app_name + '.management', {}, {}, [''])
             except ImportError:
Index: django/core/management/commands/syncdb.py
===================================================================
--- django/core/management/commands/syncdb.py	(revision 9180)
+++ django/core/management/commands/syncdb.py	(working copy)
@@ -1,3 +1,4 @@
+from django.conf import get_installed_app_paths
 from django.core.management.base import NoArgsCommand
 from django.core.management.color import no_style
 from optparse import make_option
@@ -28,7 +29,7 @@
 
         # Import the 'management' module within each installed app, to register
         # dispatcher events.
-        for app_name in settings.INSTALLED_APPS:
+        for app_name in get_installed_app_paths():
             try:
                 __import__(app_name + '.management', {}, {}, [''])
             except ImportError, exc:
@@ -55,7 +56,7 @@
 
         # Create the tables for each model
         for app in models.get_apps():
-            app_name = app.__name__.split('.')[-2]
+            app_name = app.label
             model_list = models.get_models(app)
             for model in model_list:
                 # Create the model's database table, if it doesn't already exist.
@@ -80,7 +81,7 @@
         # Create the m2m tables. This must be done after all tables have been created
         # to ensure that all referred tables will exist.
         for app in models.get_apps():
-            app_name = app.__name__.split('.')[-2]
+            app_name = app.label
             model_list = models.get_models(app)
             for model in model_list:
                 if model in created_models:
@@ -103,7 +104,7 @@
         # Install custom SQL for the app (but only if this
         # is a model we've just created)
         for app in models.get_apps():
-            app_name = app.__name__.split('.')[-2]
+            app_name = app.label
             for model in models.get_models(app):
                 if model in created_models:
                     custom_sql = custom_sql_for_model(model, self.style)
@@ -127,7 +128,7 @@
                             print "No custom SQL for %s.%s model" % (app_name, model._meta.object_name)
         # Install SQL indicies for all newly created models
         for app in models.get_apps():
-            app_name = app.__name__.split('.')[-2]
+            app_name = app.label
             for model in models.get_models(app):
                 if model in created_models:
                     index_sql = connection.creation.sql_indexes_for_model(model, self.style)
Index: django/core/management/__init__.py
===================================================================
--- django/core/management/__init__.py	(revision 9180)
+++ django/core/management/__init__.py	(working copy)
@@ -96,8 +96,8 @@
 
         # Find the installed apps
         try:
-            from django.conf import settings
-            apps = settings.INSTALLED_APPS
+            from django.conf import settings, get_installed_app_paths
+            apps = get_installed_app_paths()
         except (AttributeError, EnvironmentError, ImportError):
             apps = []
 
Index: django/core/management/sql.py
===================================================================
--- django/core/management/sql.py	(revision 9180)
+++ django/core/management/sql.py	(working copy)
@@ -137,7 +137,7 @@
     output = []
 
     app_models = get_models(app)
-    app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
+    app_dir = os.path.normpath(os.path.join(os.path.dirname(app.app_module.__file__), 'sql'))
 
     for model in app_models:
         output.extend(custom_sql_for_model(model, style))
@@ -161,7 +161,7 @@
     from django.conf import settings
 
     opts = model._meta
-    app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
+    app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).app_module.__file__), 'sql'))
     output = []
 
     # Post-creation SQL should come before any initial SQL data is loaded.
@@ -197,7 +197,7 @@
     from django.dispatch import dispatcher
     # Emit the post_sync signal for every application.
     for app in models.get_apps():
-        app_name = app.__name__.split('.')[-2]
+        app_name = app.label
         if verbosity >= 2:
             print "Running post-sync handlers for application", app_name
         models.signals.post_syncdb.send(sender=app, app=app,
Index: django/templatetags/__init__.py
===================================================================
--- django/templatetags/__init__.py	(revision 9180)
+++ django/templatetags/__init__.py	(working copy)
@@ -1,6 +1,6 @@
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 
-for a in settings.INSTALLED_APPS:
+for a in get_installed_app_paths():
     try:
         __path__.extend(__import__(a + '.templatetags', {}, {}, ['']).__path__)
     except ImportError:
Index: django/views/i18n.py
===================================================================
--- django/views/i18n.py	(revision 9180)
+++ django/views/i18n.py	(working copy)
@@ -1,7 +1,7 @@
 from django import http
 from django.utils.translation import check_for_language, activate, to_locale, get_language
 from django.utils.text import javascript_quote
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 import os
 import gettext as gettext_module
 
@@ -121,7 +121,7 @@
         packages = ['django.conf']
     if type(packages) in (str, unicode):
         packages = packages.split('+')
-    packages = [p for p in packages if p == 'django.conf' or p in settings.INSTALLED_APPS]
+    packages = [p for p in packages if p == 'django.conf' or p in get_installed_app_paths()]
     default_locale = to_locale(settings.LANGUAGE_CODE)
     locale = to_locale(get_language())
     t = {}
Index: django/utils/translation/trans_real.py
===================================================================
--- django/utils/translation/trans_real.py	(revision 9180)
+++ django/utils/translation/trans_real.py	(working copy)
@@ -114,7 +114,7 @@
     if t is not None:
         return t
 
-    from django.conf import settings
+    from django.conf import settings, get_installed_app_paths
 
     # set up the right translation class
     klass = DjangoTranslation
@@ -175,7 +175,7 @@
         if projectpath and os.path.isdir(projectpath):
             res = _merge(projectpath)
 
-        for appname in settings.INSTALLED_APPS:
+        for appname in get_installed_app_paths():
             p = appname.rfind('.')
             if p >= 0:
                 app = getattr(__import__(appname[:p], {}, {}, [appname[p+1:]]), appname[p+1:])
Index: django/contrib/comments/__init__.py
===================================================================
--- django/contrib/comments/__init__.py	(revision 9180)
+++ django/contrib/comments/__init__.py	(working copy)
@@ -1,4 +1,4 @@
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 from django.core import urlresolvers
 from django.core.exceptions import ImproperlyConfigured
 
@@ -11,7 +11,7 @@
     """
     # Make sure the app's in INSTALLED_APPS
     comments_app = get_comment_app_name()
-    if comments_app not in settings.INSTALLED_APPS:
+    if comments_app not in get_installed_app_paths():
         raise ImproperlyConfigured("The COMMENTS_APP (%r) "\
                                    "must be in INSTALLED_APPS" % settings.COMMENTS_APP)
 
Index: django/contrib/sites/management.py
===================================================================
--- django/contrib/sites/management.py	(revision 9180)
+++ django/contrib/sites/management.py	(working copy)
@@ -2,9 +2,8 @@
 Creates the default Site object.
 """
 
-from django.db.models import signals
+from django.db.models import signals, find_app
 from django.contrib.sites.models import Site
-from django.contrib.sites import models as site_app
 
 def create_default_site(app, created_models, verbosity, **kwargs):
     if Site in created_models:
@@ -14,4 +13,4 @@
         s.save()
     Site.objects.clear_cache()
 
-signals.post_syncdb.connect(create_default_site, sender=site_app)
+signals.post_syncdb.connect(create_default_site, sender=find_app('django.contrib.sites'))
Index: django/contrib/admin/__init__.py
===================================================================
--- django/contrib/admin/__init__.py	(revision 9180)
+++ django/contrib/admin/__init__.py	(working copy)
@@ -9,9 +9,9 @@
     may want.
     """
     import imp
-    from django.conf import settings
+    from django.conf import settings, get_installed_app_paths
 
-    for app in settings.INSTALLED_APPS:
+    for app in get_installed_app_paths():
         # For each app, we need to look for an admin.py inside that app's
         # package. We can't use os.path here -- recall that modules may be
         # imported different ways (think zip files) -- so we need to get
Index: django/contrib/contenttypes/management.py
===================================================================
--- django/contrib/contenttypes/management.py	(revision 9180)
+++ django/contrib/contenttypes/management.py	(working copy)
@@ -8,7 +8,7 @@
     entries that no longer have a matching model class.
     """
     ContentType.objects.clear_cache()
-    content_types = list(ContentType.objects.filter(app_label=app.__name__.split('.')[-2]))
+    content_types = list(ContentType.objects.filter(app_label=app.label))
     app_models = get_models(app)
     if not app_models:
         return
Index: django/template/loaders/app_directories.py
===================================================================
--- django/template/loaders/app_directories.py	(revision 9180)
+++ django/template/loaders/app_directories.py	(working copy)
@@ -5,14 +5,14 @@
 
 import os
 
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 from django.core.exceptions import ImproperlyConfigured
 from django.template import TemplateDoesNotExist
 from django.utils._os import safe_join
 
 # At compile time, cache the directories to search.
 app_template_dirs = []
-for app in settings.INSTALLED_APPS:
+for app in get_installed_app_paths():
     i = app.rfind('.')
     if i == -1:
         m, a = app, None
Index: django/template/loaders/eggs.py
===================================================================
--- django/template/loaders/eggs.py	(revision 9180)
+++ django/template/loaders/eggs.py	(working copy)
@@ -6,7 +6,7 @@
     resource_string = None
 
 from django.template import TemplateDoesNotExist
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 
 def load_template_source(template_name, template_dirs=None):
     """
@@ -16,7 +16,7 @@
     """
     if resource_string is not None:
         pkg_name = 'templates/' + template_name
-        for app in settings.INSTALLED_APPS:
+        for app in get_installed_app_paths():
             try:
                 return (resource_string(app, pkg_name).decode(settings.FILE_CHARSET), 'egg:%s:%s' % (app, pkg_name))
             except:
Index: tests/regressiontests/admin_scripts/tests.py
===================================================================
--- tests/regressiontests/admin_scripts/tests.py	(revision 9180)
+++ tests/regressiontests/admin_scripts/tests.py	(working copy)
@@ -969,9 +969,9 @@
         args = ['app_command', 'auth']
         out, err = self.run_manage(args)
         self.assertNoOutput(err)
-        self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
-        self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
-        self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
+        self.assertOutput(out, "EXECUTE:AppCommand app=<app: django.contrib.auth>")
+        #self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
+        self.assertOutput(out, ">, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
 
     def test_app_command_no_apps(self):
         "User AppCommands raise an error when no app name is provided"
@@ -984,12 +984,12 @@
         args = ['app_command','auth','contenttypes']
         out, err = self.run_manage(args)
         self.assertNoOutput(err)
-        self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
-        self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
-        self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
-        self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.contenttypes.models'")
-        self.assertOutput(out, os.sep.join(['django','contrib','contenttypes','models.py']))
-        self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
+        self.assertOutput(out, "EXECUTE:AppCommand app=<app: django.contrib.auth>")
+        #self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
+        self.assertOutput(out, ">, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
+        self.assertOutput(out, "EXECUTE:AppCommand app=<app: django.contrib.contenttypes>")
+        #self.assertOutput(out, os.sep.join(['django','contrib','contenttypes','models.py']))
+        self.assertOutput(out, ">, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
 
     def test_app_command_invalid_appname(self):
         "User AppCommands can execute when a single app name is provided"
Index: tests/runtests.py
===================================================================
--- tests/runtests.py	(revision 9180)
+++ tests/runtests.py	(working copy)
@@ -2,6 +2,7 @@
 
 import os, sys, traceback
 import unittest
+from django.conf import app, get_installed_app_paths
 
 import django.contrib as contrib
 
@@ -23,7 +24,8 @@
 
 ALWAYS_INSTALLED_APPS = [
     'django.contrib.contenttypes',
-    'django.contrib.auth',
+    #We need to use the same app label, otherwise fixtures will break...
+    app('django.contrib.auth', 'auth', 'Authentication'),
     'django.contrib.sites',
     'django.contrib.flatpages',
     'django.contrib.redirects',
@@ -58,13 +60,14 @@
 
     def runTest(self):
         from django.core.management.validation import get_validation_errors
-        from django.db.models.loading import load_app
+        from django.db.models.loading import load_app, find_app
         from cStringIO import StringIO
 
         try:
             module = load_app(self.model_label)
+            app = find_app(self.model_label)
         except Exception, e:
-            self.fail('Unable to load invalid model module')
+            self.fail('Unable to load invalid application %s: %s' % (self.model_label, e))
 
         # Make sure sys.stdout is not a tty so that we get errors without
         # coloring attached (makes matching the results easier). We restore
@@ -72,7 +75,7 @@
         orig_stdout = sys.stdout
         s = StringIO()
         sys.stdout = s
-        count = get_validation_errors(s, module)
+        count = get_validation_errors(s, app)
         sys.stdout = orig_stdout
         s.seek(0)
         error_log = s.read()
@@ -129,7 +132,7 @@
                     print "Importing model %s" % model_name
                 mod = load_app(model_label)
                 if mod:
-                    if model_label not in settings.INSTALLED_APPS:
+                    if model_label not in get_installed_app_paths():
                         settings.INSTALLED_APPS.append(model_label)
         except Exception, e:
             sys.stderr.write("Error while importing %s:" % model_name + ''.join(traceback.format_exception(*sys.exc_info())[1:]))
