Index: django/core/urlresolvers.py
===================================================================
--- django/core/urlresolvers.py	(revision 13431)
+++ django/core/urlresolvers.py	(working copy)
@@ -30,6 +30,34 @@
 # Overridden URLconfs for each thread are stored here.
 _urlconfs = {}
 
+class ResolverMatch(object):
+    def __init__(self, func, args, kwargs, url_name=None, app_name=None, namespaces=[]):
+        self.func = func
+        self.args = args
+        self.kwargs = kwargs
+        self.app_name = app_name
+        self.namespaces = [ x for x in namespaces if x ]
+        if not url_name:
+            url_name = '.'.join([ func.__module__, func.__name__ ])
+        self.url_name = url_name
+
+    def namespace(self):
+        return ':'.join(self.namespaces)
+    namespace = property(namespace)
+
+    def view_name(self):
+        return ':'.join([ x for x in [ self.namespace, self.url_name ]  if x ])
+    view_name = property(view_name)
+
+    def __getitem__(self, index):
+        return (self.func, self.args, self.kwargs)[index]
+
+    def __repr__(self):
+        return '(%s, %s, %s)' % (self.func, self.args, self.kwargs)
+
+    def reverse(self):
+        return reverse(self.view_name, args=self.args, kwargs=self.kwargs, current_app=self.app_name)
+
 class Resolver404(Http404):
     pass
 
@@ -120,7 +148,7 @@
             # In both cases, pass any extra_kwargs as **kwargs.
             kwargs.update(self.default_args)
 
-            return self.callback, args, kwargs
+            return ResolverMatch(self.callback, args, kwargs, self.name)
 
     def _get_callback(self):
         if self._callback is not None:
@@ -224,9 +252,9 @@
                     if sub_match:
                         sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()])
                         sub_match_dict.update(self.default_kwargs)
-                        for k, v in sub_match[2].iteritems():
+                        for k, v in sub_match.kwargs.iteritems():
                             sub_match_dict[smart_str(k)] = v
-                        return sub_match[0], sub_match[1], sub_match_dict
+                        return ResolverMatch(sub_match.func, sub_match.args, sub_match_dict, sub_match.url_name, self.app_name or sub_match.app_name, [ self.namespace ] + sub_match.namespaces)
                     tried.append(pattern.regex.pattern)
             raise Resolver404({'tried': tried, 'path': new_path})
         raise Resolver404({'path' : path})
Index: tests/regressiontests/urlpatterns_reverse/tests.py
===================================================================
--- tests/regressiontests/urlpatterns_reverse/tests.py	(revision 13431)
+++ tests/regressiontests/urlpatterns_reverse/tests.py	(working copy)
@@ -18,7 +18,7 @@
 
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404
+from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404, ResolverMatch
 from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
 from django.shortcuts import redirect
 from django.test import TestCase
@@ -26,7 +26,23 @@
 import urlconf_outer
 import urlconf_inner
 import middleware
+import views
 
+resolve_test_data = (
+    # These entries are in the format: (path, url_name, app_name, namespace, view_func, args, kwargs)
+    ('/normal/42/37/', 'normal-view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}),
+    ('/included/normal/42/37/', 'inc-normal-view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}),
+    ('/test1/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns1', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+    ('/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+    ('/ns-included1/normal/42/37/', 'inc-normal-view', None, 'inc-ns1', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}),
+    ('/ns-included1/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+    ('/default/inner/42/37/', 'urlobject-view', 'testapp', 'testapp', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+    ('/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+    ('/other2/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns2', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+    ('/other1/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns1', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+    ('/ns-included1/ns-included4/ns-included2/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:inc-ns4:inc-ns2:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+    )
+
 test_data = (
     ('places', '/places/3/', [3], {}),
     ('places', '/places/3/', ['3'], {}),
@@ -229,6 +245,12 @@
         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_nested_namespace_pattern(self):
+        "Namespaces can be nested"
+        self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view'))
+        self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/37/42/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view', args=[37,42]))
+        self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/42/37/', reverse('inc-ns1:inc-ns4: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'))
@@ -317,3 +339,24 @@
 
     def test_no_handler_exception(self):
         self.assertRaises(ImproperlyConfigured, self.client.get, '/test/me/')
+
+class ResolverMatchTests(TestCase):
+    urls = 'regressiontests.urlpatterns_reverse.namespace_urls'
+
+    def test_urlpattern_resolve(self):
+        for path, name, app_name, namespace, func, args, kwargs in resolve_test_data:
+            # Test legacy support for extracting "function, args, kwargs"
+            match_func, match_args, match_kwargs = resolve(path)
+            self.assertEqual(match_func, func)
+            self.assertEqual(match_args, args)
+            self.assertEqual(match_kwargs, kwargs)
+
+            # Test ResolverMatch capabilities.
+            match = resolve(path)
+            self.assertEqual(match.__class__, ResolverMatch)
+            self.assertEqual(match.url_name, name)
+            self.assertEqual(match.args, args)
+            self.assertEqual(match.kwargs, kwargs)
+            self.assertEqual(match.app_name, app_name)
+            self.assertEqual(match.namespace, namespace)
+            self.assertEqual(match.func, func)
Index: tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py
===================================================================
--- tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py	(revision 13431)
+++ tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py	(working copy)
@@ -9,5 +9,6 @@
 
     (r'^test3/', include(testobj3.urls)),
     (r'^ns-included3/', include('regressiontests.urlpatterns_reverse.included_urls', namespace='inc-ns3')),
+    (r'^ns-included4/', include('regressiontests.urlpatterns_reverse.namespace_urls', namespace='inc-ns4')),
 )
 
Index: docs/topics/http/urls.txt
===================================================================
--- docs/topics/http/urls.txt	(revision 13431)
+++ docs/topics/http/urls.txt	(working copy)
@@ -858,6 +858,29 @@
             return HttpResponseRedirect('/')
         return response
 
+.. versionchanged:: 1.3
+
+The :func:`django.core.urlresolvers.resolve` function returns a
+:class:`django.core.urlresolvers.ResolverMatch` object that allows you to
+access various meta-data about the resolved URL including (url_name, app_name,
+namespace, func, args, kwargs).
+
+There are two ways to access the results of
+:func:`django.core.urlresolvers.resolve` in order to maintain backwards
+compatibility::
+
+    # Pre-Django 1.3 support for list expansion.
+    func, args, kwargs = resolve('/some/path')
+
+    # ResolverMatch result.
+    match = resolve('/some/path')
+    match.func
+    match.args
+    match.kwargs
+    match.url_name
+    match.app_name
+    match.namespace
+
 permalink()
 -----------
 
