summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-10-10 02:30:50 +0000
committerChris McDonough <chrism@agendaless.com>2009-10-10 02:30:50 +0000
commitce8f9b5eadf324b4cc3154004bcf9f1f03e7d6c8 (patch)
tree6dd39091d8e27561541a9ed8ea4f1d0d54b4298f
parent048496dbbbbd6679afca52387ddbea2aefd596dd (diff)
downloadpyramid-ce8f9b5eadf324b4cc3154004bcf9f1f03e7d6c8.tar.gz
pyramid-ce8f9b5eadf324b4cc3154004bcf9f1f03e7d6c8.tar.bz2
pyramid-ce8f9b5eadf324b4cc3154004bcf9f1f03e7d6c8.zip
- The ``notfound`` and ``forbidden`` ZCML directives now accept the
following addtional attributes: ``attr``, ``renderer``, and ``wrapper``. These have the same meaning as they do in the context of a ZCML ``view`` directive.
-rw-r--r--CHANGES.txt5
-rw-r--r--docs/narr/hooks.rst77
-rw-r--r--repoze/bfg/includes/meta.zcml4
-rw-r--r--repoze/bfg/tests/test_zcml.py73
-rw-r--r--repoze/bfg/zcml.py51
5 files changed, 192 insertions, 18 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 65365ba65..a16e41296 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,6 +4,11 @@ Next release
Documentation
-------------
+- The ``notfound`` and ``forbidden`` ZCML directives now accept the
+ following addtional attributes: ``attr``, ``renderer``, and
+ ``wrapper``. These have the same meaning as they do in the context
+ of a ZCML ``view`` directive.
+
- Added a diagram of model graph traversal to the "Traversal"
narrative chapter of the documentation.
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 65e21d3f2..42d354c89 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -40,6 +40,44 @@ implements a minimal NotFound view:
This error will be different when the ``debug_notfound``
environment setting is true than it is when it is false.
+Other available attributes of the ``notfound`` ZCML directive are as
+follows:
+
+attr
+
+ The attribute of the view callable to use if ``__call__`` is not
+ correct (has the same meaning as in the context of
+ :ref:`the_view_zcml_directive`; see the description of ``attr``
+ there).
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
+renderer
+
+ This is either a single string term (e.g. ``json``) or a string
+ implying a path or :term:`resource specification`
+ (e.g. ``templates/views.pt``) used when the view returns a
+ non-:term:`response` object. This attribute has the same meaning as
+ it would in the context of :ref:`the_view_zcml_directive`; see the
+ description of ``renderer`` there).
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
+wrapper
+
+ The :term:`view name` (*not* an object dotted name) of another view
+ declared elsewhere in ZCML (or via the ``@bfg_view`` decorator)
+ which will receive the response body of this view as the
+ ``request.wrapped_body`` attribute of its own request, and the
+ response returned by this view as the ``request.wrapped_response``
+ attribute of its own request. This attribute has the same meaning
+ as it would in the context of :ref:`the_view_zcml_directive`; see
+ the description of ``wrapper`` there). Note that the wrapper view
+ *should not* be protected by any permission; behavior is undefined
+ if it does.
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
.. _changing_the_forbidden_view:
Changing the Forbidden View
@@ -86,6 +124,45 @@ code that implements a minimal forbidden view:
an alternate forbidden view. For example, it would make sense to
return a response with a ``403 Forbidden`` status code.
+Other available attributes of the ``forbidden`` ZCML directive are as
+follows:
+
+attr
+
+ The attribute of the view callable to use if ``__call__`` is not
+ correct (has the same meaning as in the context of
+ :ref:`the_view_zcml_directive`; see the description of ``attr``
+ there).
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
+renderer
+
+ This is either a single string term (e.g. ``json``) or a string
+ implying a path or :term:`resource specification`
+ (e.g. ``templates/views.pt``) used when the view returns a
+ non-:term:`response` object. This attribute has the same meaning as
+ it would in the context of :ref:`the_view_zcml_directive`; see the
+ description of ``renderer`` there).
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
+wrapper
+
+ The :term:`view name` (*not* an object dotted name) of another view
+ declared elsewhere in ZCML (or via the ``@bfg_view`` decorator)
+ which will receive the response body of this view as the
+ ``request.wrapped_body`` attribute of its own request, and the
+ response returned by this view as the ``request.wrapped_response``
+ attribute of its own request. This attribute has the same meaning
+ as it would in the context of :ref:`the_view_zcml_directive`; see
+ the description of ``wrapper`` there). Note that the wrapper view
+ *should not* be protected by any permission; behavior is undefined
+ if it does.
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
+
Changing the response factory
-----------------------------
diff --git a/repoze/bfg/includes/meta.zcml b/repoze/bfg/includes/meta.zcml
index 2eeb87314..4c9d18873 100644
--- a/repoze/bfg/includes/meta.zcml
+++ b/repoze/bfg/includes/meta.zcml
@@ -18,13 +18,13 @@
<meta:directive
name="notfound"
- schema="repoze.bfg.zcml.INotFoundViewDirective"
+ schema="repoze.bfg.zcml.ISystemViewDirective"
handler="repoze.bfg.zcml.notfound"
/>
<meta:directive
name="forbidden"
- schema="repoze.bfg.zcml.IForbiddenViewDirective"
+ schema="repoze.bfg.zcml.ISystemViewDirective"
handler="repoze.bfg.zcml.forbidden"
/>
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index 35584f301..7cbadd1e2 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -947,6 +947,12 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(wrapper, view)
class TestNotFoundDirective(unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
def _callFUT(self, context, view):
from repoze.bfg.zcml import notfound
return notfound(context, view)
@@ -972,6 +978,12 @@ class TestNotFoundDirective(unittest.TestCase):
self.assertEqual(derived_view.__name__, view.__name__)
class TestForbiddenDirective(unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
def _callFUT(self, context, view):
from repoze.bfg.zcml import forbidden
return forbidden(context, view)
@@ -996,6 +1008,67 @@ class TestForbiddenDirective(unittest.TestCase):
self.assertEqual(derived_view(None, None), 'OK')
self.assertEqual(derived_view.__name__, view.__name__)
+class TestViewUtility(unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
+ def _callFUT(self, context, view, attr, renderer, wrapper, iface):
+ from repoze.bfg.zcml import view_utility
+ return view_utility(context, view, attr, renderer, wrapper, iface)
+
+ def test_no_view_no_renderer(self):
+ from zope.configuration.exceptions import ConfigurationError
+ context = DummyContext()
+ self.assertRaises(ConfigurationError, self._callFUT, context,
+ None, None, None, None, None)
+
+ def test_no_view_with_renderer(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRendererFactory
+ sm = getSiteManager()
+ def renderer(path):
+ return lambda *arg: 'OK'
+ sm.registerUtility(renderer, IRendererFactory, name='dummy')
+ class IDummy(Interface):
+ pass
+ context = DummyContext()
+ self._callFUT(context, None, None, 'dummy', None, IDummy)
+ actions = context.actions
+ self.assertEqual(len(actions), 1)
+ regadapt = actions[0]
+ self.assertEqual(regadapt['discriminator'], IDummy)
+ register = regadapt['callable']
+ register()
+ derived_view = sm.getUtility(IDummy)
+ request = DummyRequest()
+ self.assertEqual(derived_view(None, request).body, 'OK')
+
+ def test_template_renderer(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRendererFactory
+ sm = getSiteManager()
+ def renderer(path):
+ return lambda *arg: 'OK'
+ sm.registerUtility(renderer, IRendererFactory, name='.pt')
+ class IDummy(Interface):
+ pass
+ context = DummyContext()
+ self._callFUT(context, None, None, 'fixtures/minimal.pt', None, IDummy)
+ actions = context.actions
+ self.assertEqual(len(actions), 1)
+ regadapt = actions[0]
+ self.assertEqual(regadapt['discriminator'], IDummy)
+ register = regadapt['callable']
+ register()
+ derived_view = sm.getUtility(IDummy)
+ request = DummyRequest()
+ self.assertEqual(derived_view(None, request).body, 'OK')
+
class TestRepozeWho1AuthenticationPolicyDirective(unittest.TestCase):
def setUp(self):
cleanUp()
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index 3faadb24b..adb1f1f31 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -279,29 +279,49 @@ def view(
_view = view # for directives that take a view arg
-class INotFoundViewDirective(Interface):
+class ISystemViewDirective(Interface):
view = GlobalObject(
title=u"",
- description=u"The notfound view callable",
- required=True,
+ description=u"The view function",
+ required=False,
)
-def notfound(_context, view):
- view_utility(_context, view, INotFoundView)
+ attr = TextLine(
+ title=u'The callable attribute of the view object(default is __call__)',
+ description=u'',
+ required=False)
-class IForbiddenViewDirective(Interface):
- view = GlobalObject(
- title=u"",
- description=u"The forbidden view callable",
- required=True,
- )
+ renderer = TextLine(
+ title=u'The renderer asssociated with the view',
+ description=u'',
+ required=False)
-def forbidden(_context, view):
- view_utility(_context, view, IForbiddenView)
+ wrapper = TextLine(
+ title = u'The *name* of the view that acts as a wrapper for this view.',
+ description = u'',
+ required=False)
+
+def notfound(_context, view=None, attr=None, renderer=None, wrapper=None):
+ view_utility(_context, view, attr, renderer, wrapper, INotFoundView)
+
+def forbidden(_context, view=None, attr=None, renderer=None, wrapper=None):
+ view_utility(_context, view, attr, renderer, wrapper, IForbiddenView)
+
+def view_utility(_context, view, attr, renderer, wrapper, iface):
+ if not view:
+ if renderer:
+ def view(context, request):
+ return {}
+ else:
+ raise ConfigurationError('"view" attribute was not specified and '
+ 'no renderer specified')
+
+ if renderer and '.' in renderer:
+ renderer = resource_spec(renderer, package_name(_context.resolve('.')))
-def view_utility(_context, view, iface):
def register():
- derived_view = derive_view(view)
+ derived_view = derive_view(view, attr=attr, renderer_name=renderer,
+ wrapper_viewname=wrapper)
sm = getSiteManager()
sm.registerUtility(derived_view, iface, '', _context.info)
@@ -310,7 +330,6 @@ def view_utility(_context, view, iface):
callable = register,
)
-
class IResourceDirective(Interface):
"""
Directive for specifying that one package may override resources from