summaryrefslogtreecommitdiff
path: root/repoze/bfg/view.py
diff options
context:
space:
mode:
Diffstat (limited to 'repoze/bfg/view.py')
-rw-r--r--repoze/bfg/view.py106
1 files changed, 96 insertions, 10 deletions
diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py
index 8e6d1c7fe..794c7fa65 100644
--- a/repoze/bfg/view.py
+++ b/repoze/bfg/view.py
@@ -33,6 +33,9 @@ from repoze.bfg.path import caller_package
from repoze.bfg.static import PackageURLParser
+from repoze.bfg.templating import renderer_from_path
+from repoze.bfg.templating import _auto_reload
+
deprecated('view_execution_permitted',
"('from repoze.bfg.view import view_execution_permitted' was "
"deprecated as of repoze.bfg 1.0; instead use 'from "
@@ -229,6 +232,13 @@ class bfg_view(object):
If ``name`` is not supplied, the empty string is used (implying
the default view name).
+ If ``attr`` is not supplied, ``None`` is used (implying the
+ function itself if the view is a function, or the ``__call__``
+ callable attribute if the view is a class).
+
+ If ``template`` is not supplied, ``None`` is used (meaning that no
+ template is associated with this view).
+
If ``request_type`` is not supplied, the interface
``repoze.bfg.interfaces.IRequest`` is used, implying the standard
request interface type.
@@ -322,7 +332,7 @@ class bfg_view(object):
"""
def __init__(self, name='', request_type=None, for_=None, permission=None,
route_name=None, request_method=None, request_param=None,
- containment=None):
+ containment=None, attr=None, template=None):
self.name = name
self.request_type = request_type
self.for_ = for_
@@ -331,9 +341,11 @@ class bfg_view(object):
self.request_method = request_method
self.request_param = request_param
self.containment = containment
+ self.attr = attr
+ self.template = template
def __call__(self, wrapped):
- _bfg_view = map_view(wrapped)
+ _bfg_view = map_view(wrapped, self.attr, self.template)
_bfg_view.__is_bfg_view__ = True
_bfg_view.__permission__ = self.permission
_bfg_view.__for__ = self.for_
@@ -413,8 +425,41 @@ class MultiView(object):
continue
raise NotFound(self.name)
-def map_view(view):
+def templated_response(template_name, response, view, context, request,
+ auto_reload=False):
+ if is_response(response):
+ return response
+ renderer = renderer_from_path(template_name, auto_reload=auto_reload)
+ kw = {'view':view, 'template_name':template_name, 'context':context,
+ 'request':request}
+ try:
+ kw.update(response)
+ except TypeError:
+ return response
+ result = renderer(**kw)
+ response_factory = queryUtility(IResponseFactory, default=Response)
+ response = response_factory(result)
+ content_type = kw.get('content_type_', None)
+ if content_type is not None:
+ response.content_type = content_type
+ headerlist = kw.get('headerlist_', None)
+ if headerlist is not None:
+ for k, v in headerlist:
+ response.headers.add(k, v)
+ status = kw.get('status_', None)
+ if status is not None:
+ response.status = status
+ charset = kw.get('charset_', None)
+ if charset is not None:
+ response.charset = charset
+ cache_for = kw.get('cache_for_', None)
+ if cache_for is not None:
+ response.cache_expires = cache_for
+ return response
+
+def map_view(view, attr=None, template=None):
wrapped_view = view
+ auto_reload = _auto_reload()
if inspect.isclass(view):
# If the object we've located is a class, turn it into a
@@ -423,33 +468,74 @@ def map_view(view):
# position arguments, then immediately invoke the __call__
# method of the instance with no arguments; __call__ should
# return an IResponse).
- if requestonly(view):
+ if requestonly(view, attr):
# its __init__ accepts only a single request argument,
# instead of both context and request
def _bfg_class_requestonly_view(context, request):
inst = view(request)
- return inst()
+ if attr is None:
+ response = inst()
+ else:
+ response = getattr(inst, attr)()
+ if template:
+ response = templated_response(template, response, inst,
+ context, request, auto_reload)
+ return response
wrapped_view = _bfg_class_requestonly_view
else:
# its __init__ accepts both context and request
def _bfg_class_view(context, request):
inst = view(context, request)
- return inst()
+ if attr is None:
+ response = inst()
+ else:
+ response = getattr(inst, attr)()
+ if template:
+ response = templated_response(template, response, inst,
+ context, request, auto_reload)
+ return response
wrapped_view = _bfg_class_view
- elif requestonly(view):
+ elif requestonly(view, attr):
# its __call__ accepts only a single request argument,
# instead of both context and request
def _bfg_requestonly_view(context, request):
- return view(request)
+ if attr is None:
+ response = view(request)
+ else:
+ response = getattr(view, attr)(request)
+
+ if template:
+ response = templated_response(template, response, view,
+ context, request, auto_reload)
+ return response
wrapped_view = _bfg_requestonly_view
+ elif attr:
+ def _bfg_attr_view(context, request):
+ response = getattr(view, attr)(context, request)
+ if template:
+ response = templated_response(template, response, view,
+ context, request, auto_reload)
+ return response
+ wrapped_view = _bfg_attr_view
+
+ elif template:
+ def _templated_view(context, request):
+ response = view(context, request)
+ response = templated_response(template, response, view,
+ context, request, auto_reload)
+ return response
+ wrapped_view = _templated_view
+
decorate_view(wrapped_view, view)
return wrapped_view
-def requestonly(class_or_callable):
+def requestonly(class_or_callable, attr=None):
""" Return true of the class or callable accepts only a request argument,
as opposed to something that accepts context, request """
+ if attr is None:
+ attr = '__call__'
if inspect.isfunction(class_or_callable):
fn = class_or_callable
elif inspect.isclass(class_or_callable):
@@ -459,7 +545,7 @@ def requestonly(class_or_callable):
return False
else:
try:
- fn = class_or_callable.__call__
+ fn = getattr(class_or_callable, attr)
except AttributeError:
return False