summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmos Latteier <amos@latteier.com>2016-06-02 17:03:18 -0700
committerAmos Latteier <amos@latteier.com>2016-06-02 17:03:18 -0700
commit93c94b9ea69d25da63604e494f89d8c619e7babb (patch)
treea64d97777d74fe4936cfe9c98ae2a6ac8898d365
parentd6c90d154b74107da325a1c45c7a2c4f35ea03c5 (diff)
downloadpyramid-93c94b9ea69d25da63604e494f89d8c619e7babb.tar.gz
pyramid-93c94b9ea69d25da63604e494f89d8c619e7babb.tar.bz2
pyramid-93c94b9ea69d25da63604e494f89d8c619e7babb.zip
Add exception_view_config decorator.
-rw-r--r--pyramid/tests/test_view.py46
-rw-r--r--pyramid/view.py51
2 files changed, 95 insertions, 2 deletions
diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py
index 2de44d579..d18c6eca4 100644
--- a/pyramid/tests/test_view.py
+++ b/pyramid/tests/test_view.py
@@ -132,7 +132,49 @@ class Test_forbidden_view_config(BaseTest, unittest.TestCase):
self.assertEqual(settings[0]['view'], None) # comes from call_venusian
self.assertEqual(settings[0]['attr'], 'view')
self.assertEqual(settings[0]['_info'], 'codeinfo')
-
+
+class Test_exception_view_config(BaseTest, unittest.TestCase):
+ def _makeOne(self, **kw):
+ from pyramid.view import exception_view_config
+ return exception_view_config(**kw)
+
+ def test_ctor(self):
+ inst = self._makeOne(context=Exception, path_info='path_info')
+ self.assertEqual(inst.__dict__,
+ {'context':Exception, 'path_info':'path_info'})
+
+ def test_it_function(self):
+ def view(request): pass
+ decorator = self._makeOne(context=Exception, renderer='renderer')
+ venusian = DummyVenusian()
+ decorator.venusian = venusian
+ wrapped = decorator(view)
+ self.assertTrue(wrapped is view)
+ config = call_venusian(venusian)
+ settings = config.settings
+ self.assertEqual(
+ settings,
+ [{'venusian': venusian, 'context': Exception,
+ 'renderer': 'renderer', '_info': 'codeinfo', 'view': None}]
+ )
+
+ def test_it_class(self):
+ decorator = self._makeOne()
+ venusian = DummyVenusian()
+ decorator.venusian = venusian
+ decorator.venusian.info.scope = 'class'
+ class view(object): pass
+ wrapped = decorator(view)
+ self.assertTrue(wrapped is view)
+ config = call_venusian(venusian)
+ settings = config.settings
+ self.assertEqual(len(settings), 1)
+ self.assertEqual(len(settings[0]), 4)
+ self.assertEqual(settings[0]['venusian'], venusian)
+ self.assertEqual(settings[0]['view'], None) # comes from call_venusian
+ self.assertEqual(settings[0]['attr'], 'view')
+ self.assertEqual(settings[0]['_info'], 'codeinfo')
+
class RenderViewToResponseTests(BaseTest, unittest.TestCase):
def _callFUT(self, *arg, **kw):
from pyramid.view import render_view_to_response
@@ -898,7 +940,7 @@ class DummyConfig(object):
def add_view(self, **kw):
self.settings.append(kw)
- add_notfound_view = add_forbidden_view = add_view
+ add_notfound_view = add_forbidden_view = add_exception_view = add_view
def with_package(self, pkg):
self.pkg = pkg
diff --git a/pyramid/view.py b/pyramid/view.py
index 88c6397af..5a9f2a068 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -463,6 +463,57 @@ class forbidden_view_config(object):
settings['_info'] = info.codeinfo # fbo "action_method"
return wrapped
+class exception_view_config(object):
+ """
+ .. versionadded:: 1.8
+
+ An analogue of :class:`pyramid.view.view_config` which registers an
+ exception view.
+
+ The exception_view_config constructor requires an exception context, and
+ additionally accepts most of the same argumenta as the constructor of
+ :class:`pyramid.view.view_config`. It can be used in the same places,
+ and behaves in largely the same way, except it always registers an exception
+ view instead of a 'normal' view.
+
+ Example:
+
+ .. code-block:: python
+
+ from pyramid.view import exception_view_config
+ from pyramid.response import Response
+
+ @exception_view_config(context=ValueError)
+ def error_view(request):
+ return Response('A value error ocurred')
+
+ All arguments passed to this function have the same meaning as
+ :meth:`pyramid.view.view_config` and each predicate argument restricts
+ the set of circumstances under which this exception view will be invoked.
+ """
+
+ def __init__(self, **settings):
+ self.__dict__.update(settings)
+
+ def __call__(self, wrapped):
+ settings = self.__dict__.copy()
+
+ def callback(context, name, ob):
+ config = context.config.with_package(info.module)
+ config.add_exception_view(view=ob, **settings)
+
+ info = self.venusian.attach(wrapped, callback, category='pyramid')
+
+ if info.scope == 'class':
+ # if the decorator was attached to a method in a class, or
+ # otherwise executed at class scope, we need to set an
+ # 'attr' into the settings if one isn't already in there
+ if settings.get('attr') is None:
+ settings['attr'] = wrapped.__name__
+
+ settings['_info'] = info.codeinfo # fbo "action_method"
+ return wrapped
+
def _find_views(
registry,
request_iface,