summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-05-04 18:27:40 -0700
committerChris McDonough <chrism@plope.com>2011-05-04 18:27:40 -0700
commitaf05419e53e432ef121e4e5e3d23b1d588dce0d8 (patch)
tree44a545651c01cf8d59c4cb5ed58972b648d0c94c
parent11c4d636ac158b7a3d0ca40cc52130ffef6d21c3 (diff)
parent127f51481cc47e9507bd4d87973a11e5dbab97c2 (diff)
downloadpyramid-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.txt2
-rw-r--r--docs/narr/urldispatch.rst4
-rw-r--r--docs/narr/viewconfig.rst97
-rw-r--r--pyramid/config.py33
-rw-r--r--pyramid/paster.py251
-rw-r--r--pyramid/tests/test_config.py40
-rw-r--r--pyramid/tests/test_paster.py525
-rw-r--r--setup.py1
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
+
diff --git a/setup.py b/setup.py
index 5208f58a1..f965353ed 100644
--- a/setup.py
+++ b/setup.py
@@ -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
"""