From 8abf0a3c996a22dbf9b3ed4602df026a87686a74 Mon Sep 17 00:00:00 2001 From: cguardia Date: Wed, 4 May 2011 15:11:59 -0500 Subject: code refactoring for showing route->view relationship more correctly; tests and docs for pull request --- docs/narr/urldispatch.rst | 4 ++ docs/narr/viewconfig.rst | 97 +++++++++++++++++++++++++++ pyramid/config.py | 31 +++++---- pyramid/paster.py | 132 ++++++++++++++++++++----------------- pyramid/tests/test_config.py | 22 +++++++ pyramid/tests/test_paster.py | 153 +++++++++++++++++++++++++++++++++---------- 6 files changed, 328 insertions(+), 111 deletions(-) diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 1024dd188..a180003d0 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -1121,6 +1121,10 @@ a developer to understand either of them in detail. It also means that we can allow a developer to combine :term:`URL dispatch` and :term:`traversal` in various exceptional cases as documented in :ref:`hybrid_chapter`. +To gain a better understanding of how routes and views are associated in a +real application, you can use the ``paster pviews`` command, as documented +in :ref:`displaying_matching_views`. + References ---------- diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst index 743cc016e..d99e5bed5 100644 --- a/docs/narr/viewconfig.rst +++ b/docs/narr/viewconfig.rst @@ -732,3 +732,100 @@ found will be printed to ``stderr``, and the browser representation of the error will include the same information. See :ref:`environment_chapter` for more information about how, and where to set these values. +.. index:: + pair: matching views; printing + single: paster pviews + +.. _displaying_matching_views: + +Displaying Matching Views for a Given URL +----------------------------------------- + +For a big application with several views, it can be hard to keep the view +configuration details in your head, even if you defined all the views +yourself. You can use the ``paster pviews`` command in a terminal window to +print a summary of matching routes and views for a given URL in your +application. The ``paster pviews`` command accepts three arguments. The +first argument to ``pviews`` is the path to your application's ``.ini`` file. +The second is the ``app`` section name inside the ``.ini`` file which points +to your application. The third is the URL to test for matching views. + +Here is an example for a simple view configuration using :term:`traversal`: + +.. code-block:: text + :linenos: + + $ ../bin/paster pviews development.ini tutorial /FrontPage + + URL = /FrontPage + + context: + view name: + + View: + ----- + tutorial.views.view_page + required permission = view + +The output always has the requested URL at the top and below that all the +views that matched with their view configuration details. In this example +only one view matches, so there is just a single *View* section. For each +matching view, the full code path to the associated view callable is shown, +along with any permissions and predicates that are part of that view +configuration. + +A more complex configuration might generate something like this: + +.. code-block:: text + :linenos: + + $ ../bin/paster pviews development.ini shootout /about + + URL = /about + + context: + view name: about + + Route: + ------ + route name: about + route pattern: /about + route path: /about + subpath: + route predicates (request method = GET) + + View: + ----- + shootout.views.about_view + required permission = view + view predicates (request_param testing, header X/header) + + Route: + ------ + route name: about_post + route pattern: /about + route path: /about + subpath: + route predicates (request method = POST) + + View: + ----- + shootout.views.about_view_post + required permission = view + view predicates (request_param test) + + View: + ----- + shootout.views.about_view_post2 + required permission = view + view predicates (request_param test2) + +In this case, we are dealing with a :term:`URL dispatch` application. This +specific URL has two matching routes. The matching route information is +displayed first, followed by any views that are associated with that route. +As you can see from the second matching route output, a route can be +associated with more than one view. + +For a URL that doesn't match any views, ``paster pviews`` will simply print +out a *Not found* message. + diff --git a/pyramid/config.py b/pyramid/config.py index 9aa7031e0..ca76cafb4 100644 --- a/pyramid/config.py +++ b/pyramid/config.py @@ -2485,16 +2485,17 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if xhr: def xhr_predicate(context, request): - """xhr = True""" return request.is_xhr + xhr_predicate.__text__ = "xhr = True" weights.append(1 << 1) predicates.append(xhr_predicate) h.update('xhr:%r' % bool(xhr)) if request_method is not None: def request_method_predicate(context, request): - """request_method = %s""" % request_method return request.method == request_method + text = "request method = %s" + request_method_predicate.__text__ = text % request_method weights.append(1 << 2) predicates.append(request_method_predicate) h.update('request_method:%r' % request_method) @@ -2505,8 +2506,9 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, except re.error, why: raise ConfigurationError(why[0]) def path_info_predicate(context, request): - """path_info = %s""" % path_info return path_info_val.match(request.path_info) is not None + text = "path_info = %s" + path_info_predicate.__text__ = text % path_info weights.append(1 << 3) predicates.append(path_info_predicate) h.update('path_info:%r' % path_info) @@ -2516,14 +2518,14 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if '=' in request_param: request_param, request_param_val = request_param.split('=', 1) if request_param_val is None: - msg = "request_param %s" % request_param + text = "request_param %s" % request_param else: - msg = "request_param %s = %s" % (request_param, request_param_val) + text = "request_param %s = %s" % (request_param, request_param_val) def request_param_predicate(context, request): - """%s""" % msg if request_param_val is None: return request_param in request.params return request.params.get(request_param) == request_param_val + request_param_predicate.__text__ = text weights.append(1 << 4) predicates.append(request_param_predicate) h.update('request_param:%r=%r' % (request_param, request_param_val)) @@ -2538,41 +2540,42 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, except re.error, why: raise ConfigurationError(why[0]) if header_val is None: - msg = "header %s" % header_name + text = "header %s" % header_name else: - msg = "header %s = %s" % (header_name, header_val) + text = "header %s = %s" % (header_name, header_val) def header_predicate(context, request): - """%s""" % msg if header_val is None: return header_name in request.headers val = request.headers.get(header_name) if val is None: return False return header_val.match(val) is not None + header_predicate.__text__ = text weights.append(1 << 5) predicates.append(header_predicate) h.update('header:%r=%r' % (header_name, header_val)) if accept is not None: def accept_predicate(context, request): - """accept = %s""" % accept return accept in request.accept + accept_predicate.__text__ = "accept = %s" % accept weights.append(1 << 6) predicates.append(accept_predicate) h.update('accept:%r' % accept) if containment is not None: def containment_predicate(context, request): - """containment = %s""" % containment return find_interface(context, containment) is not None + containment_predicate.__text__ = "containment = %s" % containment weights.append(1 << 7) predicates.append(containment_predicate) h.update('containment:%r' % hash(containment)) if request_type is not None: def request_type_predicate(context, request): - """request_type = %s""" % request_type return request_type.providedBy(request) + text = "request_type = %s" + request_type_predicate.__text__ = text % request_type weights.append(1 << 8) predicates.append(request_type_predicate) h.update('request_type:%r' % hash(request_type)) @@ -2600,8 +2603,8 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if custom: for num, predicate in enumerate(custom): - if getattr(predicate, '__doc__', None) is None: - predicate.__doc__ = "" + if getattr(predicate, '__text__', None) is None: + predicate.__text__ = "" predicates.append(predicate) # using hash() here rather than id() is intentional: we # want to allow custom predicates that are part of diff --git a/pyramid/paster.py b/pyramid/paster.py index 15a50458a..f82246fea 100644 --- a/pyramid/paster.py +++ b/pyramid/paster.py @@ -201,6 +201,9 @@ class PRoutesCommand(PCommand): IView, name='', default=None) self.out(fmt % (route.name, route.pattern, view_callable)) + +from pyramid.interfaces import IMultiView + class PViewsCommand(PCommand): """Print, for a given URL, the views that might match. Underneath each potentially matching route, list the predicates required. Underneath @@ -260,10 +263,9 @@ class PViewsCommand(PCommand): from pyramid.interfaces import IRouteRequest from pyramid.interfaces import IRequestFactory from pyramid.interfaces import IRoutesMapper - from pyramid.interfaces import ITraverser - from pyramid.interfaces import IMultiView from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import ITraverser from pyramid.request import Request from pyramid.traversal import DefaultRootFactory from pyramid.traversal import ResourceTreeTraverser @@ -279,7 +281,7 @@ class PViewsCommand(PCommand): class RoutesMultiView(object): implements(IMultiView) - def __init__(self, infos, context_iface, subpath): + def __init__(self, infos, context_iface, root_factory, request): self.views = [] for info in infos: match, route = info['match'], info['route'] @@ -293,11 +295,18 @@ class PViewsCommand(PCommand): IView, name='', default=None) if view is None: continue - view.__predicates__ = list(route.predicates) - view.__route_attrs__ = {'matchdict': match, - 'matched_route': route, - 'subpath': subpath} - view.__view_attr__ = '' + view.__request_attrs__ = {} + view.__request_attrs__['matchdict'] = match + view.__request_attrs__['matched_route'] = route + root_factory = route.factory or root_factory + root = root_factory(request) + traverser = adapters.queryAdapter(root, ITraverser) + if traverser is None: + traverser = ResourceTreeTraverser(root) + tdict = traverser(request) + view.__request_attrs__.update(tdict) + if not hasattr(view, '__view_attr__'): + view.__view_attr__ = '' self.views.append((None, view, None)) @@ -355,7 +364,7 @@ class PViewsCommand(PCommand): (IViewClassifier, request_iface, context_iface), IView, name=view_name, default=None) else: - view = RoutesMultiView(infos, context_iface, subpath) + view = RoutesMultiView(infos, context_iface, root_factory, request) # routes are not registered with a view name if view is None: @@ -371,58 +380,52 @@ class PViewsCommand(PCommand): return view - def output_route_attrs(self, attrs): - if 'matched_route' in attrs: - route = attrs['matched_route'] - self.out(" route name: %s" % route.name) - self.out(" route pattern: %s" % route.pattern) - self.out(" route path: %s" % route.path) - self.out(" subpath: %s" % '/'.join(attrs['subpath'])) - - def output_view_attrs(self, attrs): - self.out(" context: %s" % attrs['context']) - self.out(" view name: %s" % attrs['view_name']) - - def output_multiview_info(self, view_wrapper): - name = view_wrapper.__name__ - module = view_wrapper.__module__ - attr = view_wrapper.__view_attr__ - route_attrs = getattr(view_wrapper, '__route_attrs__', {}) - self.out('') - self.out(" View:") - self.out(" -----") - self.out(" %s.%s.%s" % (module, name, attr)) - self.output_route_attrs(route_attrs) - permission = getattr(view_wrapper, '__permission__', None) - if permission is not None: - self.out(" required permission = %s" % permission) - predicates = getattr(view_wrapper, '__predicates__', None) - if predicates is not None: - for predicate in predicates: - self.out(" %s" % predicate.__doc__) - - def output_view_info(self, view): - if view is not None: - name = getattr(view, '__name__', view.__class__.__name__) - module = view.__module__ + def output_route_attrs(self, attrs, indent): + route = attrs['matched_route'] + self.out("%sroute name: %s" % (indent, route.name)) + self.out("%sroute pattern: %s" % (indent, route.pattern)) + self.out("%sroute path: %s" % (indent, route.path)) + self.out("%ssubpath: %s" % (indent, '/'.join(attrs['subpath']))) + predicates = ', '.join([p.__text__ for p in route.predicates]) + if predicates != '': + self.out("%sroute predicates (%s)" % (indent, predicates)) + + def output_view_info(self, view_wrapper, level=1): + indent = " " * level + name = getattr(view_wrapper, '__name__', '') + module = getattr(view_wrapper, '__module__', '') + attr = getattr(view_wrapper, '__view_attr__', None) + request_attrs = getattr(view_wrapper, '__request_attrs__', {}) + if attr is not None: + view_callable = "%s.%s.%s" % (module, name, attr) else: - module = 'Not found' - name = '' + attr = view_wrapper.__class__.__name__ + if attr == 'function': + attr = name + view_callable = "%s.%s" % (module, attr) self.out('') - self.out(" View:") - self.out(" -----") - self.out(" %s.%s" % (module, name)) - permission = getattr(view, '__permission__', None) - if permission is not None: - self.out(" required permission = %s" % permission) - predicates = getattr(view, '__predicates__', None) - if predicates is not None: - for predicate in predicates: - self.out(" %s" % predicate.__doc__) + if 'matched_route' in request_attrs: + self.out("%sRoute:" % indent) + self.out("%s------" % indent) + self.output_route_attrs(request_attrs, indent) + permission = getattr(view_wrapper, '__permission__', None) + if not IMultiView.providedBy(view_wrapper): + # single view for this route, so repeat call without route data + del request_attrs['matched_route'] + self.output_view_info(view_wrapper, level+1) + else: + self.out("%sView:" % indent) + self.out("%s-----" % indent) + self.out("%s%s" % (indent, view_callable)) + permission = getattr(view_wrapper, '__permission__', None) + if permission is not None: + self.out("%srequired permission = %s" % (indent, permission)) + predicates = getattr(view_wrapper, '__predicates__', None) + if predicates is not None: + predicate_text = ', '.join([p.__text__ for p in predicates]) + self.out("%sview predicates (%s)" % (indent, predicate_text)) def command(self): - from pyramid.interfaces import IMultiView - config_file, section_name, url = self.args if not url.startswith('/'): url = '/%s' % url @@ -431,13 +434,20 @@ class PViewsCommand(PCommand): view = self._find_view(url, registry) self.out('') self.out("URL = %s" % url) + self.out('') if view is not None: - self.output_view_attrs(view.__request_attrs__) - self.output_route_attrs(view.__request_attrs__) + self.out(" context: %s" % view.__request_attrs__['context']) + self.out(" view name: %s" % view.__request_attrs__['view_name']) if IMultiView.providedBy(view): for dummy, view_wrapper, dummy in view.views: - self.output_multiview_info(view_wrapper) + self.output_view_info(view_wrapper) + if IMultiView.providedBy(view_wrapper): + for dummy, mv_view_wrapper, dummy in view_wrapper.views: + self.output_view_info(mv_view_wrapper, level=2) else: - self.output_view_info(view) + if view is not None: + self.output_view_info(view) + else: + self.out(" Not found.") self.out('') diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py index 2bd24ac26..f583cc783 100644 --- a/pyramid/tests/test_config.py +++ b/pyramid/tests/test_config.py @@ -4477,6 +4477,28 @@ class Test__make_predicates(unittest.TestCase): self.assertEqual(info, {'match': {'a':'a', 'b':'b', 'traverse':('1', 'a', 'b')}}) + def test_predicate_text_is_correct(self): + _, predicates, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + accept='accept', + containment='containment', + request_type='request_type', + custom=(DummyCustomPredicate(),)) + self.assertEqual(predicates[0].__text__, 'xhr = True') + self.assertEqual(predicates[1].__text__, + 'request method = request_method') + self.assertEqual(predicates[2].__text__, 'path_info = path_info') + self.assertEqual(predicates[3].__text__, 'request_param param') + self.assertEqual(predicates[4].__text__, 'header header') + self.assertEqual(predicates[5].__text__, 'accept = accept') + self.assertEqual(predicates[6].__text__, 'containment = containment') + self.assertEqual(predicates[7].__text__, 'request_type = request_type') + self.assertEqual(predicates[8].__text__, '') + class TestMultiView(unittest.TestCase): def _getTargetClass(self): from pyramid.config import MultiView diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py index 8a5ef7003..2239d81ea 100644 --- a/pyramid/tests/test_paster.py +++ b/pyramid/tests/test_paster.py @@ -545,7 +545,7 @@ class TestPViewsCommand(unittest.TestCase): result = command.command() self.assertEqual(result, None) self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[5], ' Not found.') + self.assertEqual(L[3], ' Not found.') def test_views_command_not_found_url_starts_without_slash(self): from pyramid.registry import Registry @@ -562,7 +562,7 @@ class TestPViewsCommand(unittest.TestCase): result = command.command() self.assertEqual(result, None) self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[5], ' Not found.') + self.assertEqual(L[3], ' Not found.') def test_views_command_single_view_traversal(self): from pyramid.registry import Registry @@ -580,9 +580,30 @@ class TestPViewsCommand(unittest.TestCase): result = command.command() self.assertEqual(result, None) self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[2], ' context: context') - self.assertEqual(L[3], ' view name: a') - self.assertEqual(L[7], ' pyramid.tests.test_paster.DummyView') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_paster.DummyView') + + def test_views_command_single_view_function_traversal(self): + from pyramid.registry import Registry + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + def view(): pass + view.__request_attrs__ = {'context': 'context', 'view_name': 'a'} + command._find_view = lambda arg1, arg2: view + app = DummyApp() + app.registry = registry + loadapp = DummyLoadApp(app) + command.loadapp = (loadapp,) + command.args = ('/foo/bar/myapp.ini', 'myapp', '/a') + result = command.command() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_paster.view') def test_views_command_single_view_traversal_with_permission(self): from pyramid.registry import Registry @@ -601,10 +622,10 @@ class TestPViewsCommand(unittest.TestCase): result = command.command() self.assertEqual(result, None) self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[2], ' context: context') - self.assertEqual(L[3], ' view name: a') - self.assertEqual(L[7], ' pyramid.tests.test_paster.DummyView') - self.assertEqual(L[8], ' required permission = test') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_paster.DummyView') + self.assertEqual(L[9], ' required permission = test') def test_views_command_single_view_traversal_with_predicates(self): from pyramid.registry import Registry @@ -612,8 +633,8 @@ class TestPViewsCommand(unittest.TestCase): registry = Registry() L = [] command.out = L.append - def predicate(): - """predicate = x""" + def predicate(): pass + predicate.__text__ = "predicate = x" view = DummyView(context='context', view_name='a') view.__predicates__ = [predicate] command._find_view = lambda arg1, arg2: view @@ -625,10 +646,10 @@ class TestPViewsCommand(unittest.TestCase): result = command.command() self.assertEqual(result, None) self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[2], ' context: context') - self.assertEqual(L[3], ' view name: a') - self.assertEqual(L[7], ' pyramid.tests.test_paster.DummyView') - self.assertEqual(L[8], ' predicate = x') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_paster.DummyView') + self.assertEqual(L[9], ' view predicates (predicate = x)') def test_views_command_single_view_route(self): from pyramid.registry import Registry @@ -648,13 +669,70 @@ class TestPViewsCommand(unittest.TestCase): result = command.command() self.assertEqual(result, None) self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[2], ' context: context') - self.assertEqual(L[3], ' view name: a') - self.assertEqual(L[4], ' route name: a') - self.assertEqual(L[5], ' route pattern: /a') - self.assertEqual(L[6], ' route path: /a') - self.assertEqual(L[7], ' subpath: ') - self.assertEqual(L[11], ' pyramid.tests.test_paster.DummyView') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[6], ' Route:') + self.assertEqual(L[8], ' route name: a') + self.assertEqual(L[9], ' route pattern: /a') + self.assertEqual(L[10], ' route path: /a') + self.assertEqual(L[11], ' subpath: ') + self.assertEqual(L[15], ' pyramid.tests.test_paster.DummyView') + + def test_views_command_multi_view_nested(self): + from pyramid.registry import Registry + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + view1 = DummyView(context='context', view_name='a1') + view1.__name__ = 'view1' + view1.__view_attr__ = 'call' + multiview1 = DummyMultiView(view1, context='context', view_name='a1') + multiview2 = DummyMultiView(multiview1, context='context', + view_name='a') + command._find_view = lambda arg1, arg2: multiview2 + app = DummyApp() + app.registry = registry + loadapp = DummyLoadApp(app) + command.loadapp = (loadapp,) + command.args = ('/foo/bar/myapp.ini', 'myapp', '/a') + result = command.command() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_paster.DummyMultiView') + self.assertEqual(L[12], ' pyramid.tests.test_paster.view1.call') + + def test_views_command_single_view_route_with_route_predicates(self): + from pyramid.registry import Registry + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + def predicate(): pass + predicate.__text__ = "predicate = x" + route = DummyRoute('a', '/a', matchdict={}, predicate=predicate) + view = DummyView(context='context', view_name='a', + matched_route=route, subpath='') + command._find_view = lambda arg1, arg2: view + app = DummyApp() + app.registry = registry + loadapp = DummyLoadApp(app) + command.loadapp = (loadapp,) + command.args = ('/foo/bar/myapp.ini', 'myapp', '/a') + result = command.command() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[6], ' Route:') + self.assertEqual(L[8], ' route name: a') + self.assertEqual(L[9], ' route pattern: /a') + self.assertEqual(L[10], ' route path: /a') + self.assertEqual(L[11], ' subpath: ') + self.assertEqual(L[12], ' route predicates (predicate = x)') + self.assertEqual(L[16], ' pyramid.tests.test_paster.DummyView') def test_views_command_multiview(self): from pyramid.registry import Registry @@ -675,9 +753,9 @@ class TestPViewsCommand(unittest.TestCase): result = command.command() self.assertEqual(result, None) self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[2], ' context: context') - self.assertEqual(L[3], ' view name: a') - self.assertEqual(L[7], ' pyramid.tests.test_paster.view.call') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_paster.view.call') def test_views_command_multiview_with_permission(self): from pyramid.registry import Registry @@ -699,10 +777,10 @@ class TestPViewsCommand(unittest.TestCase): result = command.command() self.assertEqual(result, None) self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[2], ' context: context') - self.assertEqual(L[3], ' view name: a') - self.assertEqual(L[7], ' pyramid.tests.test_paster.view.call') - self.assertEqual(L[8], ' required permission = test') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_paster.view.call') + self.assertEqual(L[9], ' required permission = test') def test_views_command_multiview_with_predicates(self): from pyramid.registry import Registry @@ -710,8 +788,8 @@ class TestPViewsCommand(unittest.TestCase): registry = Registry() L = [] command.out = L.append - def predicate(): - """predicate = x""" + def predicate(): pass + predicate.__text__ = "predicate = x" view = DummyView(context='context') view.__name__ = 'view' view.__view_attr__ = 'call' @@ -726,10 +804,10 @@ class TestPViewsCommand(unittest.TestCase): result = command.command() self.assertEqual(result, None) self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[2], ' context: context') - self.assertEqual(L[3], ' view name: a') - self.assertEqual(L[7], ' pyramid.tests.test_paster.view.call') - self.assertEqual(L[8], ' predicate = x') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_paster.view.call') + self.assertEqual(L[9], ' view predicates (predicate = x)') def _register_mapper(self, registry, routes): from pyramid.interfaces import IRoutesMapper @@ -823,13 +901,16 @@ class DummyMapper(object): return self.routes class DummyRoute(object): - def __init__(self, name, pattern, factory=None, matchdict=None): + def __init__(self, name, pattern, factory=None, + matchdict=None, predicate=None): self.name = name self.path = pattern self.pattern = pattern self.factory = factory self.matchdict = matchdict self.predicates = [] + if predicate is not None: + self.predicates = [predicate] def match(self, route): return self.matchdict -- cgit v1.2.3