Repository: Pylons/pyramid

Issue:
define a new IPredicateInfo to mirror IDeriverInfo
One annoyance with predicates right now is that their signature is `val, config` which doesn't make much sense. It's not particularly good practice to pass around the config. The only reason it's done right now is because the containment predicate needs to call `config.maybe_dotted`. That's it. It'd be much preferred to be something like `info, val` which could more closely mirror the new `IViewDeriverInfo`. We could then define an `IPredicateInfo` which contained the registry and package information without passing the raw `config`.

This would be a backward compatibility issue, unfortunately, so I'd save it for Pyramid 2.0.



Discussion:

Resolving patch:
diff --git a/src/pyramid/config/predicates.py b/src/pyramid/config/predicates.py
--- a/src/pyramid/config/predicates.py
+++ b/src/pyramid/config/predicates.py
@@ -98,6 +98,14 @@ def __init__(self, value):
 # 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 @@ def make(self, config, **kw):
         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 @@ def make(self, config, **kw):
                 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
--- 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 '


Test patch:
diff --git a/tests/test_config/test_predicates.py b/tests/test_config/test_predicates.py
--- a/tests/test_config/test_predicates.py
+++ b/tests/test_config/test_predicates.py
@@ -476,5 +476,11 @@ def __init__(self, environ=None):
 
 
 class DummyConfigurator(object):
+    package = 'dummy package'
+    registry = 'dummy registry'
+
+    def get_settings(self):
+        return {}
+
     def maybe_dotted(self, thing):
         return thing