summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst11
-rw-r--r--docs/api/interfaces.rst21
-rw-r--r--docs/narr/hooks.rst16
-rw-r--r--src/pyramid/config/predicates.py16
-rw-r--r--src/pyramid/interfaces.py85
-rw-r--r--tests/test_config/test_predicates.py6
6 files changed, 146 insertions, 9 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 400e6d896..e2d5dbbac 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -46,6 +46,10 @@ Features
``Referer`` header for privacy reasons.
See https://github.com/Pylons/pyramid/pull/3512
+- Added ``pyramid.interfaces.IPredicateInfo`` which defines the object passed
+ to predicate factories as their second argument.
+ See https://github.com/Pylons/pyramid/pull/3514
+
Deprecations
------------
@@ -109,6 +113,13 @@ Backward Incompatibilities
``config.scan(..., categories=None)``.
See https://github.com/Pylons/pyramid/pull/3510
+- The second argument to predicate factories has been changed from ``config``
+ to ``info``, an instance of ``pyramid.interfaces.IPredicateInfo``. This
+ limits the data available to predicates but still provides the package,
+ registry, settings and dotted-name resolver which should cover most use
+ cases and is largely backward compatible.
+ See https://github.com/Pylons/pyramid/pull/3514
+
Documentation Changes
---------------------
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index e542a6be0..08c67c385 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -109,3 +109,24 @@ Other Interfaces
.. autointerface:: IViewDeriverInfo
:members:
+
+ .. autointerface:: IPredicateFactory
+ :members:
+
+ .. autointerface:: IPredicateInfo
+ :members:
+
+ .. autointerface:: IPredicate
+ :members:
+
+ .. autointerface:: IRoutePredicate
+ :inherited-members:
+ :members:
+
+ .. autointerface:: ISubscriberPredicate
+ :inherited-members:
+ :members:
+
+ .. autointerface:: IViewPredicate
+ :inherited-members:
+ :members:
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 1ca5c3a6d..4a594a8c9 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -1482,7 +1482,7 @@ method. For example:
:linenos:
class ContentTypePredicate(object):
- def __init__(self, val, config):
+ def __init__(self, val, info):
self.val = val
def text(self):
@@ -1493,11 +1493,11 @@ method. For example:
def __call__(self, context, request):
return request.content_type == self.val
-The constructor of a predicate factory takes two arguments: ``val`` and
-``config``. The ``val`` argument will be the argument passed to
-``view_config`` (or ``add_view``). In the example above, it will be the string
-``File``. The second argument, ``config``, will be the Configurator instance
-at the time of configuration.
+The constructor of an :class:`pyramid.interfaces.IPredicateFactory`` takes two arguments: ``val`` and ``info``.
+The ``val`` argument will be the argument passed to ``view_config`` (or ``add_view``).
+In the example above, it will be the string ``File``.
+The second argument, ``info``, will be an :class:`pyramid.interfaces.IPredicateInfo` instance created relative to the action configuring the predicate.
+This means the ``info.package`` value is the package where the action is invoked passing in ``val`` and ``info.maybe_dotted`` is also relative to this package.
The ``text`` method must return a string. It should be useful to describe the
behavior of the predicate in error messages.
@@ -1524,7 +1524,7 @@ a :term:`view predicate` or a :term:`route predicate`:
performed using either the route's :term:`root factory` or the app's
:term:`default root factory`.
-In both cases the ``__call__`` method is expected to return ``True`` or
+In all cases the ``__call__`` method is expected to return ``True`` or
``False``.
It is possible to use the same predicate factory as both a view predicate and
@@ -1560,7 +1560,7 @@ event type.
:linenos:
class RequestPathStartsWith(object):
- def __init__(self, val, config):
+ def __init__(self, val, info):
self.val = val
def text(self):
diff --git a/src/pyramid/config/predicates.py b/src/pyramid/config/predicates.py
index 3eb07c17d..ffdff5c21 100644
--- a/src/pyramid/config/predicates.py
+++ b/src/pyramid/config/predicates.py
@@ -98,6 +98,14 @@ class not_(object):
# over = before
+class PredicateInfo(object):
+ def __init__(self, package, registry, settings, maybe_dotted):
+ self.package = package
+ self.registry = registry
+ self.settings = settings
+ self.maybe_dotted = maybe_dotted
+
+
class PredicateList(object):
def __init__(self):
self.sorter = TopologicalSorter()
@@ -134,6 +142,12 @@ class PredicateList(object):
phash = md5()
weights = []
preds = []
+ info = PredicateInfo(
+ package=config.package,
+ registry=config.registry,
+ settings=config.get_settings(),
+ maybe_dotted=config.maybe_dotted,
+ )
for n, (name, predicate_factory) in enumerate(ordered):
vals = kw.pop(name, None)
if vals is None: # XXX should this be a sentinel other than None?
@@ -146,7 +160,7 @@ class PredicateList(object):
if isinstance(val, not_):
realval = val.value
notted = True
- pred = predicate_factory(realval, config)
+ pred = predicate_factory(realval, info)
if notted:
pred = Notted(pred)
hashes = pred.phash()
diff --git a/src/pyramid/interfaces.py b/src/pyramid/interfaces.py
index 2d8b1ac40..688293509 100644
--- a/src/pyramid/interfaces.py
+++ b/src/pyramid/interfaces.py
@@ -1440,6 +1440,91 @@ class IPredicateList(Interface):
""" Interface representing a predicate list """
+class IPredicateInfo(Interface):
+ package = Attribute(
+ 'The "current package" where the predicate '
+ 'configuration statement was found'
+ )
+ registry = Attribute(
+ 'The "current" application registry where the predicate was invoked'
+ )
+ settings = Attribute(
+ 'The deployment settings dictionary related '
+ 'to the current application'
+ )
+
+ def maybe_dotted(value):
+ """ Resolve the :term:`dotted Python name` ``dotted`` to a
+ global Python object. If ``dotted`` is not a string, return
+ it without attempting to do any name resolution. If
+ ``dotted`` is a relative dotted name (e.g. ``.foo.bar``,
+ consider it relative to the ``package``."""
+
+
+class IPredicateFactory(Interface):
+ def __call__(value, info):
+ """
+ Create a a :class:`.IPredicate` instance for a specific value.
+
+ """
+
+
+class IPredicate(Interface):
+ def text():
+ """
+ A textual description of the predicate used in the introspector.
+
+ For example, ``'content_type = application/json'`` for a
+ ``ContentTypePredicate`` with a ``value == 'application/json'``.
+
+ """
+
+ def phash():
+ """
+ A unique string for the predicate containing both the name and value.
+
+ Often implementations simply set ``phash = text``.
+
+ """
+
+
+class IRoutePredicate(IPredicate):
+ def __call__(info, request):
+ """
+ The ``info`` object is a dictionary containing two keys:
+
+ - "match" is a dictionary of parameters that becomes
+ ``request.matchdict`` if the route is selected
+ (all route predicates match).
+
+ - "route" is the :class:`.IRoute` object being matched.
+
+ Return ``True`` if the route should be selected or ``False`` otherwise.
+
+ """
+
+
+class ISubscriberPredicate(IPredicate):
+ def __call__(*args):
+ """
+ The ``args`` is usually just a single ``event`` argument sent to
+ ``registry.notify``.
+
+ Return ``True`` if the subscriber should be executed for the given
+ arguments or ``False`` otherwise.
+
+ """
+
+
+class IViewPredicate(IPredicate):
+ def __call__(context, request):
+ """
+ Return ``True`` if the view should be selected for the given
+ arguments or ``False`` otherwise.
+
+ """
+
+
class IViewDeriver(Interface):
options = Attribute(
'A list of supported options to be passed to '
diff --git a/tests/test_config/test_predicates.py b/tests/test_config/test_predicates.py
index c27b41639..f8abbbce4 100644
--- a/tests/test_config/test_predicates.py
+++ b/tests/test_config/test_predicates.py
@@ -476,5 +476,11 @@ class DummyRequest:
class DummyConfigurator(object):
+ package = 'dummy package'
+ registry = 'dummy registry'
+
+ def get_settings(self):
+ return {}
+
def maybe_dotted(self, thing):
return thing