summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2016-03-09 00:46:59 -0600
committerMichael Merickel <michael@merickel.org>2016-03-09 00:46:59 -0600
commite4b931a67455f14d84d415be49e6596d06cc0a42 (patch)
tree630d43ac7e725627e6ca557d5d57eb9c590795f3
parent46fd86919578221b339a18de90e926ef12e764d3 (diff)
downloadpyramid-e4b931a67455f14d84d415be49e6596d06cc0a42.tar.gz
pyramid-e4b931a67455f14d84d415be49e6596d06cc0a42.tar.bz2
pyramid-e4b931a67455f14d84d415be49e6596d06cc0a42.zip
add options support to view derivers
exposed a new IViewDeriver api with an optional ``options`` list to expose support for new kwargs that may be passed to config.add_view
-rw-r--r--docs/api/interfaces.rst6
-rw-r--r--pyramid/config/derivations.py16
-rw-r--r--pyramid/config/views.py15
-rw-r--r--pyramid/interfaces.py20
-rw-r--r--pyramid/tests/test_config/test_derivations.py11
-rw-r--r--pyramid/util.py3
6 files changed, 68 insertions, 3 deletions
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index de2a664a4..635d3c5b6 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -91,3 +91,9 @@ Other Interfaces
.. autointerface:: ICacheBuster
:members:
+
+ .. autointerface:: IViewDeriver
+ :members:
+
+ .. autointerface:: IViewDeriverInfo
+ :members:
diff --git a/pyramid/config/derivations.py b/pyramid/config/derivations.py
index 0e109f425..99baf46f9 100644
--- a/pyramid/config/derivations.py
+++ b/pyramid/config/derivations.py
@@ -205,6 +205,8 @@ def mapped_view(view, info):
mapped_view = mapper(**info.options)(view)
return mapped_view
+mapped_view.options = ('mapper', 'attr')
+
def owrapped_view(view, info):
wrapper_viewname = info.options.get('wrapper')
viewname = info.options.get('name')
@@ -224,6 +226,8 @@ def owrapped_view(view, info):
return wrapped_response
return _owrapped_view
+owrapped_view.options = ('name', 'wrapper')
+
def http_cached_view(view, info):
if info.settings.get('prevent_http_cache', False):
return view
@@ -253,6 +257,8 @@ def http_cached_view(view, info):
return wrapper
+http_cached_view.options = ('http_cache',)
+
def secured_view(view, info):
permission = info.options.get('permission')
if permission == NO_PERMISSION_REQUIRED:
@@ -285,6 +291,8 @@ def secured_view(view, info):
return wrapped_view
+secured_view.options = ('permission',)
+
def authdebug_view(view, info):
wrapped_view = view
settings = info.settings
@@ -322,6 +330,8 @@ def authdebug_view(view, info):
return wrapped_view
+authdebug_view.options = ('permission',)
+
def predicated_view(view, info):
preds = info.predicates
if not preds:
@@ -363,6 +373,8 @@ def attr_wrapped_view(view, info):
attr_view.__permission__ = info.options.get('permission')
return attr_view
+attr_wrapped_view.options = ('accept', 'attr', 'permission')
+
def rendered_view(view, info):
# one way or another this wrapper must produce a Response (unless
# the renderer is a NullRendererHelper)
@@ -430,8 +442,12 @@ def rendered_view(view, info):
return rendered_view
+rendered_view.options = ('renderer',)
+
def decorated_view(view, info):
decorator = info.options.get('decorator')
if decorator is None:
return view
return decorator(view)
+
+decorated_view.options = ('decorator',)
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index 6511ecd1e..c3e5d360e 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -755,10 +755,15 @@ class ViewsConfiguratorMixin(object):
# called.
valid_predicates = predlist.names()
pvals = {}
+ dvals = {}
for (k, v) in ovals.items():
if k in valid_predicates:
pvals[k] = v
+ else:
+ dvals[k] = v
+
+ self._check_view_options(**dvals)
order, preds, phash = predlist.make(self, **pvals)
@@ -1012,6 +1017,16 @@ class ViewsConfiguratorMixin(object):
introspectables.append(perm_intr)
self.action(discriminator, register, introspectables=introspectables)
+ def _check_view_options(self, **kw):
+ # we only need to validate deriver options because the predicates
+ # were checked by the predlist
+ derivers = self.registry.getUtility(IViewDerivers)
+ for deriver in derivers.values():
+ for opt in getattr(deriver, 'options', []):
+ kw.pop(opt, None)
+ if kw:
+ raise ConfigurationError('Unknown view options: %s' % (kw,))
+
def _apply_view_derivers(self, info):
d = pyramid.config.derivations
# These inner derivations have fixed order
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index 6791befce..692bf3d6d 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -1187,8 +1187,21 @@ class IJSONAdapter(Interface):
class IPredicateList(Interface):
""" Interface representing a predicate list """
-class IViewDerivers(Interface):
- """ Interface for view derivers list """
+class IViewDeriver(Interface):
+ options = Attribute('An list of supported options to be passed to '
+ ':meth:`pyramid.config.Configurator.add_view`. '
+ 'This attribute is optional.')
+
+ def __call__(view, info):
+ """
+ Derive a new view from the supplied view.
+
+ View options, package information and registry are available on
+ ``info``, an instance of :class:`pyramid.interfaces.IViewDeriverInfo`.
+
+ The ``view`` is a callable accepting ``(context, request)``.
+
+ """
class IViewDeriverInfo(Interface):
""" An object implementing this interface is passed to every
@@ -1204,6 +1217,9 @@ class IViewDeriverInfo(Interface):
predicates = Attribute('The list of predicates active on the view')
original_view = Attribute('The original view object being wrapped')
+class IViewDerivers(Interface):
+ """ Interface for view derivers list """
+
class ICacheBuster(Interface):
"""
A cache buster modifies the URL generation machinery for
diff --git a/pyramid/tests/test_config/test_derivations.py b/pyramid/tests/test_config/test_derivations.py
index ec7ef1434..12fcc300a 100644
--- a/pyramid/tests/test_config/test_derivations.py
+++ b/pyramid/tests/test_config/test_derivations.py
@@ -1293,15 +1293,16 @@ class TestDeriverIntegration(unittest.TestCase):
def deriv1(view, info):
response.deriv.append(info.options['deriv1'])
return view
+ deriv1.options = ('deriv1',)
def deriv2(view, info):
response.deriv.append(info.options['deriv2'])
return view
+ deriv2.options = ('deriv2',)
self.config.add_view_deriver('deriv1', deriv1)
self.config.add_view_deriver('deriv2', deriv2)
self.config.add_view(view, deriv1='test1', deriv2='test2')
- self.config.commit()
wrapper = self._getViewCallable(self.config)
request = self._makeRequest(self.config)
@@ -1309,6 +1310,14 @@ class TestDeriverIntegration(unittest.TestCase):
self.assertEqual(wrapper(None, request), response)
self.assertEqual(['test1', 'test2'], response.deriv)
+ def test_unexpected_view_options(self):
+ from pyramid.exceptions import ConfigurationError
+ def deriv1(view, info): pass
+ self.config.add_view_deriver('deriv1', deriv1)
+ self.assertRaises(
+ ConfigurationError,
+ lambda: self.config.add_view(lambda r: {}, deriv1='test1'))
+
from zope.interface import implementer
from pyramid.interfaces import (
diff --git a/pyramid/util.py b/pyramid/util.py
index 0a73cedaf..27140936c 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -380,6 +380,9 @@ class TopologicalSorter(object):
self.first = first
self.last = last
+ def values(self):
+ return self.name2val.values()
+
def remove(self, name):
""" Remove a node from the sort input """
self.names.remove(name)