summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2010-12-22 11:28:58 -0500
committerChris McDonough <chrism@plope.com>2010-12-22 11:28:58 -0500
commite8f26928bf5c8fb8490a72436718cedf8fe19281 (patch)
tree8b22d3d7368665189365e783dd059322dc0d4298
parent732fe5c5fccb9fc2ba2392cf9f345ff1e1f259d2 (diff)
downloadpyramid-e8f26928bf5c8fb8490a72436718cedf8fe19281.tar.gz
pyramid-e8f26928bf5c8fb8490a72436718cedf8fe19281.tar.bz2
pyramid-e8f26928bf5c8fb8490a72436718cedf8fe19281.zip
factor all view wrapper funcs into a ViewDeriver class
-rw-r--r--pyramid/config.py589
-rw-r--r--pyramid/tests/test_config.py20
2 files changed, 344 insertions, 265 deletions
diff --git a/pyramid/config.py b/pyramid/config.py
index 93123c119..f6746a20e 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -266,10 +266,14 @@ class Configurator(object):
renderer_globals_factory=None,
default_permission=None,
session_factory=None,
+ view_deriver=None,
autocommit=False,
):
if package is None:
package = caller_package()
+ if view_deriver is None:
+ view_deriver = DefaultViewDeriver
+ self.view_deriver = view_deriver
name_resolver = DottedNameResolver(package)
self.name_resolver = name_resolver
self.package_name = name_resolver.package_name
@@ -342,26 +346,20 @@ class Configurator(object):
attr=None, renderer=None, wrapper_viewname=None,
viewname=None, accept=None, order=MAX_ORDER,
phash=DEFAULT_PHASH):
- if renderer is None: # use default renderer if one exists
- default_renderer_factory = self.registry.queryUtility(
- IRendererFactory)
- if default_renderer_factory is not None:
- renderer = {'name':None, 'package':self.package}
view = self.maybe_dotted(view)
- authn_policy = self.registry.queryUtility(IAuthenticationPolicy)
- authz_policy = self.registry.queryUtility(IAuthorizationPolicy)
- settings = self.registry.settings
- logger = self.registry.queryUtility(IDebugLogger)
- mapped_view = _map_view(view, self.registry, attr, renderer)
- owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname)
- secured_view = _secure_view(owrapped_view, permission,
- authn_policy, authz_policy)
- debug_view = _authdebug_view(secured_view, permission,
- authn_policy, authz_policy, settings,
- logger)
- predicated_view = _predicate_wrap(debug_view, predicates)
- derived_view = _attr_wrap(predicated_view, accept, order, phash)
- return derived_view
+ deriver = self.view_deriver(view,
+ registry=self.registry,
+ permission=permission,
+ predicates=predicates,
+ attr=attr,
+ renderer=renderer,
+ wrapper_viewname=wrapper_viewname,
+ viewname=viewname,
+ accept=accept,
+ order=order,
+ phash=phash,
+ package=self.package)
+ return deriver()
def _override(self, package, path, override_package, override_prefix,
PackageOverrides=PackageOverrides):
@@ -2619,149 +2617,312 @@ class MultiView(object):
continue
raise PredicateMismatch(self.name)
-def decorate_view(wrapped_view, original_view):
- if wrapped_view is original_view:
- return False
- wrapped_view.__module__ = original_view.__module__
- wrapped_view.__doc__ = original_view.__doc__
- try:
- wrapped_view.__name__ = original_view.__name__
- except AttributeError:
- wrapped_view.__name__ = repr(original_view)
- try:
- wrapped_view.__permitted__ = original_view.__permitted__
- except AttributeError:
- pass
- try:
- wrapped_view.__call_permissive__ = original_view.__call_permissive__
- except AttributeError:
- pass
- try:
- wrapped_view.__predicated__ = original_view.__predicated__
- except AttributeError:
- pass
- try:
- wrapped_view.__accept__ = original_view.__accept__
- except AttributeError:
- pass
- try:
- wrapped_view.__order__ = original_view.__order__
- except AttributeError:
- pass
- return True
-
-def requestonly(class_or_callable, attr=None):
+def requestonly(view, 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):
+ return DefaultViewDeriver(view, attr=attr).requestonly()
+
+def preserve_attrs(wrapped):
+ def inner(self, view):
+ wrapped_view = wrapped(self, view)
+ if wrapped_view is view:
+ return view
+ wrapped_view.__module__ = view.__module__
+ wrapped_view.__doc__ = view.__doc__
try:
- fn = class_or_callable.__init__
+ wrapped_view.__name__ = view.__name__
except AttributeError:
- return False
- else:
+ wrapped_view.__name__ = repr(view)
try:
- fn = getattr(class_or_callable, attr)
+ wrapped_view.__permitted__ = view.__permitted__
except AttributeError:
- return False
+ pass
+ try:
+ wrapped_view.__call_permissive__ = view.__call_permissive__
+ except AttributeError:
+ pass
+ try:
+ wrapped_view.__predicated__ = view.__predicated__
+ except AttributeError:
+ pass
+ try:
+ wrapped_view.__accept__ = view.__accept__
+ except AttributeError:
+ pass
+ try:
+ wrapped_view.__order__ = view.__order__
+ except AttributeError:
+ pass
+ return wrapped_view
+ return inner
+
+class DefaultViewDeriver(object):
+ def __init__(self, view, **kw):
+ self.kw = kw
+ self.view = view
+ self.registry = kw.get('registry')
+ self.helper = None
+ self.renderer = kw.get('renderer')
+ if self.renderer is None and self.registry is not None:
+ # use default renderer if one exists
+ default_renderer_factory = self.registry.queryUtility(
+ IRendererFactory)
+ if default_renderer_factory is not None:
+ self.renderer = {'name':None, 'package':kw.get('package')}
+ if self.renderer is not None:
+ self.helper = RendererHelper(self.renderer['name'],
+ package=self.renderer['package'],
+ registry=self.registry)
+ if self.registry is not None:
+ self.authn_policy = self.registry.queryUtility(
+ IAuthenticationPolicy)
+ self.authz_policy = self.registry.queryUtility(
+ IAuthorizationPolicy)
+ self.logger = self.registry.queryUtility(IDebugLogger)
+
+ def __call__(self):
+ return self.attr_wrapped_view(
+ self.predicated_view(
+ self.authdebug_view(
+ self.secured_view(
+ self.owrap_view(
+ self.map_view(self.view))))))
+
+ def requestonly(self):
+ view = self.view
+ attr = self.kw.get('attr')
+ if attr is None:
+ attr = '__call__'
+ if inspect.isfunction(view):
+ fn = view
+ elif inspect.isclass(view):
+ try:
+ fn = view.__init__
+ except AttributeError:
+ return False
+ else:
+ try:
+ fn = getattr(view, attr)
+ except AttributeError:
+ return False
- try:
- argspec = inspect.getargspec(fn)
- except TypeError:
- return False
+ try:
+ argspec = inspect.getargspec(fn)
+ except TypeError:
+ return False
- args = argspec[0]
- defaults = argspec[3]
+ args = argspec[0]
+ defaults = argspec[3]
- if hasattr(fn, 'im_func'):
- # it's an instance method
+ if hasattr(fn, 'im_func'):
+ # it's an instance method
+ if not args:
+ return False
+ args = args[1:]
if not args:
return False
- args = args[1:]
- if not args:
- return False
-
- if len(args) == 1:
- return True
- elif args[0] == 'request':
- if len(args) - len(defaults) == 1:
+ if len(args) == 1:
return True
- return False
+ elif args[0] == 'request':
+ if len(args) - len(defaults) == 1:
+ return True
-def is_response(ob):
- if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and
- hasattr(ob, 'status') ):
- return True
- return False
+ return False
+
-def _map_view(view, registry, attr=None, renderer=None):
- wrapped_view = view
-
- helper = None
-
- if renderer is not None:
- helper = RendererHelper(renderer['name'],
- package=renderer['package'],
- registry=registry)
-
- if inspect.isclass(view):
- # If the object we've located is a class, turn it into a
- # function that operates like a Zope view (when it's invoked,
- # construct an instance using 'context' and 'request' as
- # position arguments, then immediately invoke the __call__
- # method of the instance with no arguments; __call__ should
- # return an IResponse).
- if requestonly(view, attr):
- # its __init__ accepts only a single request argument,
- # instead of both context and request
- def _class_requestonly_view(context, request):
- inst = view(request)
- if attr is None:
- response = inst()
- else:
- response = getattr(inst, attr)()
- if helper is not None:
- if not is_response(response):
- system = {
- 'view':inst,
- 'renderer_name':renderer['name'], # b/c
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _class_requestonly_view
- else:
- # its __init__ accepts both context and request
- def _class_view(context, request):
- inst = view(context, request)
- if attr is None:
- response = inst()
+ @preserve_attrs
+ def owrap_view(self, view):
+ wrapper_viewname = self.kw.get('wrapper_viewname')
+ viewname = self.kw.get('viewname')
+ if not wrapper_viewname:
+ return view
+ def _owrapped_view(context, request):
+ response = view(context, request)
+ request.wrapped_response = response
+ request.wrapped_body = response.body
+ request.wrapped_view = view
+ wrapped_response = render_view_to_response(context, request,
+ wrapper_viewname)
+ if wrapped_response is None:
+ raise ValueError(
+ 'No wrapper view named %r found when executing view '
+ 'named %r' % (wrapper_viewname, viewname))
+ return wrapped_response
+ return _owrapped_view
+
+ @preserve_attrs
+ def secured_view(self, view):
+ permission = self.kw.get('permission')
+ if permission == '__no_permission_required__':
+ # allow views registered within configurations that have a
+ # default permission to explicitly override the default
+ # permission, replacing it with no permission at all
+ permission = None
+
+ wrapped_view = view
+ if self.authn_policy and self.authz_policy and (permission is not None):
+ def _secured_view(context, request):
+ principals = self.authn_policy.effective_principals(request)
+ if self.authz_policy.permits(context, principals, permission):
+ return view(context, request)
+ msg = getattr(request, 'authdebug_message',
+ 'Unauthorized: %s failed permission check' % view)
+ raise Forbidden(msg)
+ _secured_view.__call_permissive__ = view
+ def _permitted(context, request):
+ principals = self.authn_policy.effective_principals(request)
+ return self.authz_policy.permits(context, principals,
+ permission)
+ _secured_view.__permitted__ = _permitted
+ wrapped_view = _secured_view
+
+ return wrapped_view
+
+ @preserve_attrs
+ def authdebug_view(self, view):
+ wrapped_view = view
+ settings = self.registry.settings
+ permission = self.kw.get('permission')
+ if settings and settings.get('debug_authorization', False):
+ def _authdebug_view(context, request):
+ view_name = getattr(request, 'view_name', None)
+
+ if self.authn_policy and self.authz_policy:
+ if permission is None:
+ msg = 'Allowed (no permission registered)'
+ else:
+ principals = self.authn_policy.effective_principals(
+ request)
+ msg = str(self.authz_policy.permits(context, principals,
+ permission))
else:
- response = getattr(inst, attr)()
- if helper is not None:
- if not is_response(response):
- system = {'view':inst,
- 'renderer_name':renderer['name'], # b/c
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _class_view
-
- elif requestonly(view, attr):
- # its __call__ accepts only a single request argument,
- # instead of both context and request
+ msg = 'Allowed (no authorization policy in use)'
+
+ view_name = getattr(request, 'view_name', None)
+ url = getattr(request, 'url', None)
+ msg = ('debug_authorization of url %s (view name %r against '
+ 'context %r): %s' % (url, view_name, context, msg))
+ self.logger and self.logger.debug(msg)
+ if request is not None:
+ request.authdebug_message = msg
+ return view(context, request)
+
+ wrapped_view = _authdebug_view
+
+ return wrapped_view
+
+ @preserve_attrs
+ def predicated_view(self, view):
+ predicates = self.kw.get('predicates', ())
+ if not predicates:
+ return view
+ def predicate_wrapper(context, request):
+ if all((predicate(context, request) for predicate in predicates)):
+ return view(context, request)
+ raise PredicateMismatch('predicate mismatch for view %s' % view)
+ def checker(context, request):
+ return all((predicate(context, request) for predicate in
+ predicates))
+ predicate_wrapper.__predicated__ = checker
+ return predicate_wrapper
+
+ @preserve_attrs
+ def attr_wrapped_view(self, view):
+ kw = self.kw
+ accept, order, phash = (kw.get('accept', None),
+ kw.get('order', MAX_ORDER),
+ kw.get('phash', DEFAULT_PHASH))
+ # this is a little silly but we don't want to decorate the original
+ # function with attributes that indicate accept, order, and phash,
+ # so we use a wrapper
+ if ( (accept is None) and (order == MAX_ORDER) and
+ (phash == DEFAULT_PHASH) ):
+ return view # defaults
+ def attr_view(context, request):
+ return view(context, request)
+ attr_view.__accept__ = accept
+ attr_view.__order__ = order
+ attr_view.__phash__ = phash
+ return attr_view
+
+ @preserve_attrs
+ def map_view(self, view):
+ attr = self.kw.get('attr')
+ isclass = inspect.isclass(view)
+ ronly = self.requestonly()
+ if isclass and ronly:
+ view = self.adapt_requestonly_class()
+ elif isclass:
+ view = self.adapt_class()
+ elif ronly:
+ view = self.adapt_requestonly_func()
+ elif attr:
+ view = self.adapt_attr()
+ elif self.helper is not None:
+ view = self.adapt_rendered()
+ return view
+
+ def adapt_requestonly_class(self):
+ # its a class that has an __init__ which only accepts request
+ view = self.view
+ attr = self.kw.get('attr')
+ helper = self.helper
+ renderer = self.renderer
+ def _class_requestonly_view(context, request):
+ inst = view(request)
+ if attr is None:
+ response = inst()
+ else:
+ response = getattr(inst, attr)()
+ if helper is not None:
+ if not is_response(response):
+ system = {
+ 'view':inst,
+ 'renderer_name':renderer['name'], # b/c
+ 'renderer_info':renderer,
+ 'context':context,
+ 'request':request
+ }
+ response = helper.render_to_response(response, system,
+ request=request)
+ return response
+ return _class_requestonly_view
+
+ def adapt_class(self):
+ # its a class that has an __init__ which accepts both context and
+ # request
+ view = self.view
+ attr = self.kw.get('attr')
+ helper = self.helper
+ renderer = self.renderer
+ def _class_view(context, request):
+ inst = view(context, request)
+ if attr is None:
+ response = inst()
+ else:
+ response = getattr(inst, attr)()
+ if helper is not None:
+ if not is_response(response):
+ system = {'view':inst,
+ 'renderer_name':renderer['name'], # b/c
+ 'renderer_info':renderer,
+ 'context':context,
+ 'request':request
+ }
+ response = helper.render_to_response(response, system,
+ request=request)
+ return response
+ return _class_view
+
+ def adapt_requestonly_func(self):
+ # its a function or instance that has a __call__ accepts only a
+ # single request argument
+ view = self.view
+ attr = self.kw.get('attr')
+ helper = self.helper
+ renderer = self.renderer
def _requestonly_view(context, request):
if attr is None:
response = view(request)
@@ -2780,9 +2941,15 @@ def _map_view(view, registry, attr=None, renderer=None):
response = helper.render_to_response(response, system,
request=request)
return response
- wrapped_view = _requestonly_view
-
- elif attr:
+ return _requestonly_view
+
+ def adapt_attr(self):
+ # its a function that has a __call__ which accepts both context and
+ # request, but still has an attr
+ view = self.view
+ attr = self.kw.get('attr')
+ helper = self.helper
+ renderer = self.renderer
def _attr_view(context, request):
response = getattr(view, attr)(context, request)
if helper is not None:
@@ -2797,9 +2964,14 @@ def _map_view(view, registry, attr=None, renderer=None):
response = helper.render_to_response(response, system,
request=request)
return response
- wrapped_view = _attr_view
-
- elif helper is not None:
+ return _attr_view
+
+ def adapt_rendered(self):
+ # it's a function that has a __call__ that accepts both context and
+ # request, but requires rendering
+ view = self.view
+ helper = self.helper
+ renderer = self.renderer
def _rendered_view(context, request):
response = view(context, request)
if not is_response(response):
@@ -2813,113 +2985,11 @@ def _map_view(view, registry, attr=None, renderer=None):
response = helper.render_to_response(response, system,
request=request)
return response
- wrapped_view = _rendered_view
-
- decorate_view(wrapped_view, view)
- return wrapped_view
-
-def _owrap_view(view, viewname, wrapper_viewname):
- if not wrapper_viewname:
- return view
- def _owrapped_view(context, request):
- response = view(context, request)
- request.wrapped_response = response
- request.wrapped_body = response.body
- request.wrapped_view = view
- wrapped_response = render_view_to_response(context, request,
- wrapper_viewname)
- if wrapped_response is None:
- raise ValueError(
- 'No wrapper view named %r found when executing view '
- 'named %r' % (wrapper_viewname, viewname))
- return wrapped_response
- decorate_view(_owrapped_view, view)
- return _owrapped_view
-
-def _predicate_wrap(view, predicates):
- if not predicates:
- return view
- def predicate_wrapper(context, request):
- if all((predicate(context, request) for predicate in predicates)):
- return view(context, request)
- raise PredicateMismatch('predicate mismatch for view %s' % view)
- def checker(context, request):
- return all((predicate(context, request) for predicate in
- predicates))
- predicate_wrapper.__predicated__ = checker
- decorate_view(predicate_wrapper, view)
- return predicate_wrapper
-
-def _secure_view(view, permission, authn_policy, authz_policy):
- if permission == '__no_permission_required__':
- # allow views registered within configurations that have a
- # default permission to explicitly override the default
- # permission, replacing it with no permission at all
- permission = None
-
- wrapped_view = view
- if authn_policy and authz_policy and (permission is not None):
- def _secured_view(context, request):
- principals = authn_policy.effective_principals(request)
- if authz_policy.permits(context, principals, permission):
- return view(context, request)
- msg = getattr(request, 'authdebug_message',
- 'Unauthorized: %s failed permission check' % view)
- raise Forbidden(msg)
- _secured_view.__call_permissive__ = view
- def _permitted(context, request):
- principals = authn_policy.effective_principals(request)
- return authz_policy.permits(context, principals, permission)
- _secured_view.__permitted__ = _permitted
- wrapped_view = _secured_view
- decorate_view(wrapped_view, view)
-
- return wrapped_view
-
-def _authdebug_view(view, permission, authn_policy, authz_policy, settings,
- logger):
- wrapped_view = view
- if settings and settings.get('debug_authorization', False):
- def _authdebug_view(context, request):
- view_name = getattr(request, 'view_name', None)
-
- if authn_policy and authz_policy:
- if permission is None:
- msg = 'Allowed (no permission registered)'
- else:
- principals = authn_policy.effective_principals(request)
- msg = str(authz_policy.permits(context, principals,
- permission))
- else:
- msg = 'Allowed (no authorization policy in use)'
-
- view_name = getattr(request, 'view_name', None)
- url = getattr(request, 'url', None)
- msg = ('debug_authorization of url %s (view name %r against '
- 'context %r): %s' % (url, view_name, context, msg))
- logger and logger.debug(msg)
- if request is not None:
- request.authdebug_message = msg
- return view(context, request)
-
- wrapped_view = _authdebug_view
- decorate_view(wrapped_view, view)
+ return _rendered_view
- return wrapped_view
-
-def _attr_wrap(view, accept, order, phash):
- # this is a little silly but we don't want to decorate the original
- # function with attributes that indicate accept, order, and phash,
- # so we use a wrapper
- if (accept is None) and (order == MAX_ORDER) and (phash == DEFAULT_PHASH):
- return view # defaults
- def attr_view(context, request):
- return view(context, request)
- attr_view.__accept__ = accept
- attr_view.__order__ = order
- attr_view.__phash__ = phash
- decorate_view(attr_view, view)
- return attr_view
+def _map_view(view, registry, attr=None, renderer=None):
+ return DefaultViewDeriver(view, registry=registry, attr=attr,
+ renderer=renderer).map_view(view)
def isexception(o):
if IInterface.providedBy(o):
@@ -2970,3 +3040,8 @@ class PyramidConfigurationMachine(ConfigurationMachine):
self._seen_files.add(spec)
return True
+def is_response(ob):
+ if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and
+ hasattr(ob, 'status') ):
+ return True
+ return False
diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py
index e4ab9a867..812bfffa3 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/tests/test_config.py
@@ -3788,16 +3788,18 @@ class Test__map_view(unittest.TestCase):
request = self._makeRequest()
self.assertEqual(result(None, request).body, 'Hello!')
-class Test_decorate_view(unittest.TestCase):
- def _callFUT(self, wrapped, original):
- from pyramid.config import decorate_view
- return decorate_view(wrapped, original)
+class Test_preserve_attrs(unittest.TestCase):
+ def _callFUT(self, fn, view):
+ from pyramid.config import preserve_attrs
+ return preserve_attrs(fn)(None, view)
def test_it_same(self):
def view(context, request):
""" """
- result = self._callFUT(view, view)
- self.assertEqual(result, False)
+ def afunc(self, view):
+ return view
+ result = self._callFUT(afunc, view)
+ self.failUnless(result is view)
def test_it_different(self):
class DummyView1:
@@ -3826,8 +3828,10 @@ class Test_decorate_view(unittest.TestCase):
""" """
view1 = DummyView1()
view2 = DummyView2()
- result = self._callFUT(view1, view2)
- self.assertEqual(result, True)
+ def afunc(self, view):
+ return view1
+ result = self._callFUT(afunc, view2)
+ self.assertEqual(result, view1)
self.failUnless(view1.__doc__ is view2.__doc__)
self.failUnless(view1.__module__ is view2.__module__)
self.failUnless(view1.__name__ is view2.__name__)