summaryrefslogtreecommitdiff
path: root/docs/narr
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2020-01-17 16:08:35 -0600
committerGitHub <noreply@github.com>2020-01-17 16:08:35 -0600
commit03d3bbd2791918a844da49eb4449b4953b83a31b (patch)
tree5899bebf50a3baf340b8fe650030c6a2b0525d0d /docs/narr
parent9c153e1250e00faa06003c10c3a26886489e6210 (diff)
parent108f026e1dcca554867ff8f7a211a5ce0668a26e (diff)
downloadpyramid-03d3bbd2791918a844da49eb4449b4953b83a31b.tar.gz
pyramid-03d3bbd2791918a844da49eb4449b4953b83a31b.tar.bz2
pyramid-03d3bbd2791918a844da49eb4449b4953b83a31b.zip
Merge pull request #3560 from merwok/docs/rewrite-custom-predicates
Rewrite docs for custom predicates
Diffstat (limited to 'docs/narr')
-rw-r--r--docs/narr/hooks.rst10
-rw-r--r--docs/narr/urldispatch.rst176
-rw-r--r--docs/narr/viewconfig.rst20
3 files changed, 106 insertions, 100 deletions
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 4a594a8c9..c19d26236 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -1416,10 +1416,10 @@ The ``ptweens`` command-line utility can be used to report the current implicit
and explicit tween chains used by an application. See
:ref:`displaying_tweens`.
-.. _registering_thirdparty_predicates:
+.. _registering_custom_predicates:
-Adding a Third Party View, Route, or Subscriber Predicate
----------------------------------------------------------
+Adding a Custom View, Route, or Subscriber Predicate
+----------------------------------------------------
.. versionadded:: 1.4
@@ -1446,7 +1446,7 @@ Likewise, a similar predicate can be used as a *route* predicate:
config.add_route('name', '/foo', request_method='POST')
Many other built-in predicates exists (``request_param``, and others). You can
-add third-party predicates to the list of available predicates by using one of
+add custom predicates to the list of available predicates by using one of
:meth:`pyramid.config.Configurator.add_view_predicate` or
:meth:`pyramid.config.Configurator.add_route_predicate`. The former adds a
view predicate, the latter a route predicate.
@@ -1564,7 +1564,7 @@ event type.
self.val = val
def text(self):
- return 'path_startswith = %s' % (self.val,)
+ return 'request_path_startswith = %s' % (self.val,)
phash = text
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 9372163e8..684e64395 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -1087,11 +1087,8 @@ A convenience context manager exists to set the route prefix for any
Custom Route Predicates
-----------------------
-Each of the predicate callables fed to the ``custom_predicates`` argument of
-:meth:`~pyramid.config.Configurator.add_route` must be a callable accepting two
-arguments. The first argument passed to a custom predicate is a dictionary
-conventionally named ``info``. The second argument is the current
-:term:`request` object.
+A predicate is a callable that accepts two arguments: a dictionary
+conventionally named ``info``, and the current :term:`request` object.
The ``info`` dictionary has a number of contained values, including ``match``
and ``route``. ``match`` is a dictionary which represents the arguments matched
@@ -1100,52 +1097,71 @@ was matched (see :class:`pyramid.interfaces.IRoute` for the API of such a route
object).
``info['match']`` is useful when predicates need access to the route match.
-For example:
+Imagine you want to define a route when a part of the URL matches some
+specific values. You could define three view configurations, each one
+with its own ``match_param`` value (see :ref:`predicate_view_args`), but
+you want to think of it as one route, and associate it with one view.
+See this code:
.. code-block:: python
:linenos:
- def any_of(segment_name, *allowed):
- def predicate(info, request):
- if info['match'][segment_name] in allowed:
- return True
- return predicate
+ class AnyOfPredicate:
+ def __init__(self, val, info):
+ self.segment_name = val[0]
+ self.allowed = tuple(val[0:])
+
+ def text(self):
+ args = (self.segment_name,) + self.allowed
+ return 'any_of = %s' % (args,)
+
+ phash = text
+
+ def __call__(self, info, request):
+ return info['match'][self.segment_name] in self.allowed
- num_one_two_or_three = any_of('num', 'one', 'two', 'three')
- config.add_route('route_to_num', '/{num}',
- custom_predicates=(num_one_two_or_three,))
+ config.add_route_predicate('any_of', AnyOfPredicate)
+ config.add_route('route_to_num', '/{num}', any_of=('num', 'one', 'two', 'three'))
-The above ``any_of`` function generates a predicate which ensures that the
-match value named ``segment_name`` is in the set of allowable values
-represented by ``allowed``. We use this ``any_of`` function to generate a
-predicate function named ``num_one_two_or_three``, which ensures that the
-``num`` segment is one of the values ``one``, ``two``, or ``three`` , and use
-the result as a custom predicate by feeding it inside a tuple to the
-``custom_predicates`` argument to
-:meth:`~pyramid.config.Configurator.add_route`.
+
+We register this class as a :term:`predicate factory` with the ``any_of`` keyword argument name.
+Then we use that new keyword argument with :meth:`~pyramid.config.Configurator.add_route`.
+When the route is requested, Pyramid instantiates the ``AnyOfPredicate`` class using the value passed to the ``any_of`` argument.
+(The ``info`` parameter passed to the factory contains some metadata, you can ignore it for now.)
+The resulting instance is a :term:`predicate`.
+It will determine whether incoming requests satisfy its condition.
+In the example above, a request for ``/three`` would match the route's URL pattern and satisfy the route's predicate because ``three`` is one of the allowed values, so the route would be matched.
+However a request for ``/millions`` will match the route's URL pattern but would not satisfy the route's predicate, and the route would not be matched.
A custom route predicate may also *modify* the ``match`` dictionary. For
instance, a predicate might do some type conversion of values:
.. code-block:: python
- :linenos:
+ :linenos:
- def integers(*segment_names):
- def predicate(info, request):
- match = info['match']
- for segment_name in segment_names:
- try:
- match[segment_name] = int(match[segment_name])
- except (TypeError, ValueError):
- pass
- return True
- return predicate
+ class IntegersPredicate:
+ def __init__(self, val, info):
+ self.segment_names = val
- ymd_to_int = integers('year', 'month', 'day')
+ def text(self):
+ return 'integers = %s' % (self.segment_names,)
- config.add_route('ymd', '/{year}/{month}/{day}',
- custom_predicates=(ymd_to_int,))
+ phash = text
+
+ def __call__(self, info, request):
+ match = info['match']
+ for segment_name in self.segment_names:
+ try:
+ match[segment_name] = int(match[segment_name])
+ except (TypeError, ValueError):
+ pass
+ return True
+
+
+ config.add_route_predicate('integers', IntegersPredicate)
+ config.add_route('ymd', '/{year}/{month}/{day}',
+ integers=('year', 'month', 'day'))
Note that a conversion predicate is still a predicate, so it must return
``True`` or ``False``. A predicate that does *only* conversion, such as the one
@@ -1157,18 +1173,25 @@ expressions specifying requirements for that marker. For instance:
.. code-block:: python
:linenos:
- def integers(*segment_names):
- def predicate(info, request):
+ class IntegersPredicate:
+ def __init__(self, val, info):
+ self.segment_names = val
+
+ def text(self):
+ return 'integers = %s' % (self.segment_names,)
+
+ phash = text
+
+ def __call__(self, info, request):
match = info['match']
- for segment_name in segment_names:
+ for segment_name in self.segment_names:
match[segment_name] = int(match[segment_name])
return True
- return predicate
- ymd_to_int = integers('year', 'month', 'day')
- config.add_route('ymd', '/{year:\d+}/{month:\d+}/{day:\d+}',
- custom_predicates=(ymd_to_int,))
+ config.add_route_predicate('integers', IntegersPredicate)
+ config.add_route('ymd', r'/{year:\d+}/{month:\d+}/{day:\d+}',
+ integers=('year', 'month', 'day'))
Now the try/except is no longer needed because the route will not match at all
unless these markers match ``\d+`` which requires them to be valid digits for
@@ -1199,61 +1222,40 @@ route in a set of route predicates:
.. code-block:: python
:linenos:
- def twenty_ten(info, request):
- if info['route'].name in ('ymd', 'ym', 'y'):
- return info['match']['year'] == '2010'
-
- config.add_route('y', '/{year}', custom_predicates=(twenty_ten,))
- config.add_route('ym', '/{year}/{month}', custom_predicates=(twenty_ten,))
- config.add_route('ymd', '/{year}/{month}/{day}',
- custom_predicates=(twenty_ten,))
-
-The above predicate, when added to a number of route configurations ensures
-that the year match argument is '2010' if and only if the route name is 'ymd',
-'ym', or 'y'.
-
-You can also caption the predicates by setting the ``__text__`` attribute. This
-will help you with the ``pviews`` command (see
-:ref:`displaying_application_routes`) and the ``pyramid_debugtoolbar``.
-
-If a predicate is a class, just add ``__text__`` property in a standard manner.
-
-.. code-block:: python
- :linenos:
+ class TwentyTenPredicate:
+ def __init__(self, val, info):
+ pass
- class DummyCustomPredicate1(object):
- def __init__(self):
- self.__text__ = 'my custom class predicate'
+ def text(self):
+ return "twenty_ten = True"
- class DummyCustomPredicate2(object):
- __text__ = 'my custom class predicate'
+ phash = text
-If a predicate is a method, you'll need to assign it after method declaration
-(see `PEP 232 <https://www.python.org/dev/peps/pep-0232/>`_).
+ def __call__(self, info, request):
+ if info['route'].name in ('ymd', 'ym', 'y'):
+ return info['match']['year'] == '2010'
-.. code-block:: python
- :linenos:
+ config.add_route_predicate('twenty_ten', TwentyTenPredicate)
+ config.add_route('y', '/{year}', twenty_ten=True)
+ config.add_route('ym', '/{year}/{month}', twenty_ten=True)
+ config.add_route('ymd', '/{year}/{month}/{day}', twenty_ten=True)
- def custom_predicate():
- pass
- custom_predicate.__text__ = 'my custom method predicate'
+The above predicate, when added to a number of route configurations ensures that the year match
+argument is ``2010`` if and only if the route name is ``ymd``, ``ym``, or ``y``.
-If a predicate is a classmethod, using ``@classmethod`` will not work, but you
-can still easily do it by wrapping it in a classmethod call.
+The ``text`` method is a way to caption the predicates. This will help you with the ``pviews``
+command (see :ref:`displaying_application_routes`) and the :term:`pyramid_debugtoolbar`.
-.. code-block:: python
- :linenos:
-
- def classmethod_predicate():
- pass
- classmethod_predicate.__text__ = 'my classmethod predicate'
- classmethod_predicate = classmethod(classmethod_predicate)
+The ``phash`` ("predicate hash") method should return a string that uniquely identifies a specific predicate.
+A good way to do that is to use the same argument name and value that are in the call to ``add_route``,
+like in the examples above.
-The same will work with ``staticmethod``, using ``staticmethod`` instead of
-``classmethod``.
.. seealso::
+ See :ref:`registering_custom_predicates` for more information about
+ custom view, route, and subscriber predicates.
+
See also :class:`pyramid.interfaces.IRoute` for more API documentation
about route objects.
diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst
index 6a49e02a5..5ba965def 100644
--- a/docs/narr/viewconfig.rst
+++ b/docs/narr/viewconfig.rst
@@ -317,6 +317,8 @@ Non-Predicate Arguments
.. versionadded:: 1.8
+.. _predicate_view_args:
+
Predicate Arguments
+++++++++++++++++++
@@ -506,21 +508,23 @@ configured view.
``custom_predicates``
If ``custom_predicates`` is specified, it must be a sequence of references to
- custom predicate callables. Use custom predicates when no set of predefined
- predicates do what you need. Custom predicates can be combined with
+ custom predicate callables. Custom predicates can be combined with
predefined predicates as necessary. Each custom predicate callable should
accept two arguments, ``context`` and ``request``, and should return either
``True`` or ``False`` after doing arbitrary evaluation of the context
resource and/or the request. If all callables return ``True``, the
associated view callable will be considered viable for a given request.
+ This parameter is kept around for backward compatibility.
- If ``custom_predicates`` is not specified, no custom predicates are used.
+ .. deprecated:: 1.5
+ See section below for new-style custom predicates.
-``predicates``
- Pass a key/value pair here to use a third-party predicate registered via
- :meth:`pyramid.config.Configurator.add_view_predicate`. More than one
- key/value pair can be used at the same time. See
- :ref:`view_and_route_predicates` for more information about third-party
+``**predicates``
+ Extra keyword parameters are used to invoke custom predicates, defined
+ in your app or by third-party packages extending Pyramid and registered via
+ :meth:`pyramid.config.Configurator.add_view_predicate`. Use custom predicates
+ when no set of predefined predicates do what you need. See
+ :ref:`view_and_route_predicates` for more information about custom
predicates.
.. versionadded:: 1.4a1