diff options
| author | Chris Rossi <chris@archimedeanco.com> | 2010-11-10 16:39:07 -0500 |
|---|---|---|
| committer | Chris Rossi <chris@archimedeanco.com> | 2010-11-10 16:39:07 -0500 |
| commit | 51c305c772150a8ee8b12c5dbdcb1fc30cb3a251 (patch) | |
| tree | bf170d70e84018900cb01e1307dda20f17328659 | |
| parent | acae5b33a825d1739dd76672e10f5c9f66f783a6 (diff) | |
| download | pyramid-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.txt | 5 | ||||
| -rw-r--r-- | pyramid/configuration.py | 36 | ||||
| -rw-r--r-- | pyramid/request.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_configuration.py | 87 | ||||
| -rw-r--r-- | pyramid/tests/test_request.py | 14 | ||||
| -rw-r--r-- | pyramid/tests/test_traversal.py | 19 | ||||
| -rw-r--r-- | pyramid/traversal.py | 9 |
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) |
