diff --git a/django/conf/urls/defaults.py b/django/conf/urls/defaults.py
index 26cdd3e..572a7b0 100644
--- a/django/conf/urls/defaults.py
+++ b/django/conf/urls/defaults.py
@@ -6,7 +6,16 @@ __all__ = ['handler404', 'handler500', 'include', 'patterns', 'url']
 handler404 = 'django.views.defaults.page_not_found'
 handler500 = 'django.views.defaults.server_error'
 
-include = lambda urlconf_module: [urlconf_module]
+def include(arg, namespace=None, app_name=None):
+    if type(arg) == tuple:
+        # callable returning a namespace hint
+        if namespace:
+            raise ImproperlyConfigured('Cannot override the namespace for a dynamic module that provides a namespace')
+        urlconf_module, app_name, namespace = arg
+    else:
+        # No namespace hint - use manually provided namespace
+        urlconf_module = arg
+    return (urlconf_module, app_name, namespace)
 
 def patterns(prefix, *args):
     pattern_list = []
@@ -19,9 +28,10 @@ def patterns(prefix, *args):
     return pattern_list
 
 def url(regex, view, kwargs=None, name=None, prefix=''):
-    if type(view) == list:
+    if type(view) == tuple:
         # For include(...) processing.
-        return RegexURLResolver(regex, view[0], kwargs)
+        urlconf_module, app_name, namespace = view
+        return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace)
     else:
         if isinstance(view, basestring):
             if not view:
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 8297eca..0545409 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -226,24 +226,24 @@ class ModelAdmin(BaseModelAdmin):
                 return self.admin_site.admin_view(view)(*args, **kwargs)
             return update_wrapper(wrapper, view)
 
-        info = self.admin_site.name, self.model._meta.app_label, self.model._meta.module_name
+        info = self.model._meta.app_label, self.model._meta.module_name
 
         urlpatterns = patterns('',
             url(r'^$',
                 wrap(self.changelist_view),
-                name='%sadmin_%s_%s_changelist' % info),
+                name='%s_%s_changelist' % info),
             url(r'^add/$',
                 wrap(self.add_view),
-                name='%sadmin_%s_%s_add' % info),
+                name='%s_%s_add' % info),
             url(r'^(.+)/history/$',
                 wrap(self.history_view),
-                name='%sadmin_%s_%s_history' % info),
+                name='%s_%s_history' % info),
             url(r'^(.+)/delete/$',
                 wrap(self.delete_view),
-                name='%sadmin_%s_%s_delete' % info),
+                name='%s_%s_delete' % info),
             url(r'^(.+)/$',
                 wrap(self.change_view),
-                name='%sadmin_%s_%s_change' % info),
+                name='%s_%s_change' % info),
         )
         return urlpatterns
 
@@ -582,11 +582,12 @@ class ModelAdmin(BaseModelAdmin):
             'save_on_top': self.save_on_top,
             'root_path': self.admin_site.root_path,
         })
+        context_instance = template.RequestContext(request, app_name=self.admin_site.name)
         return render_to_response(self.change_form_template or [
             "admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
             "admin/%s/change_form.html" % app_label,
             "admin/change_form.html"
-        ], context, context_instance=template.RequestContext(request))
+        ], context, context_instance=context_instance)
 
     def response_add(self, request, obj, post_url_continue='../%s/'):
         """
@@ -977,11 +978,12 @@ class ModelAdmin(BaseModelAdmin):
             'actions_on_bottom': self.actions_on_bottom,
         }
         context.update(extra_context or {})
+        context_instance = template.RequestContext(request, app_name=self.admin_site.name)
         return render_to_response(self.change_list_template or [
             'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()),
             'admin/%s/change_list.html' % app_label,
             'admin/change_list.html'
-        ], context, context_instance=template.RequestContext(request))
+        ], context, context_instance=context_instance)
 
     def delete_view(self, request, object_id, extra_context=None):
         "The 'delete' admin view for this model."
@@ -1032,11 +1034,12 @@ class ModelAdmin(BaseModelAdmin):
             "app_label": app_label,
         }
         context.update(extra_context or {})
+        context_instance = template.RequestContext(request, app_name=self.admin_site.name)
         return render_to_response(self.delete_confirmation_template or [
             "admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()),
             "admin/%s/delete_confirmation.html" % app_label,
             "admin/delete_confirmation.html"
-        ], context, context_instance=template.RequestContext(request))
+        ], context, context_instance=context_instance)
 
     def history_view(self, request, object_id, extra_context=None):
         "The 'history' admin view for this model."
@@ -1059,11 +1062,12 @@ class ModelAdmin(BaseModelAdmin):
             'app_label': app_label,
         }
         context.update(extra_context or {})
+        context_instance = template.RequestContext(request, app_name=self.admin_site.name)
         return render_to_response(self.object_history_template or [
             "admin/%s/%s/object_history.html" % (app_label, opts.object_name.lower()),
             "admin/%s/object_history.html" % app_label,
             "admin/object_history.html"
-        ], context, context_instance=template.RequestContext(request))
+        ], context, context_instance=context_instance)
 
     #
     # DEPRECATED methods.
diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
index 6e9ef11..964719e 100644
--- a/django/contrib/admin/sites.py
+++ b/django/contrib/admin/sites.py
@@ -38,17 +38,14 @@ class AdminSite(object):
     login_template = None
     app_index_template = None
 
-    def __init__(self, name=None):
+    def __init__(self, name=None, app_name='admin'):
         self._registry = {} # model_class class -> admin_class instance
-        # TODO Root path is used to calculate urls under the old root() method
-        # in order to maintain backwards compatibility we are leaving that in
-        # so root_path isn't needed, not sure what to do about this.
-        self.root_path = 'admin/'
+        self.root_path = None
         if name is None:
-            name = ''
+            self.name = 'admin'
         else:
-            name += '_'
-        self.name = name
+            self.name = name
+        self.app_name = app_name
         self._actions = {'delete_selected': actions.delete_selected}
         self._global_actions = self._actions.copy()
 
@@ -114,20 +111,20 @@ class AdminSite(object):
         name = name or action.__name__
         self._actions[name] = action
         self._global_actions[name] = action
-        
+
     def disable_action(self, name):
         """
         Disable a globally-registered action. Raises KeyError for invalid names.
         """
         del self._actions[name]
-        
+
     def get_action(self, name):
         """
         Explicitally get a registered global action wheather it's enabled or
         not. Raises KeyError for invalid names.
         """
         return self._global_actions[name]
-    
+
     def actions(self):
         """
         Get all the enabled actions as an iterable of (name, func).
@@ -186,7 +183,6 @@ class AdminSite(object):
 
     def get_urls(self):
         from django.conf.urls.defaults import patterns, url, include
-
         def wrap(view):
             def wrapper(*args, **kwargs):
                 return self.admin_view(view)(*args, **kwargs)
@@ -196,24 +192,24 @@ class AdminSite(object):
         urlpatterns = patterns('',
             url(r'^$',
                 wrap(self.index),
-                name='%sadmin_index' % self.name),
+                name='index'),
             url(r'^logout/$',
                 wrap(self.logout),
-                name='%sadmin_logout'),
+                name='logout'),
             url(r'^password_change/$',
                 wrap(self.password_change),
-                name='%sadmin_password_change' % self.name),
+                name='password_change'),
             url(r'^password_change/done/$',
                 wrap(self.password_change_done),
-                name='%sadmin_password_change_done' % self.name),
+                name='password_change_done'),
             url(r'^jsi18n/$',
                 wrap(self.i18n_javascript),
-                name='%sadmin_jsi18n' % self.name),
+                name='jsi18n'),
             url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
                 'django.views.defaults.shortcut'),
             url(r'^(?P<app_label>\w+)/$',
                 wrap(self.app_index),
-                name='%sadmin_app_list' % self.name),
+                name='app_list')
         )
 
         # Add in each model's views.
@@ -222,7 +218,7 @@ class AdminSite(object):
                 url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
                     include(model_admin.urls))
             )
-        return urlpatterns
+        return urlpatterns, self.app_name, self.name
 
     def urls(self):
         return self.get_urls()
@@ -233,8 +229,11 @@ class AdminSite(object):
         Handles the "change password" task -- both form display and validation.
         """
         from django.contrib.auth.views import password_change
-        return password_change(request,
-            post_change_redirect='%spassword_change/done/' % self.root_path)
+        if self.root_path is not None:
+            url = '%spassword_change/done/' % self.root_path
+        else:
+            url = reverse('%s:password_change_done' % self.name)
+        return password_change(request, post_change_redirect=url)
 
     def password_change_done(self, request):
         """
@@ -362,8 +361,9 @@ class AdminSite(object):
             'root_path': self.root_path,
         }
         context.update(extra_context or {})
+        context_instance = template.RequestContext(request, app_name=self.name)
         return render_to_response(self.index_template or 'admin/index.html', context,
-            context_instance=template.RequestContext(request)
+            context_instance=context_instance
         )
     index = never_cache(index)
 
@@ -376,8 +376,9 @@ class AdminSite(object):
             'root_path': self.root_path,
         }
         context.update(extra_context or {})
+        context_instance = template.RequestContext(request, app_name=self.name)
         return render_to_response(self.login_template or 'admin/login.html', context,
-            context_instance=template.RequestContext(request)
+            context_instance=context_instance
         )
 
     def app_index(self, request, app_label, extra_context=None):
@@ -419,9 +420,10 @@ class AdminSite(object):
             'root_path': self.root_path,
         }
         context.update(extra_context or {})
+        context_instance = template.RequestContext(request, app_name=self.name)
         return render_to_response(self.app_index_template or ('admin/%s/app_index.html' % app_label,
             'admin/app_index.html'), context,
-            context_instance=template.RequestContext(request)
+            context_instance=context_instance
         )
 
     def root(self, request, url):
diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html
index 8cab439..9525728 100644
--- a/django/contrib/admin/templates/admin/base.html
+++ b/django/contrib/admin/templates/admin/base.html
@@ -23,7 +23,30 @@
         {% block branding %}{% endblock %}
         </div>
         {% if user.is_authenticated and user.is_staff %}
-        <div id="user-tools">{% trans 'Welcome,' %} <strong>{% firstof user.first_name user.username %}</strong>. {% block userlinks %}{% url django-admindocs-docroot as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %}<a href="{{ root_path }}password_change/">{% trans 'Change password' %}</a> / <a href="{{ root_path }}logout/">{% trans 'Log out' %}</a>{% endblock %}</div>
+        <div id="user-tools">
+            {% trans 'Welcome,' %}
+            <strong>{% firstof user.first_name user.username %}</strong>.
+            {% block userlinks %}
+                {% url django-admindocs-docroot as docsroot %}
+                {% if docsroot %}
+                    <a href="{{ docsroot }}">{% trans 'Documentation' %}</a> /
+                {% endif %}
+                {% url admin:password_change as password_change_url %}
+                {% if password_change_url %}
+                    <a href="{{ password_change_url }}">
+                {% else %}
+                    <a href="{{ root_path }}password_change/">
+                {% endif %}
+                {% trans 'Change password' %}</a> /
+                {% url admin:logout as logout_url %}
+                {% if logout_url %}
+                    <a href="{{ logout_url }}">
+                {% else %}
+                    <a href="{{ root_path }}logout/">
+                {% endif %}
+                {% trans 'Log out' %}</a>
+            {% endblock %}
+        </div>
         {% endif %}
         {% block nav-global %}{% endblock %}
     </div>
diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
index 10e97bb..6b41659 100644
--- a/django/core/urlresolvers.py
+++ b/django/core/urlresolvers.py
@@ -139,7 +139,7 @@ class RegexURLPattern(object):
     callback = property(_get_callback)
 
 class RegexURLResolver(object):
-    def __init__(self, regex, urlconf_name, default_kwargs=None):
+    def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
         # regex is a string representing a regular expression.
         # urlconf_name is a string representing the module containing URLconfs.
         self.regex = re.compile(regex, re.UNICODE)
@@ -148,19 +148,29 @@ class RegexURLResolver(object):
             self._urlconf_module = self.urlconf_name
         self.callback = None
         self.default_kwargs = default_kwargs or {}
-        self._reverse_dict = MultiValueDict()
+        self.namespace = namespace
+        self.app_name = app_name
+        self._reverse_dict = None
+        self._namespace_dict = None
+        self._app_dict = None
 
     def __repr__(self):
-        return '<%s %s %s>' % (self.__class__.__name__, self.urlconf_name, self.regex.pattern)
-
-    def _get_reverse_dict(self):
-        if not self._reverse_dict:
-            lookups = MultiValueDict()
-            for pattern in reversed(self.url_patterns):
-                p_pattern = pattern.regex.pattern
-                if p_pattern.startswith('^'):
-                    p_pattern = p_pattern[1:]
-                if isinstance(pattern, RegexURLResolver):
+        return '<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern)
+
+    def _populate(self):
+        lookups = MultiValueDict()
+        namespaces = {}
+        apps = {}
+        for pattern in reversed(self.url_patterns):
+            p_pattern = pattern.regex.pattern
+            if p_pattern.startswith('^'):
+                p_pattern = p_pattern[1:]
+            if isinstance(pattern, RegexURLResolver):
+                if pattern.namespace:
+                    namespaces[pattern.namespace] = (p_pattern, pattern)
+                    if pattern.app_name:
+                        apps.setdefault(pattern.app_name, []).append(pattern.namespace)
+                else:
                     parent = normalize(pattern.regex.pattern)
                     for name in pattern.reverse_dict:
                         for matches, pat in pattern.reverse_dict.getlist(name):
@@ -168,14 +178,36 @@ class RegexURLResolver(object):
                             for piece, p_args in parent:
                                 new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches])
                             lookups.appendlist(name, (new_matches, p_pattern + pat))
-                else:
-                    bits = normalize(p_pattern)
-                    lookups.appendlist(pattern.callback, (bits, p_pattern))
-                    lookups.appendlist(pattern.name, (bits, p_pattern))
-            self._reverse_dict = lookups
+                    for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items():
+                        namespaces[namespace] = (p_pattern + prefix, sub_pattern)
+                    for app_name, namespace_list in pattern.app_dict.items():
+                        apps.setdefault(app_name, []).extend(namespace_list)
+            else:
+                bits = normalize(p_pattern)
+                lookups.appendlist(pattern.callback, (bits, p_pattern))
+                lookups.appendlist(pattern.name, (bits, p_pattern))
+        self._reverse_dict = lookups
+        self._namespace_dict = namespaces
+        self._app_dict = apps
+
+    def _get_reverse_dict(self):
+        if self._reverse_dict is None:
+            self._populate()
         return self._reverse_dict
     reverse_dict = property(_get_reverse_dict)
 
+    def _get_namespace_dict(self):
+        if self._namespace_dict is None:
+            self._populate()
+        return self._namespace_dict
+    namespace_dict = property(_get_namespace_dict)
+
+    def _get_app_dict(self):
+        if self._app_dict is None:
+            self._populate()
+        return self._app_dict
+    app_dict = property(_get_app_dict)
+
     def resolve(self, path):
         tried = []
         match = self.regex.search(path)
@@ -261,12 +293,48 @@ class RegexURLResolver(object):
 def resolve(path, urlconf=None):
     return get_resolver(urlconf).resolve(path)
 
-def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
+def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, app_name=None):
+    print "REVERSE",viewname,app_name
+    parts = viewname.split(':')
+    parts.reverse()
+    view = parts[0]
+    path = parts[1:]
+
     args = args or []
     kwargs = kwargs or {}
     if prefix is None:
         prefix = get_script_prefix()
-    return iri_to_uri(u'%s%s' % (prefix, get_resolver(urlconf).reverse(viewname,
+
+    resolver = get_resolver(urlconf)
+    resolver._populate()
+    resolved_path = []
+    while path:
+        ns = path.pop()
+        # Lookup the name to see if it could be an app identifier
+        try:
+            app_list = resolver.app_dict[ns]
+            # Yes! Path part matches an app in the current Resolver
+            if app_name and app_name in app_list:
+                # If we are reversing for a particular app, use that namespace
+                ns = app_name
+            elif ns not in app_list:
+                # The name isn't shared by one of the instances (i.e., the default)
+                # so just pick the first instance as the default.
+                ns = app_list[0]
+        except KeyError:
+            pass
+
+        try:
+            extra, resolver = resolver.namespace_dict[ns]
+            resolved_path.append(ns)
+            prefix = prefix + extra
+        except KeyError, key:
+            if resolved_path:
+                raise NoReverseMatch("%s is not a registered namespace inside '%s'" % (key, ':'.join(resolved_path)))
+            else:
+                raise NoReverseMatch("%s is not a registered namespace" % key)
+
+    return iri_to_uri(u'%s%s' % (prefix, resolver.reverse(view,
             *args, **kwargs)))
 
 def clear_url_caches():
diff --git a/django/template/context.py b/django/template/context.py
index 0ccb5fa..e79af4d 100644
--- a/django/template/context.py
+++ b/django/template/context.py
@@ -9,10 +9,11 @@ class ContextPopException(Exception):
 
 class Context(object):
     "A stack container for variable context"
-    def __init__(self, dict_=None, autoescape=True):
+    def __init__(self, dict_=None, autoescape=True, app_name=None):
         dict_ = dict_ or {}
         self.dicts = [dict_]
         self.autoescape = autoescape
+        self.app_name = app_name
 
     def __repr__(self):
         return repr(self.dicts)
@@ -96,8 +97,8 @@ class RequestContext(Context):
     Additional processors can be specified as a list of callables
     using the "processors" keyword argument.
     """
-    def __init__(self, request, dict=None, processors=None):
-        Context.__init__(self, dict)
+    def __init__(self, request, dict=None, processors=None, app_name=None):
+        Context.__init__(self, dict, app_name=app_name)
         if processors is None:
             processors = ()
         else:
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index 7d91cd6..a61e965 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -367,17 +367,17 @@ class URLNode(Node):
         # {% url ... as var %} construct in which cause return nothing.
         url = ''
         try:
-            url = reverse(self.view_name, args=args, kwargs=kwargs)
+            url = reverse(self.view_name, args=args, kwargs=kwargs, app_name=context.app_name)
         except NoReverseMatch, e:
             if settings.SETTINGS_MODULE:
                 project_name = settings.SETTINGS_MODULE.split('.')[0]
                 try:
                     url = reverse(project_name + '.' + self.view_name,
-                              args=args, kwargs=kwargs)
+                              args=args, kwargs=kwargs, app_name=context.app_name)
                 except NoReverseMatch:
                     if self.asvar is None:
                         # Re-raise the original exception, not the one with
-                        # the path relative to the project. This makes a 
+                        # the path relative to the project. This makes a
                         # better error message.
                         raise e
             else:
diff --git a/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py
new file mode 100644
index 0000000..08b8567
--- /dev/null
+++ b/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py
@@ -0,0 +1,13 @@
+from django.conf.urls.defaults import *
+from namespace_urls import URLObject
+
+testobj3 = URLObject('test-ns3')
+
+urlpatterns = patterns('regressiontests.urlpatterns_reverse.views',
+    url(r'^normal/$', 'empty_view', name='inc-normal-view'),
+    url(r'^normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='inc-normal-view'),
+
+    (r'^test3/', include(testobj3.urls)),
+    (r'^ns-included3/', include('regressiontests.urlpatterns_reverse.included_urls', namespace='inc-ns3')),
+)
+
diff --git a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
new file mode 100644
index 0000000..db00e49
--- /dev/null
+++ b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
@@ -0,0 +1,31 @@
+from django.conf.urls.defaults import *
+
+class URLObject(object):
+    def __init__(self, namespace):
+        self.namespace = namespace
+
+    def urls(self):
+        return patterns('',
+            url(r'^inner/$', 'empty_view', name='urlobject-view'),
+            url(r'^inner/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='urlobject-view'),
+        ), 'testapp', self.namespace
+    urls = property(urls)
+
+testobj1 = URLObject('test-ns1')
+testobj2 = URLObject('test-ns2')
+default_testobj = URLObject('testapp')
+
+urlpatterns = patterns('regressiontests.urlpatterns_reverse.views',
+    url(r'^normal/$', 'empty_view', name='normal-view'),
+    url(r'^normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='normal-view'),
+
+    (r'^test1/', include(testobj1.urls)),
+    (r'^test2/', include(testobj2.urls)),
+    (r'^default/', include(default_testobj.urls)),
+
+    (r'^ns-included1/', include('regressiontests.urlpatterns_reverse.included_namespace_urls', namespace='inc-ns1')),
+    (r'^ns-included2/', include('regressiontests.urlpatterns_reverse.included_namespace_urls', namespace='inc-ns2')),
+
+    (r'^included/', include('regressiontests.urlpatterns_reverse.included_namespace_urls')),
+
+)
diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py
index 9def6b2..cb6e22a 100644
--- a/tests/regressiontests/urlpatterns_reverse/tests.py
+++ b/tests/regressiontests/urlpatterns_reverse/tests.py
@@ -158,4 +158,74 @@ class ReverseShortcutTests(TestCase):
         res = redirect('/foo/')
         self.assertEqual(res['Location'], '/foo/')
         res = redirect('http://example.com/')
-        self.assertEqual(res['Location'], 'http://example.com/')
\ No newline at end of file
+        self.assertEqual(res['Location'], 'http://example.com/')
+
+
+class NamespaceTests(TestCase):
+    urls = 'regressiontests.urlpatterns_reverse.namespace_urls'
+
+    def test_ambiguous_object(self):
+        "Names deployed via dynamic URL objects that require namespaces can't be resolved"
+        self.assertRaises(NoReverseMatch, reverse, 'urlobject-view')
+        self.assertRaises(NoReverseMatch, reverse, 'urlobject-view', args=[37,42])
+        self.assertRaises(NoReverseMatch, reverse, 'urlobject-view', kwargs={'arg1':42, 'arg2':37})
+
+    def test_ambiguous_urlpattern(self):
+        "Names deployed via dynamic URL objects that require namespaces can't be resolved"
+        self.assertRaises(NoReverseMatch, reverse, 'inner-nothing')
+        self.assertRaises(NoReverseMatch, reverse, 'inner-nothing', args=[37,42])
+        self.assertRaises(NoReverseMatch, reverse, 'inner-nothing', kwargs={'arg1':42, 'arg2':37})
+
+    def test_non_existent_namespace(self):
+        "Non-existent namespaces raise errors"
+        self.assertRaises(NoReverseMatch, reverse, 'blahblah:urlobject-view')
+        self.assertRaises(NoReverseMatch, reverse, 'test-ns1:blahblah:urlobject-view')
+
+    def test_normal_name(self):
+        "Normal lookups work as expected"
+        self.assertEquals('/normal/', reverse('normal-view'))
+        self.assertEquals('/normal/37/42/', reverse('normal-view', args=[37,42]))
+        self.assertEquals('/normal/42/37/', reverse('normal-view', kwargs={'arg1':42, 'arg2':37}))
+
+    def test_simple_included_name(self):
+        "Normal lookups work on names included from other patterns"
+        self.assertEquals('/included/normal/', reverse('inc-normal-view'))
+        self.assertEquals('/included/normal/37/42/', reverse('inc-normal-view', args=[37,42]))
+        self.assertEquals('/included/normal/42/37/', reverse('inc-normal-view', kwargs={'arg1':42, 'arg2':37}))
+
+    def test_namespace_object(self):
+        "Dynamic URL objects can be found using a namespace"
+        self.assertEquals('/test1/inner/', reverse('test-ns1:urlobject-view'))
+        self.assertEquals('/test1/inner/37/42/', reverse('test-ns1:urlobject-view', args=[37,42]))
+        self.assertEquals('/test1/inner/42/37/', reverse('test-ns1:urlobject-view', kwargs={'arg1':42, 'arg2':37}))
+
+    def test_embedded_namespace_object(self):
+        "Namespaces can be installed anywhere in the URL pattern tree"
+        self.assertEquals('/included/test3/inner/', reverse('test-ns3:urlobject-view'))
+        self.assertEquals('/included/test3/inner/37/42/', reverse('test-ns3:urlobject-view', args=[37,42]))
+        self.assertEquals('/included/test3/inner/42/37/', reverse('test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37}))
+
+    def test_namespace_pattern(self):
+        "Namespaces can be applied to include()'d urlpatterns"
+        self.assertEquals('/ns-included1/normal/', reverse('inc-ns1:inc-normal-view'))
+        self.assertEquals('/ns-included1/normal/37/42/', reverse('inc-ns1:inc-normal-view', args=[37,42]))
+        self.assertEquals('/ns-included1/normal/42/37/', reverse('inc-ns1:inc-normal-view', kwargs={'arg1':42, 'arg2':37}))
+
+    def test_multiple_namespace_pattern(self):
+        "Namespaces can be embedded"
+        self.assertEquals('/ns-included1/test3/inner/', reverse('inc-ns1:test-ns3:urlobject-view'))
+        self.assertEquals('/ns-included1/test3/inner/37/42/', reverse('inc-ns1:test-ns3:urlobject-view', args=[37,42]))
+        self.assertEquals('/ns-included1/test3/inner/42/37/', reverse('inc-ns1:test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37}))
+
+    def test_app_lookup_object(self):
+        "A default application namespace can be used for lookup"
+        self.assertEquals('/default/inner/', reverse('testapp:urlobject-view'))
+        self.assertEquals('/default/inner/37/42/', reverse('testapp:urlobject-view', args=[37,42]))
+        self.assertEquals('/default/inner/42/37/', reverse('testapp:urlobject-view', kwargs={'arg1':42, 'arg2':37}))
+
+    def test_app_lookup_object_with_default(self):
+        "A default application namespace is sensitive to the 'current' app can be used for lookup"
+        self.assertEquals('/included/test3/inner/', reverse('testapp:urlobject-view', app_name='test-ns3'))
+        self.assertEquals('/included/test3/inner/37/42/', reverse('testapp:urlobject-view', args=[37,42], app_name='test-ns3'))
+        self.assertEquals('/included/test3/inner/42/37/', reverse('testapp:urlobject-view', kwargs={'arg1':42, 'arg2':37}, app_name='test-ns3'))
+
