diff options
| author | Chris McDonough <chrism@plope.com> | 2011-05-04 18:27:40 -0700 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2011-05-04 18:27:40 -0700 |
| commit | af05419e53e432ef121e4e5e3d23b1d588dce0d8 (patch) | |
| tree | 44a545651c01cf8d59c4cb5ed58972b648d0c94c | |
| parent | 11c4d636ac158b7a3d0ca40cc52130ffef6d21c3 (diff) | |
| parent | 127f51481cc47e9507bd4d87973a11e5dbab97c2 (diff) | |
| download | pyramid-af05419e53e432ef121e4e5e3d23b1d588dce0d8.tar.gz pyramid-af05419e53e432ef121e4e5e3d23b1d588dce0d8.tar.bz2 pyramid-af05419e53e432ef121e4e5e3d23b1d588dce0d8.zip | |
Merge pull request #182 from cguardia/master
Merge paster pviews command from cguardia's master
| -rw-r--r-- | CONTRIBUTORS.txt | 2 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 4 | ||||
| -rw-r--r-- | docs/narr/viewconfig.rst | 97 | ||||
| -rw-r--r-- | pyramid/config.py | 33 | ||||
| -rw-r--r-- | pyramid/paster.py | 251 | ||||
| -rw-r--r-- | pyramid/tests/test_config.py | 40 | ||||
| -rw-r--r-- | pyramid/tests/test_paster.py | 525 | ||||
| -rw-r--r-- | setup.py | 1 |
8 files changed, 945 insertions, 8 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 75f9e9166..f2195de70 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -131,6 +131,8 @@ Contributors - Malthe Borch, 2011/02/28 +- Carlos de la Guardia, 2011/03/29 + - Joel Bohman, 2011/04/16 - Juliusz Gonera, 2011/04/17 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: <tutorial.models.Page object at 0xa12536c> + 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: <shootout.models.RootFactory object at 0xa56668c> + 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 5b44dd7aa..9c547d736 100644 --- a/pyramid/config.py +++ b/pyramid/config.py @@ -2486,6 +2486,7 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if xhr: def xhr_predicate(context, request): return request.is_xhr + xhr_predicate.__text__ = "xhr = True" weights.append(1 << 1) predicates.append(xhr_predicate) h.update('xhr:%r' % bool(xhr)) @@ -2493,6 +2494,8 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if request_method is not None: def request_method_predicate(context, request): 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) @@ -2504,6 +2507,8 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, raise ConfigurationError(why[0]) def path_info_predicate(context, request): 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) @@ -2512,10 +2517,15 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, request_param_val = None if '=' in request_param: request_param, request_param_val = request_param.split('=', 1) + if request_param_val is None: + text = "request_param %s" % request_param + else: + text = "request_param %s = %s" % (request_param, request_param_val) def request_param_predicate(context, request): 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)) @@ -2529,6 +2539,10 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, header_val = re.compile(header_val) except re.error, why: raise ConfigurationError(why[0]) + if header_val is None: + text = "header %s" % header_name + else: + text = "header %s = %s" % (header_name, header_val) def header_predicate(context, request): if header_val is None: return header_name in request.headers @@ -2536,6 +2550,7 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, 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)) @@ -2543,6 +2558,7 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if accept is not None: def accept_predicate(context, request): return accept in request.accept + accept_predicate.__text__ = "accept = %s" % accept weights.append(1 << 6) predicates.append(accept_predicate) h.update('accept:%r' % accept) @@ -2550,6 +2566,7 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if containment is not None: def containment_predicate(context, request): 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)) @@ -2557,6 +2574,8 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if request_type is not None: def request_type_predicate(context, request): 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)) @@ -2584,6 +2603,8 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, if custom: for num, predicate in enumerate(custom): + if getattr(predicate, '__text__', None) is None: + predicate.__text__ = "<unknown custom predicate>" predicates.append(predicate) # using hash() here rather than id() is intentional: we # want to allow custom predicates that are part of @@ -2698,10 +2719,18 @@ def preserve_view_attrs(view, wrapped_view): except AttributeError: pass try: + wrapped_view.__permission__ = view.__permission__ + except AttributeError: + pass + try: wrapped_view.__predicated__ = view.__predicated__ except AttributeError: pass try: + wrapped_view.__predicates__ = view.__predicates__ + except AttributeError: + pass + try: wrapped_view.__accept__ = view.__accept__ except AttributeError: pass @@ -2786,6 +2815,7 @@ class ViewDeriver(object): raise Forbidden(msg, result) _secured_view.__call_permissive__ = view _secured_view.__permitted__ = _permitted + _secured_view.__permission__ = permission wrapped_view = _secured_view return wrapped_view @@ -2836,6 +2866,7 @@ class ViewDeriver(object): return all((predicate(context, request) for predicate in predicates)) predicate_wrapper.__predicated__ = checker + predicate_wrapper.__predicates__ = predicates return predicate_wrapper @wraps_view @@ -2858,6 +2889,8 @@ class ViewDeriver(object): attr_view.__accept__ = accept attr_view.__order__ = order attr_view.__phash__ = phash + attr_view.__view_attr__ = self.kw.get('attr') + attr_view.__permission__ = self.kw.get('permission') return attr_view @wraps_view diff --git a/pyramid/paster.py b/pyramid/paster.py index f9f8925d7..f82246fea 100644 --- a/pyramid/paster.py +++ b/pyramid/paster.py @@ -200,3 +200,254 @@ class PRoutesCommand(PCommand): (IViewClassifier, request_iface, Interface), 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 + each route+predicate set, print each view that might match and its + predicates. + + This command accepts three positional arguments: + + ``config_file`` -- specifies the PasteDeploy config file to use + for the interactive shell. + + ``section_name`` -- specifies the section name in the PasteDeploy + config file that represents the application. + + ``url`` -- specifies the URL that will be used to find matching views. + + Example:: + + $ paster proutes myapp.ini main url + + .. note:: You should use a ``section_name`` that refers to the + actual ``app`` section in the config file that points at + your Pyramid app without any middleware wrapping, or this + command will almost certainly fail. + """ + summary = "Print all views in an application that might match a URL" + min_args = 3 + max_args = 3 + stdout = sys.stdout + + parser = Command.standard_parser(simulate=True) + + def out(self, msg): # pragma: no cover + print msg + + def _find_multi_routes(self, mapper, request): + infos = [] + path = request.environ['PATH_INFO'] + # find all routes that match path, regardless of predicates + for route in mapper.get_routes(): + match = route.match(path) + if match is not None: + info = {'match':match, 'route':route} + infos.append(info) + return infos + + def _find_view(self, url, registry): + """ + Accept ``url`` and ``registry``; create a :term:`request` and + find a :app:`Pyramid` view based on introspection of :term:`view + configuration` within the application registry; return the view. + """ + from zope.interface import providedBy + from zope.interface import implements + from pyramid.interfaces import IRequest + from pyramid.interfaces import IRootFactory + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IRequestFactory + from pyramid.interfaces import IRoutesMapper + 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 + + q = registry.queryUtility + root_factory = q(IRootFactory, default=DefaultRootFactory) + routes_mapper = q(IRoutesMapper) + request_factory = q(IRequestFactory, default=Request) + + adapters = registry.adapters + request = None + + class RoutesMultiView(object): + implements(IMultiView) + + def __init__(self, infos, context_iface, root_factory, request): + self.views = [] + for info in infos: + match, route = info['match'], info['route'] + if route is not None: + request_iface = registry.queryUtility( + IRouteRequest, + name=route.name, + default=IRequest) + view = adapters.lookup( + (IViewClassifier, request_iface, context_iface), + IView, name='', default=None) + if view is None: + continue + 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)) + + + # create the request + environ = { + 'wsgi.url_scheme':'http', + 'SERVER_NAME':'localhost', + 'SERVER_PORT':'8080', + 'REQUEST_METHOD':'GET', + 'PATH_INFO':url, + } + request = request_factory(environ) + context = None + routes_multiview = None + attrs = request.__dict__ + attrs['registry'] = registry + request_iface = IRequest + + # find the root object + if routes_mapper is not None: + infos = self._find_multi_routes(routes_mapper, request) + if len(infos) == 1: + info = infos[0] + match, route = info['match'], info['route'] + if route is not None: + attrs['matchdict'] = match + attrs['matched_route'] = route + request.environ['bfg.routes.matchdict'] = match + request_iface = registry.queryUtility( + IRouteRequest, + name=route.name, + default=IRequest) + root_factory = route.factory or root_factory + if len(infos) > 1: + routes_multiview = infos + + root = root_factory(request) + attrs['root'] = root + + # find a context + traverser = adapters.queryAdapter(root, ITraverser) + if traverser is None: + traverser = ResourceTreeTraverser(root) + tdict = traverser(request) + context, view_name, subpath, traversed, vroot, vroot_path =( + tdict['context'], tdict['view_name'], tdict['subpath'], + tdict['traversed'], tdict['virtual_root'], + tdict['virtual_root_path']) + attrs.update(tdict) + + # find a view callable + context_iface = providedBy(context) + if routes_multiview is None: + view = adapters.lookup( + (IViewClassifier, request_iface, context_iface), + IView, name=view_name, default=None) + else: + view = RoutesMultiView(infos, context_iface, root_factory, request) + + # routes are not registered with a view name + if view is None: + view = adapters.lookup( + (IViewClassifier, request_iface, context_iface), + IView, name='', default=None) + # we don't want a multiview here + if IMultiView.providedBy(view): + view = None + + if view is not None: + view.__request_attrs__ = attrs + + return view + + 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: + attr = view_wrapper.__class__.__name__ + if attr == 'function': + attr = name + view_callable = "%s.%s" % (module, attr) + self.out('') + 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): + config_file, section_name, url = self.args + if not url.startswith('/'): + url = '/%s' % url + app = self.get_app(config_file, section_name, loadapp=self.loadapp[0]) + registry = app.registry + view = self._find_view(url, registry) + self.out('') + self.out("URL = %s" % url) + self.out('') + if view is not None: + 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_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: + 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 131c7efb3..9f7873ab5 100644 --- a/pyramid/tests/test_config.py +++ b/pyramid/tests/test_config.py @@ -4288,7 +4288,7 @@ class Test__make_predicates(unittest.TestCase): accept='accept', containment='containment', request_type='request_type', - custom=('a',) + custom=(DummyCustomPredicate(),), ) order2, _, _ = self._callFUT( xhr='xhr', @@ -4299,7 +4299,7 @@ class Test__make_predicates(unittest.TestCase): accept='accept', containment='containment', request_type='request_type', - custom=('a',) + custom=(DummyCustomPredicate(),), ) order3, _, _ = self._callFUT( xhr='xhr', @@ -4392,7 +4392,7 @@ class Test__make_predicates(unittest.TestCase): request_type='request_type', ) order9, _, _ = self._callFUT( - custom=('a',), + custom=(DummyCustomPredicate(),), ) self.assertTrue(order1 > order2) self.assertTrue(order2 > order3) @@ -4409,7 +4409,7 @@ class Test__make_predicates(unittest.TestCase): request_method='request_method', ) order2, _, _ = self._callFUT( - custom=('a',), + custom=(DummyCustomPredicate(),), ) self.assertTrue(order1 < order2) @@ -4419,7 +4419,7 @@ class Test__make_predicates(unittest.TestCase): ) order2, _, _ = self._callFUT( request_method='request_method', - custom=('a',), + custom=(DummyCustomPredicate(),), ) self.assertTrue(order1 > order2) @@ -4430,7 +4430,7 @@ class Test__make_predicates(unittest.TestCase): ) order2, _, _ = self._callFUT( request_method='request_method', - custom=('a',), + custom=(DummyCustomPredicate(),), ) self.assertTrue(order1 < order2) @@ -4442,7 +4442,7 @@ class Test__make_predicates(unittest.TestCase): order2, _, _ = self._callFUT( xhr='xhr', request_method='request_method', - custom=('a',), + custom=(DummyCustomPredicate(),), ) self.assertTrue(order1 > order2) @@ -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__, 'custom predicate') + class TestMultiView(unittest.TestCase): def _getTargetClass(self): from pyramid.config import MultiView @@ -5049,6 +5071,10 @@ class DummyStaticURLInfo: def add(self, name, spec, **kw): self.added.append((name, spec, kw)) +class DummyCustomPredicate(object): + def __init__(self): + self.__text__ = 'custom predicate' + def dummy_view(request): return 'OK' diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py index d1082fc51..2239d81ea 100644 --- a/pyramid/tests/test_paster.py +++ b/pyramid/tests/test_paster.py @@ -319,7 +319,501 @@ class TestPRoutesCommand(unittest.TestCase): result = command._get_mapper(app) self.assertEqual(result.__class__, RoutesMapper) +class TestPViewsCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.paster import PViewsCommand + return PViewsCommand + + def _makeOne(self): + return self._getTargetClass()('pviews') + + def test__find_view_no_match(self): + from pyramid.registry import Registry + registry = Registry() + self._register_mapper(registry, []) + command = self._makeOne() + result = command._find_view('/a', registry) + self.assertEqual(result, None) + + def test__find_view_no_match_multiview_registered(self): + from zope.interface import implements + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + registry = Registry() + class View1(object): + implements(IMultiView) + request = DummyRequest({'PATH_INFO':'/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + registry.registerAdapter(View1(), + (IViewClassifier, IRequest, root_iface), + IMultiView) + self._register_mapper(registry, []) + command = self._makeOne() + result = command._find_view('/x', registry) + self.assertEqual(result, None) + + def test__find_view_traversal(self): + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + registry = Registry() + def view1(): pass + request = DummyRequest({'PATH_INFO':'/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + registry.registerAdapter(view1, + (IViewClassifier, IRequest, root_iface), + IView, name='a') + self._register_mapper(registry, []) + command = self._makeOne() + result = command._find_view('/a', registry) + self.assertEqual(result, view1) + + def test__find_view_traversal_multiview(self): + from zope.interface import implements + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + registry = Registry() + class View1(object): + implements(IMultiView) + request = DummyRequest({'PATH_INFO':'/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + view = View1() + registry.registerAdapter(view, + (IViewClassifier, IRequest, root_iface), + IMultiView, name='a') + self._register_mapper(registry, []) + command = self._makeOne() + result = command._find_view('/a', registry) + self.assertEqual(result, view) + + def test__find_view_route_no_multiview(self): + from zope.interface import Interface + from zope.interface import implements + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.registry import Registry + registry = Registry() + def view():pass + class IMyRoot(Interface): + pass + class IMyRoute(Interface): + pass + registry.registerAdapter(view, + (IViewClassifier, IMyRoute, IMyRoot), + IView, '') + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + class Factory(object): + implements(IMyRoot) + def __init__(self, request): + pass + routes = [DummyRoute('a', '/a', factory=Factory, matchdict={}), + DummyRoute('b', '/b', factory=Factory)] + self._register_mapper(registry, routes) + command = self._makeOne() + result = command._find_view('/a', registry) + self.assertEqual(result, view) + + def test__find_view_route_multiview_no_view_registered(self): + from zope.interface import Interface + from zope.interface import implements + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IRootFactory + from pyramid.registry import Registry + registry = Registry() + def view1():pass + def view2():pass + class IMyRoot(Interface): + pass + class IMyRoute1(Interface): + pass + class IMyRoute2(Interface): + pass + registry.registerUtility(IMyRoute1, IRouteRequest, name='a') + registry.registerUtility(IMyRoute2, IRouteRequest, name='b') + class Factory(object): + implements(IMyRoot) + def __init__(self, request): + pass + registry.registerUtility(Factory, IRootFactory) + routes = [DummyRoute('a', '/a', matchdict={}), + DummyRoute('b', '/a', matchdict={})] + self._register_mapper(registry, routes) + command = self._makeOne() + result = command._find_view('/a', registry) + self.failUnless(IMultiView.providedBy(result)) + + def test__find_view_route_multiview(self): + from zope.interface import Interface + from zope.interface import implements + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IRootFactory + from pyramid.registry import Registry + registry = Registry() + def view1():pass + def view2():pass + class IMyRoot(Interface): + pass + class IMyRoute1(Interface): + pass + class IMyRoute2(Interface): + pass + registry.registerAdapter(view1, + (IViewClassifier, IMyRoute1, IMyRoot), + IView, '') + registry.registerAdapter(view2, + (IViewClassifier, IMyRoute2, IMyRoot), + IView, '') + registry.registerUtility(IMyRoute1, IRouteRequest, name='a') + registry.registerUtility(IMyRoute2, IRouteRequest, name='b') + class Factory(object): + implements(IMyRoot) + def __init__(self, request): + pass + registry.registerUtility(Factory, IRootFactory) + routes = [DummyRoute('a', '/a', matchdict={}), + DummyRoute('b', '/a', matchdict={})] + self._register_mapper(registry, routes) + command = self._makeOne() + result = command._find_view('/a', registry) + self.failUnless(IMultiView.providedBy(result)) + self.assertEqual(len(result.views), 2) + self.failUnless((None, view1, None) in result.views) + self.failUnless((None, view2, None) in result.views) + + def test__find_multi_routes_all_match(self): + command = self._makeOne() + def factory(request): pass + routes = [DummyRoute('a', '/a', factory=factory, matchdict={}), + DummyRoute('b', '/a', factory=factory, matchdict={})] + mapper = DummyMapper(*routes) + request = DummyRequest({'PATH_INFO':'/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, [{'match':{}, 'route':routes[0]}, + {'match':{}, 'route':routes[1]}]) + def test__find_multi_routes_some_match(self): + command = self._makeOne() + def factory(request): pass + routes = [DummyRoute('a', '/a', factory=factory), + DummyRoute('b', '/a', factory=factory, matchdict={})] + mapper = DummyMapper(*routes) + request = DummyRequest({'PATH_INFO':'/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, [{'match':{}, 'route':routes[1]}]) + + def test__find_multi_routes_none_match(self): + command = self._makeOne() + def factory(request): pass + routes = [DummyRoute('a', '/a', factory=factory), + DummyRoute('b', '/a', factory=factory)] + mapper = DummyMapper(*routes) + request = DummyRequest({'PATH_INFO':'/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, []) + + def test_views_command_not_found(self): + from pyramid.registry import Registry + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + command._find_view = lambda arg1, arg2: None + 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], ' Not found.') + + def test_views_command_not_found_url_starts_without_slash(self): + from pyramid.registry import Registry + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + command._find_view = lambda arg1, arg2: None + 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], ' Not found.') + + def test_views_command_single_view_traversal(self): + from pyramid.registry import Registry + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + view = DummyView(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.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 + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + view = DummyView(context='context', view_name='a') + view.__permission__ = 'test' + 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.DummyView') + self.assertEqual(L[9], ' required permission = test') + + def test_views_command_single_view_traversal_with_predicates(self): + from pyramid.registry import Registry + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + def predicate(): pass + predicate.__text__ = "predicate = x" + view = DummyView(context='context', view_name='a') + view.__predicates__ = [predicate] + 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.DummyView') + self.assertEqual(L[9], ' view predicates (predicate = x)') + + def test_views_command_single_view_route(self): + from pyramid.registry import Registry + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + route = DummyRoute('a', '/a', matchdict={}) + 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[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 + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + view = DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + multiview = DummyMultiView(view, context='context', view_name='a') + command._find_view = lambda arg1, arg2: multiview + 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.call') + + def test_views_command_multiview_with_permission(self): + from pyramid.registry import Registry + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + view = DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + view.__permission__ = 'test' + multiview = DummyMultiView(view, context='context', view_name='a') + command._find_view = lambda arg1, arg2: multiview + 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.call') + self.assertEqual(L[9], ' required permission = test') + + def test_views_command_multiview_with_predicates(self): + from pyramid.registry import Registry + command = self._makeOne() + registry = Registry() + L = [] + command.out = L.append + def predicate(): pass + predicate.__text__ = "predicate = x" + view = DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + view.__predicates__ = [predicate] + multiview = DummyMultiView(view, context='context', view_name='a') + command._find_view = lambda arg1, arg2: multiview + 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.call') + self.assertEqual(L[9], ' view predicates (predicate = x)') + + def _register_mapper(self, registry, routes): + from pyramid.interfaces import IRoutesMapper + mapper = DummyMapper(*routes) + registry.registerUtility(mapper, IRoutesMapper) + class TestGetApp(unittest.TestCase): def _callFUT(self, config_file, section_name, loadapp): from pyramid.paster import get_app @@ -407,8 +901,37 @@ class DummyMapper(object): return self.routes class DummyRoute(object): - def __init__(self, name, pattern, factory=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 +class DummyRequest: + application_url = 'http://example.com:5432' + script_name = '' + def __init__(self, environ): + self.environ = environ + self.matchdict = {} + +class DummyView(object): + def __init__(self, **attrs): + self.__request_attrs__ = attrs + +class DummyMultiView(object): + from zope.interface import implements + from pyramid.interfaces import IMultiView + implements(IMultiView) + + def __init__(self, *views, **attrs): + self.views = [(None, view, None) for view in views] + self.__request_attrs__ = attrs + @@ -85,6 +85,7 @@ setup(name='pyramid', [paste.paster_command] pshell=pyramid.paster:PShellCommand proutes=pyramid.paster:PRoutesCommand + pviews=pyramid.paster:PViewsCommand [console_scripts] bfg2pyramid = pyramid.fixers.fix_bfg_imports:main """ |
