summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Rossi <chris@archimedeanco.com>2010-11-10 16:39:07 -0500
committerChris Rossi <chris@archimedeanco.com>2010-11-10 16:39:07 -0500
commit51c305c772150a8ee8b12c5dbdcb1fc30cb3a251 (patch)
treebf170d70e84018900cb01e1307dda20f17328659
parentacae5b33a825d1739dd76672e10f5c9f66f783a6 (diff)
downloadpyramid-51c305c772150a8ee8b12c5dbdcb1fc30cb3a251.tar.gz
pyramid-51c305c772150a8ee8b12c5dbdcb1fc30cb3a251.tar.bz2
pyramid-51c305c772150a8ee8b12c5dbdcb1fc30cb3a251.zip
Added class vars matchdict and matched_route to
pyramid.request.Request. Each is set to None.
-rw-r--r--CHANGES.txt5
-rw-r--r--pyramid/configuration.py36
-rw-r--r--pyramid/request.py2
-rw-r--r--pyramid/tests/test_configuration.py87
-rw-r--r--pyramid/tests/test_request.py14
-rw-r--r--pyramid/tests/test_traversal.py19
-rw-r--r--pyramid/traversal.py9
7 files changed, 94 insertions, 78 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 4354f5c7c..6bfb47054 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -8,6 +8,9 @@ Features
represent the function that returns a WSGI application, each now uses
WebError, each now has roughly the same shape of development.ini style.
+- Added class vars ``matchdict`` and ``matched_route`` to
+ ``pyramid.request.Request``. Each is set to ``None``.
+
Bug Fixes
---------
@@ -154,7 +157,7 @@ Backwards Incompatibilities (with BFG 1.3)
- The deprecated API ``pyramid.testing.registerViewPermission``
has been removed.
-- The deprecated API named ``pyramid.testing.registerRoutesMapper``
+- The deprecated API named ``pyramid.testing.registerRoutesMapper``
has been removed.
- The deprecated API named ``pyramid.request.get_request`` was removed.
diff --git a/pyramid/configuration.py b/pyramid/configuration.py
index 1abd11b3a..0a3041b9d 100644
--- a/pyramid/configuration.py
+++ b/pyramid/configuration.py
@@ -263,7 +263,7 @@ class Configurator(object):
policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, IAuthenticationPolicy,
info=_info)
-
+
def _set_authorization_policy(self, policy, _info=u''):
""" Add a :app:`Pyramid` :term:`authorization policy` to
the current configuration state (also accepts a :term:`dotted
@@ -472,7 +472,7 @@ class Configurator(object):
def derive_view(self, view, attr=None, renderer=None):
"""
-
+
Create a :term:`view callable` using the function, instance,
or class (or :term:`dotted Python name` referring to the same)
provided as ``view`` object.
@@ -582,7 +582,7 @@ class Configurator(object):
config.add_settings({'external_uri':'http://example.com'})
Or a set of key/value pairs::
-
+
config.add_settings(external_uri='http://example.com')
This function is useful when you need to test code that calls
@@ -658,7 +658,7 @@ class Configurator(object):
""" Add a Pylons-style view handler. This function adds a
route and some number of views based on a handler object
(usually a class).
-
+
``route_name`` is the name of the route (to be used later in
URL generation).
@@ -668,7 +668,7 @@ class Configurator(object):
``route_name`` is used. If ``pattern`` is ``None`` and no
route named ``route_name`` exists, a ``ConfigurationError`` is
raised.
-
+
``handler`` is a dotted name of (or direct reference to) a
Python handler class,
e.g. ``'my.package.handlers.MyHandler'``.
@@ -698,7 +698,7 @@ class Configurator(object):
'The "pattern" parameter may only be "None" when a route '
'with the route_name argument was previously registered. '
'No such route named %r exists' % route_name)
-
+
pattern = route.pattern
path_has_action = ':action' in pattern or '{action}' in pattern
@@ -735,7 +735,7 @@ class Configurator(object):
method_name = action
if method_name is None:
method_name = '__call__'
-
+
# Scan the controller for any other methods with this action name
for meth_name, method in inspect.getmembers(
handler, inspect.ismethod):
@@ -754,7 +754,7 @@ class Configurator(object):
del view_args['name']
self.add_view(view=handler, attr=meth_name,
route_name=route_name, **view_args)
-
+
# Now register the method itself
method = getattr(handler, method_name, None)
configs = getattr(method, '__exposed__', [{}])
@@ -1001,7 +1001,7 @@ class Configurator(object):
variable. If the regex matches, this predicate will be
``True``.
-
+
custom_predicates
This value should be a sequence of references to custom
@@ -1145,11 +1145,11 @@ class Configurator(object):
# the same predicate hash as this view; this registration
# is therefore an override.
regclosure()
-
+
else:
# - A view or multiview was already registered for this
# triad, and the new view is not an override.
-
+
# XXX we could try to be more efficient here and register
# a non-secured view for a multiview if none of the
# multiview's consituent views have a permission
@@ -1401,7 +1401,7 @@ class Configurator(object):
A Python object or :term:`dotted Python name` to the same
object that will be used as a view callable when this route
matches. e.g. ``mypackage.views.my_view``.
-
+
view_context
A class or an :term:`interface` or :term:`dotted Python
@@ -1487,7 +1487,7 @@ class Configurator(object):
traverse=traverse,
custom=custom_predicates
)
-
+
request_iface = self.registry.queryUtility(IRouteRequest, name=name)
if request_iface is None:
bases = use_global_views and (IRequest,) or ()
@@ -1524,7 +1524,7 @@ class Configurator(object):
pattern = path
if pattern is None:
raise ConfigurationError('"pattern" argument may not be None')
-
+
return mapper.connect(name, pattern, factory, predicates=predicates,
pregenerator=pregenerator)
@@ -2731,7 +2731,7 @@ class DottedNameResolver(object):
the resolver's ``resolve`` and ``maybe_resolve`` methods. A
dotted name which has a ``.`` (dot) or ``:`` (colon) as its first
character is treated as relative.
-
+
If the value ``None`` is supplied as the package name, the
resolver will only be able to resolve fully qualified (not
relative) names. Any attempt to resolve a relative name when the
@@ -2839,7 +2839,7 @@ class DottedNameResolver(object):
raise ConfigurationError(
'The dotted name %r cannot be imported' % (dotted,))
return dotted
-
+
class ActionPredicate(object):
action_name = 'action'
@@ -2851,7 +2851,7 @@ class ActionPredicate(object):
raise ConfigurationError(why[0])
def __call__(self, context, request):
- matchdict = getattr(request, 'matchdict', None)
+ matchdict = request.matchdict
if matchdict is None:
return False
action = matchdict.get(self.action_name)
@@ -2863,4 +2863,4 @@ class ActionPredicate(object):
# allow this predicate's phash to be compared as equal to
# others that share the same action name
return hash(self.action)
-
+
diff --git a/pyramid/request.py b/pyramid/request.py
index 8fe0afe37..a5f3597b6 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -37,6 +37,8 @@ class Request(WebobRequest):
response_callbacks = ()
finished_callbacks = ()
exception = None
+ matchdict = None
+ matched_route = None
@reify
def tmpl_context(self):
diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py
index 10b6e5fe7..17726320d 100644
--- a/pyramid/tests/test_configuration.py
+++ b/pyramid/tests/test_configuration.py
@@ -567,7 +567,7 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(L[0], event)
config.registry.subscribers((event.object, IDummy), None)
self.assertEqual(len(L), 1)
-
+
def test_make_wsgi_app(self):
from pyramid.router import Router
from pyramid.interfaces import IApplicationCreated
@@ -1934,7 +1934,7 @@ class ConfiguratorTests(unittest.TestCase):
def test_add_handler_with_action_and_action_in_path(self):
from pyramid.exceptions import ConfigurationError
config = self._makeOne()
- self.assertRaises(ConfigurationError, config.add_handler,
+ self.assertRaises(ConfigurationError, config.add_handler,
'name', '/{action}', DummyHandler, action='abc')
def test_add_handler_with_explicit_action(self):
@@ -1970,7 +1970,7 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(view['attr'], None)
self.assertEqual(view['route_name'], 'name')
self.assertEqual(view['view'], DummyHandler)
-
+
def test_add_handler_with_multiple_action(self):
config = self._makeOne()
class DummyHandler(object):
@@ -2584,7 +2584,7 @@ class ConfiguratorTests(unittest.TestCase):
self.failUnless(result is view)
self.failIf(hasattr(result, '__call_permissive__'))
self.assertEqual(view(None, None), 'OK')
-
+
def test__derive_view_as_function_requestonly(self):
def view(request):
return 'OK'
@@ -2611,7 +2611,7 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(view.__name__, result.__name__)
self.failIf(hasattr(result, '__call_permissive__'))
self.assertEqual(result(None, None), 'OK')
-
+
def test__derive_view_as_newstyle_class_requestonly(self):
class view(object):
def __init__(self, context, request):
@@ -2641,7 +2641,7 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(view.__name__, result.__name__)
self.failIf(hasattr(result, '__call_permissive__'))
self.assertEqual(result(None, None), 'OK')
-
+
def test__derive_view_as_oldstyle_class_requestonly(self):
class view:
def __init__(self, context, request):
@@ -2667,7 +2667,7 @@ class ConfiguratorTests(unittest.TestCase):
self.failUnless(result is view)
self.failIf(hasattr(result, '__call_permissive__'))
self.assertEqual(result(None, None), 'OK')
-
+
def test__derive_view_as_instance_requestonly(self):
class View:
def __call__(self, request):
@@ -2745,7 +2745,7 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(logger.messages[0],
"debug_authorization of url url (view name "
"'view_name' against context None): True")
-
+
def test__derive_view_debug_auth_permission_authpol_denied(self):
from pyramid.exceptions import Forbidden
view = lambda *arg: 'OK'
@@ -3028,10 +3028,10 @@ class ConfiguratorTests(unittest.TestCase):
result = render_view_to_response(ctx, req, 'stacked_method2')
self.assertEqual(result, 'stacked_method')
-
+
result = render_view_to_response(ctx, req, 'subpackage_init')
self.assertEqual(result, 'subpackage_init')
-
+
result = render_view_to_response(ctx, req, 'subpackage_notinit')
self.assertEqual(result, 'subpackage_notinit')
@@ -3132,7 +3132,7 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(len(L), 2)
self.assertEqual(L[0], 'foo')
self.assertEqual(L[1], event)
-
+
def test_testing_add_subscriber_defaults(self):
config = self._makeOne()
L = config.testing_add_subscriber()
@@ -3149,7 +3149,7 @@ class ConfiguratorTests(unittest.TestCase):
config = self._makeOne()
config.hook_zca(getSiteManager=gsm)
self.assertEqual(gsm.hook, get_current_registry)
-
+
def test_unhook_zca(self):
gsm = DummyGetSiteManager()
config = self._makeOne()
@@ -3170,7 +3170,7 @@ class ConfiguratorTests(unittest.TestCase):
renderer.assert_(foo=1)
renderer.assert_(bar=2)
renderer.assert_(request=request)
-
+
def test_testing_add_renderer_explicitrenderer(self):
config = self._makeOne()
class E(Exception): pass
@@ -3188,7 +3188,7 @@ class ConfiguratorTests(unittest.TestCase):
except E:
pass
else: # pragma: no cover
- raise AssertionError
+ raise AssertionError
def test_testing_add_template(self):
config = self._makeOne()
@@ -3214,7 +3214,7 @@ class Test__map_view(unittest.TestCase):
def tearDown(self):
del self.registry
testing.tearDown()
-
+
def _registerRenderer(self, typ='.txt'):
from pyramid.interfaces import IRendererFactory
from pyramid.interfaces import ITemplateRenderer
@@ -3237,7 +3237,7 @@ class Test__map_view(unittest.TestCase):
def _callFUT(self, *arg, **kw):
from pyramid.configuration import _map_view
return _map_view(*arg, **kw)
-
+
def test__map_view_as_function_context_and_request(self):
def view(context, request):
return 'OK'
@@ -3259,7 +3259,7 @@ class Test__map_view(unittest.TestCase):
result = self._callFUT(view, attr='__name__', renderer=info)
self.failIf(result is view)
self.assertRaises(TypeError, result, None, None)
-
+
def test__map_view_as_function_requestonly(self):
def view(request):
return 'OK'
@@ -3322,7 +3322,7 @@ class Test__map_view(unittest.TestCase):
self.assertEqual(view.__name__, result.__name__)
request = self._makeRequest()
self.assertEqual(result(None, request).body, 'Hello!')
-
+
def test__map_view_as_newstyle_class_requestonly(self):
class view(object):
def __init__(self, request):
@@ -3457,7 +3457,7 @@ class Test__map_view(unittest.TestCase):
result = self._callFUT(view)
self.failUnless(result is view)
self.assertEqual(result(None, None), 'OK')
-
+
def test__map_view_as_instance_context_and_request_and_attr(self):
class View:
def index(self, context, request):
@@ -3547,7 +3547,7 @@ class Test_decorate_view(unittest.TestCase):
def _callFUT(self, wrapped, original):
from pyramid.configuration import decorate_view
return decorate_view(wrapped, original)
-
+
def test_it_same(self):
def view(context, request):
""" """
@@ -3700,7 +3700,7 @@ class Test__make_predicates(unittest.TestCase):
)
order3, _, _ = self._callFUT(
path_info='path_info',
- )
+ )
order4, _, _ = self._callFUT(
request_param='param',
)
@@ -3809,7 +3809,7 @@ class TestMultiView(unittest.TestCase):
def _makeOne(self, name='name'):
return self._getTargetClass()(name)
-
+
def test_class_implements_ISecuredView(self):
from zope.interface.verify import verifyClass
from pyramid.interfaces import ISecuredView
@@ -3925,7 +3925,7 @@ class TestMultiView(unittest.TestCase):
""" """
mv.views = [(100, view, None)]
self.assertEqual(mv.__permitted__(None, None), True)
-
+
def test_permitted(self):
mv = self._makeOne()
def view(context, request):
@@ -4047,13 +4047,13 @@ class TestMultiView(unittest.TestCase):
mv.accepts = ['text/xml']
response = mv(context, request)
self.assertEqual(response, expected_response)
-
+
class TestRequestOnly(unittest.TestCase):
def _callFUT(self, arg):
from pyramid.configuration import requestonly
return requestonly(arg)
-
+
def test_newstyle_class_no_init(self):
class foo(object):
""" """
@@ -4064,7 +4064,7 @@ class TestRequestOnly(unittest.TestCase):
def __init__(self, context, request):
""" """
self.assertFalse(self._callFUT(foo))
-
+
def test_newstyle_class_init_onearg_named_request(self):
class foo(object):
def __init__(self, request):
@@ -4105,7 +4105,7 @@ class TestRequestOnly(unittest.TestCase):
def __init__(self, context, request):
""" """
self.assertFalse(self._callFUT(foo))
-
+
def test_oldstyle_class_init_onearg_named_request(self):
class foo:
def __init__(self, request):
@@ -4140,7 +4140,7 @@ class TestRequestOnly(unittest.TestCase):
def foo(context, request):
""" """
self.assertFalse(self._callFUT(foo))
-
+
def test_function_onearg_named_request(self):
def foo(request):
""" """
@@ -4172,7 +4172,7 @@ class TestRequestOnly(unittest.TestCase):
""" """
foo = Foo()
self.assertFalse(self._callFUT(foo))
-
+
def test_instance_defaultargs_onearg_named_request(self):
class Foo:
def __call__(self, request):
@@ -4269,7 +4269,7 @@ class TestDottedNameResolver(unittest.TestCase):
result = typ._zope_dottedname_style(
'pyramid.tests.test_configuration.TestDottedNameResolver')
self.assertEqual(result, self.__class__)
-
+
def test_zope_dottedname_style_irrresolveable_absolute(self):
typ = self._makeOne()
self.assertRaises(ImportError, typ._zope_dottedname_style,
@@ -4339,7 +4339,7 @@ class TestDottedNameResolver(unittest.TestCase):
result = typ._pkg_resources_style(
'pyramid.tests.test_configuration:TestDottedNameResolver')
self.assertEqual(result, self.__class__)
-
+
def test__pkg_resources_style_irrresolveable_absolute(self):
typ = self._makeOne()
self.assertRaises(ImportError, typ._pkg_resources_style,
@@ -4357,7 +4357,7 @@ class TestDottedNameResolver(unittest.TestCase):
typ = self._makeOne(package=pyramid.tests)
result = typ._pkg_resources_style('.')
self.assertEqual(result, pyramid.tests)
-
+
def test__pkg_resources_style_resolve_relative_nocurrentpackage(self):
typ = self._makeOne()
from pyramid.exceptions import ConfigurationError
@@ -4431,7 +4431,7 @@ class Test_isexception(unittest.TestCase):
def _callFUT(self, ob):
from pyramid.configuration import isexception
return isexception(ob)
-
+
def test_is_exception_instance(self):
class E(Exception):
pass
@@ -4457,7 +4457,7 @@ class TestActionPredicate(unittest.TestCase):
def _getTargetClass(self):
from pyramid.configuration import ActionPredicate
return ActionPredicate
-
+
def _makeOne(self, action='myaction'):
return self._getTargetClass()(action)
@@ -4501,11 +4501,12 @@ class TestActionPredicate(unittest.TestCase):
self.assertEqual(hash(pred1), hash(pred2))
self.assertNotEqual(hash(pred1), hash(pred3))
self.assertNotEqual(hash(pred2), hash(pred3))
-
-
+
+
class DummyRequest:
subpath = ()
+ matchdict = None
def __init__(self):
self.environ = {'PATH_INFO':'/static'}
self.params = {}
@@ -4524,7 +4525,7 @@ class DummyLock:
def release(self):
self.released = True
-
+
class DummyPackage:
def __init__(self, name):
self.__name__ = name
@@ -4592,13 +4593,13 @@ class DummyConfigurator(object):
def load_zcml(self, filename):
self.zcml_file = filename
-
+
def make_wsgi_app(self):
return self
def hook_zca(self):
self.zca_hooked = True
-
+
class DummyAccept(object):
def __init__(self, *matches):
@@ -4632,7 +4633,7 @@ class DummyGetSiteManager(object):
self.hook = hook
def reset(self):
self.unhooked = True
-
+
class DummyThreadLocalManager(object):
pushed = None
popped = False
@@ -4648,17 +4649,17 @@ class DummyFactory(object):
implements(IFactory)
def __call__(self):
""" """
-
+
class DummyEvent:
implements(IDummy)
class DummyStaticURLInfo:
def __init__(self):
self.added = []
-
+
def add(self, name, spec, **kw):
self.added.append((name, spec, kw))
-
+
def dummy_view(request):
return 'OK'
diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py
index 775c41731..d9ddf5cde 100644
--- a/pyramid/tests/test_request.py
+++ b/pyramid/tests/test_request.py
@@ -8,7 +8,7 @@ class TestRequest(unittest.TestCase):
def tearDown(self):
self.config.end()
-
+
def _makeOne(self, environ):
return self._getTargetClass()(environ)
@@ -24,6 +24,14 @@ class TestRequest(unittest.TestCase):
r = self._makeOne({'PATH_INFO':'/'})
self.assertEqual(r.exception, None)
+ def test_matchdict_defaults_to_None(self):
+ r = self._makeOne({'PATH_INFO':'/'})
+ self.assertEqual(r.matchdict, None)
+
+ def test_matched_route_defaults_to_None(self):
+ r = self._makeOne({'PATH_INFO':'/'})
+ self.assertEqual(r.matched_route, None)
+
def test_params_decoded_from_utf_8_by_default(self):
environ = {
'PATH_INFO':'/',
@@ -48,7 +56,7 @@ class TestRequest(unittest.TestCase):
inst = self._makeOne({})
result = inst.tmpl_context
self.assertEqual(result.__class__, TemplateContext)
-
+
def test_session_configured(self):
from pyramid.interfaces import ISessionFactory
inst = self._makeOne({})
@@ -107,7 +115,7 @@ class TestRequest(unittest.TestCase):
environ = {'zooma':1}
inst = self._makeOne(environ)
self.assertEqual(inst.get('zooma'), 1)
-
+
def test_has_key(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
diff --git a/pyramid/tests/test_traversal.py b/pyramid/tests/test_traversal.py
index 7083775a8..2deb5982c 100644
--- a/pyramid/tests/test_traversal.py
+++ b/pyramid/tests/test_traversal.py
@@ -6,7 +6,7 @@ class TraversalPathTests(unittest.TestCase):
def _callFUT(self, path):
from pyramid.traversal import traversal_path
return traversal_path(path)
-
+
def test_path_startswith_endswith(self):
self.assertEqual(self._callFUT('/foo/'), (u'foo',))
@@ -41,7 +41,7 @@ class TraversalPathTests(unittest.TestCase):
decoded = unicode(la, 'utf-8')
path = '/'.join([encoded, encoded])
self.assertEqual(self._callFUT(path), (decoded, decoded))
-
+
def test_utf16(self):
from pyramid.exceptions import URLDecodeError
import urllib
@@ -626,7 +626,7 @@ class ModelPathTupleTests(unittest.TestCase):
root.__name__ = None
result = self._callFUT(root)
self.assertEqual(result, ('',))
-
+
def test_nonroot_default(self):
root = DummyContext()
root.__parent__ = None
@@ -750,7 +750,7 @@ class TraversalContextURLTests(unittest.TestCase):
context_url = self._makeOne(two, request)
result = context_url()
self.assertEqual(result, 'http://example.com:5432/two/')
-
+
request = DummyRequest({VH_ROOT_KEY:'/one/two'})
context_url = self._makeOne(two, request)
result = context_url()
@@ -973,8 +973,9 @@ class TestDefaultRootFactory(unittest.TestCase):
return self._getTargetClass()(environ)
def test_no_matchdict(self):
- environ = {}
- root = self._makeOne(environ)
+ class DummyRequest:
+ matchdict = None
+ root = self._makeOne(DummyRequest())
self.assertEqual(root.__parent__, None)
self.assertEqual(root.__name__, None)
@@ -997,13 +998,13 @@ def make_traverser(result):
self.context.request = request
return result
return DummyTraverser
-
+
class DummyContext(object):
__parent__ = None
def __init__(self, next=None, name=None):
self.next = next
self.__name__ = name
-
+
def __getitem__(self, name):
if self.next is None:
raise KeyError, name
@@ -1018,7 +1019,7 @@ class DummyRequest:
if environ is None:
environ = {}
self.environ = environ
-
+
class DummyContextURL:
def __init__(self, context, request):
pass
diff --git a/pyramid/traversal.py b/pyramid/traversal.py
index ac151fdbd..515260ac2 100644
--- a/pyramid/traversal.py
+++ b/pyramid/traversal.py
@@ -592,7 +592,7 @@ class ModelGraphTraverser(object):
'virtual_root':vroot,
'virtual_root_path':vroot_tuple,
'root':root}
- if i == vroot_idx:
+ if i == vroot_idx:
vroot = next
ob = next
i += 1
@@ -623,7 +623,7 @@ class TraversalContextURL(object):
return self.request.root
except AttributeError:
return find_root(self.context)
-
+
def __call__(self):
""" Generate a URL based on the :term:`lineage` of a
:term:`model` object obtained via :term:`traversal`. If any
@@ -658,8 +658,9 @@ class DefaultRootFactory:
__parent__ = None
__name__ = None
def __init__(self, request):
- matchdict = getattr(request, 'matchdict', {})
+ matchdict = request.matchdict
# provide backwards compatibility for applications which
# used routes (at least apps without any custom "context
# factory") in BFG 0.9.X and before
- self.__dict__.update(matchdict)
+ if matchdict is not None:
+ self.__dict__.update(matchdict)