Skip to content

Commit

Permalink
Merge branch 'master' of github.com:Pylons/pyramid
Browse files Browse the repository at this point in the history
  • Loading branch information
mcdonc committed May 5, 2011
2 parents f426e51 + af05419 commit 70a1109
Show file tree
Hide file tree
Showing 9 changed files with 946 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTORS.txt
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/narr/urldispatch.rst
Expand Up @@ -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
----------

Expand Down
97 changes: 97 additions & 0 deletions docs/narr/viewconfig.rst
Expand Up @@ -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.

33 changes: 33 additions & 0 deletions pyramid/config.py
Expand Up @@ -2486,13 +2486,16 @@ 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))

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)
Expand All @@ -2504,6 +2507,8 @@ def request_method_predicate(context, request):
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)
Expand All @@ -2512,10 +2517,15 @@ def path_info_predicate(context, request):
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))
Expand All @@ -2529,34 +2539,43 @@ def request_param_predicate(context, request):
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
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):
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):
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):
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))
Expand Down Expand Up @@ -2584,6 +2603,8 @@ def traverse_predicate(context, request):

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
Expand Down Expand Up @@ -2697,10 +2718,18 @@ def preserve_view_attrs(view, wrapped_view):
wrapped_view.__call_permissive__ = view.__call_permissive__
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:
Expand Down Expand Up @@ -2786,6 +2815,7 @@ def _secured_view(context, request):
raise Forbidden(msg, result)
_secured_view.__call_permissive__ = view
_secured_view.__permitted__ = _permitted
_secured_view.__permission__ = permission
wrapped_view = _secured_view

return wrapped_view
Expand Down Expand Up @@ -2836,6 +2866,7 @@ def checker(context, request):
return all((predicate(context, request) for predicate in
predicates))
predicate_wrapper.__predicated__ = checker
predicate_wrapper.__predicates__ = predicates
return predicate_wrapper

@wraps_view
Expand All @@ -2858,6 +2889,8 @@ def attr_view(context, request):
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
Expand Down

0 comments on commit 70a1109

Please sign in to comment.