summaryrefslogtreecommitdiff
path: root/repoze
diff options
context:
space:
mode:
Diffstat (limited to 'repoze')
-rw-r--r--repoze/bfg/configuration.py70
-rw-r--r--repoze/bfg/tests/test_configuration.py42
2 files changed, 112 insertions, 0 deletions
diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py
index fdc259934..9fa3fe8b2 100644
--- a/repoze/bfg/configuration.py
+++ b/repoze/bfg/configuration.py
@@ -221,6 +221,76 @@ class Configurator(object):
def _split_spec(self, path_or_spec):
return resolve_resource_spec(path_or_spec, self.package.__name__)
+ def derive_view(self, view, attr=None, renderer=None):
+ """
+ Create a :term:`view callable` using the function, instance,
+ or class provided as ``view`` object.
+
+ This is API is useful to framework extenders who create
+ pluggable systems which need to register 'proxy' view
+ callables for functions, instances, or classes which meet the
+ requirements of being a :mod:`repoze.bfg` view callable. For
+ example, a ``some_other_framework`` function in another
+ framework may want to allow a user to supply a view callable,
+ but he may want to wrap the view callable in his own before
+ registering the wrapper as a :mod:`repoze.bfg` view callable.
+ Because a :mod:`repoze.bfg` view callable can be any of a
+ number of valid objects, the framework extender will not know
+ how to call the user-supplied object. Running it through
+ ``derive_view`` normalizes it to a callable which accepts two
+ arguments: ``context`` and ``request``.
+
+ For example:
+
+ .. code-block:: python
+
+ def some_other_framework(user_supplied_view):
+ config = Configurator(reg)
+ proxy_view = config.derive_view(user_supplied_view)
+ def my_wrapper(context, request):
+ do_something_that_mutates(request)
+ return proxy_view(context, request)
+ config.add_view(my_wrapper)
+
+ The ``view`` object provided should be one of the following:
+
+ - A function or another non-class callable object that accepts
+ a :term:`request` as a single positional argument and which
+ returns a :term:`response` object.
+
+ - A function or other non-class callable object that accepts
+ two positional arguments, ``context, request`` and which
+ returns a :term:`response` object.
+
+ - A class which accepts a single positional argument in its
+ constructor named ``request``, and which has a ``__call__``
+ method that accepts no arguments that returns a
+ :term:`response` object.
+
+ - A class which accepts two positional arguments named
+ ``context, request``, and which has a ``__call__`` method
+ that accepts no arguments that returns a :term:`response`
+ object.
+
+ This API returns a callable which accepts the arguments
+ ``context, request`` and which returns the result of calling
+ the provided ``view`` object.
+
+ The ``attr`` keyword argument is most useful when the view
+ object is a class. It names the method that should be used as
+ the callable. If ``attr`` is not provided, the attribute
+ effectively defaults to ``__call__``. See
+ :ref:`class_as_view` for more information.
+
+ The ``renderer`` keyword argument, if supplies, causes the
+ returned callable to use a :term:`renderer` to convert the
+ user-supplied view result to a :term:`response` object. If a
+ ``renderer`` argument is not supplied, the user-supplied view
+ must itself return a :term:`response` object.
+ """
+
+ return self._derive_view(view, attr=attr, renderer_name=renderer)
+
def _derive_view(self, view, permission=None, predicates=(),
attr=None, renderer_name=None, wrapper_viewname=None,
viewname=None, accept=None, order=MAX_ORDER,
diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py
index fa18b75cb..0a443720f 100644
--- a/repoze/bfg/tests/test_configuration.py
+++ b/repoze/bfg/tests/test_configuration.py
@@ -1879,6 +1879,48 @@ class ConfiguratorTests(unittest.TestCase):
result = config._renderer_from_name(None)
self.assertEqual(result, 'OK')
+ def test_derive_view_function(self):
+ def view(request):
+ return 'OK'
+ config = self._makeOne()
+ result = config.derive_view(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None), 'OK')
+
+ def test_derive_view_with_renderer(self):
+ def view(request):
+ return 'OK'
+ config = self._makeOne()
+ class moo(object):
+ def __init__(self, *arg, **kw):
+ pass
+ def __call__(self, *arg, **kw):
+ return 'moo'
+ config.add_renderer('moo', moo)
+ result = config.derive_view(view, renderer='moo')
+ self.failIf(result is view)
+ self.assertEqual(result(None, None).body, 'moo')
+
+ def test_derive_view_class_without_attr(self):
+ class View(object):
+ def __init__(self, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ config = self._makeOne()
+ result = config.derive_view(View)
+ self.assertEqual(result(None, None), 'OK')
+
+ def test_derive_view_class_with_attr(self):
+ class View(object):
+ def __init__(self, request):
+ pass
+ def another(self):
+ return 'OK'
+ config = self._makeOne()
+ result = config.derive_view(View, attr='another')
+ self.assertEqual(result(None, None), 'OK')
+
def test__derive_view_as_function_context_and_request(self):
def view(context, request):
return 'OK'