summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2010-12-29 14:47:32 -0500
committerChris McDonough <chrism@plope.com>2010-12-29 15:52:20 -0500
commit8739f576ed84bb48cec9c2d4b60e92878a273b1f (patch)
tree784b553f48ca2e05cf877fa8100955474c6a7a9c
parent7f14c3750dcf2b79e9146b0a3750af307fd1a86b (diff)
downloadpyramid-8739f576ed84bb48cec9c2d4b60e92878a273b1f.tar.gz
pyramid-8739f576ed84bb48cec9c2d4b60e92878a273b1f.tar.bz2
pyramid-8739f576ed84bb48cec9c2d4b60e92878a273b1f.zip
factor deriver from mapper
-rw-r--r--docs/api/interfaces.rst4
-rw-r--r--pyramid/config.py257
-rw-r--r--pyramid/interfaces.py21
3 files changed, 165 insertions, 117 deletions
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index b3c14e5f7..3ce926230 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -35,3 +35,7 @@ Other Interfaces
.. autointerface:: ITemplateRenderer
+ .. autointerface:: IViewMapperFactory
+
+ .. autointerface:: IViewMapper
+
diff --git a/pyramid/config.py b/pyramid/config.py
index d31011aa1..d3c197008 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -45,6 +45,7 @@ from pyramid.interfaces import ITranslationDirectories
from pyramid.interfaces import ITraverser
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
+from pyramid.interfaces import IViewMapperFactory
try:
from pyramid import chameleon_text
@@ -266,14 +267,10 @@ 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,24 +339,25 @@ class Configurator(object):
def _split_spec(self, path_or_spec):
return resolve_asset_spec(path_or_spec, self.package_name)
+ # b/w compat
def _derive_view(self, view, permission=None, predicates=(),
attr=None, renderer=None, wrapper_viewname=None,
viewname=None, accept=None, order=MAX_ORDER,
phash=DEFAULT_PHASH):
view = self.maybe_dotted(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()
+ deriver = ViewDeriver(
+ 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(view)
def _override(self, package, path, override_package, override_prefix,
PackageOverrides=PackageOverrides):
@@ -1008,7 +1006,7 @@ class Configurator(object):
request_param=None, containment=None, attr=None,
renderer=None, wrapper=None, xhr=False, accept=None,
header=None, path_info=None, custom_predicates=(),
- context=None):
+ context=None, view_mapper=None):
""" Add a :term:`view configuration` to the current
configuration state. Arguments to ``add_view`` are broken
down below into *predicate* arguments and *non-predicate*
@@ -1253,6 +1251,18 @@ class Configurator(object):
the context and/or the request. If all callables return
``True``, the associated view callable will be considered
viable for a given request.
+
+ view_mapper
+
+ A class implementing the
+ :class:`pyramid.interfaces.IViewMapperFactory` interface, which
+ performs view argument and response mapping. By default it is
+ ``None``, which indicates that the view should use the default view
+ mapper. This plug-point is useful for Pyramid extension
+ developers, but it's not very useful for'
+ 'civilians' who are just developing stock Pyramid applications.
+ Pay no attention to the man behind the curtain.
+
"""
view = self.maybe_dotted(view)
context = self.maybe_dotted(context)
@@ -1291,6 +1301,7 @@ class Configurator(object):
renderer=renderer, wrapper=wrapper, xhr=xhr, accept=accept,
header=header, path_info=path_info,
custom_predicates=custom_predicates, context=context,
+ view_mapper = view_mapper,
)
view_info = deferred_views.setdefault(route_name, [])
view_info.append(info)
@@ -1321,10 +1332,18 @@ class Configurator(object):
permission = self.registry.queryUtility(IDefaultPermission)
# NO_PERMISSION_REQUIRED handled by _secure_view
- derived_view = self._derive_view(view, permission, predicates, attr,
- renderer, wrapper, name, accept,
- order, phash)
-
+ derived_view = ViewDeriver(registry=self.registry,
+ permission=permission,
+ predicates=predicates,
+ attr=attr,
+ renderer=renderer,
+ wrapper_viewname=wrapper,
+ viewname=name,
+ accept=accept,
+ order=order,
+ phash=phash,
+ view_mapper=view_mapper)(view)
+
registered = self.registry.adapters.registered
# A multiviews is a set of views which are registered for
@@ -2631,11 +2650,6 @@ class MultiView(object):
continue
raise PredicateMismatch(self.name)
-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 """
- return DefaultViewDeriver(view, attr=attr).requestonly()
-
def preserve_attrs(wrapped):
def inner(self, view):
wrapped_view = wrapped(self, view)
@@ -2670,81 +2684,27 @@ def preserve_attrs(wrapped):
return wrapped_view
return inner
-class DefaultViewDeriver(object):
- def __init__(self, view, **kw):
+class ViewDeriver(object):
+ def __init__(self, **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):
+ self.registry = kw['registry']
+ self.authn_policy = self.registry.queryUtility(
+ IAuthenticationPolicy)
+ self.authz_policy = self.registry.queryUtility(
+ IAuthorizationPolicy)
+ self.logger = self.registry.queryUtility(IDebugLogger)
+
+ def __call__(self, view):
+ mapper = self.kw.get('view_mapper')
+ if mapper is None:
+ mapper = DefaultViewMapper
+ view = mapper(**self.kw)(view)
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
-
- args = argspec[0]
- defaults = argspec[3]
-
- if hasattr(fn, 'im_func'):
- # it's an instance method
- 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:
- return True
-
- return False
-
+ view)))))
@preserve_attrs
def owrap_view(self, view):
@@ -2861,26 +2821,85 @@ class DefaultViewDeriver(object):
attr_view.__phash__ = phash
return attr_view
+class DefaultViewMapper(object):
+ implements(IViewMapperFactory)
+ def __init__(self, **kw):
+ self.kw = kw
+ self.helper = None
+ self.renderer = kw.get('renderer')
+ self.registry = kw.get('registry')
+ 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)
+
+ def requestonly(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
+
+ args = argspec[0]
+ defaults = argspec[3]
+
+ if hasattr(fn, 'im_func'):
+ # it's an instance method
+ 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:
+ return True
+
+ return False
+
@preserve_attrs
- def map_view(self, view):
+ def __call__(self, view):
attr = self.kw.get('attr')
isclass = inspect.isclass(view)
- ronly = self.requestonly()
+ ronly = self.requestonly(view)
if isclass and ronly:
- view = self.adapt_requestonly_class()
+ view = self.map_requestonly_class(view)
elif isclass:
- view = self.adapt_class()
+ view = self.map_class(view)
elif ronly:
- view = self.adapt_requestonly_func()
+ view = self.map_requestonly_func(view)
elif attr:
- view = self.adapt_attr()
+ view = self.map_attr(view)
elif self.helper is not None:
- view = self.adapt_rendered()
+ view = self.map_rendered(view)
return view
- def adapt_requestonly_class(self):
+ def map_requestonly_class(self, view):
# 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
@@ -2904,10 +2923,9 @@ class DefaultViewDeriver(object):
return response
return _class_requestonly_view
- def adapt_class(self):
+ def map_class(self, view):
# 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
@@ -2930,10 +2948,9 @@ class DefaultViewDeriver(object):
return response
return _class_view
- def adapt_requestonly_func(self):
+ def map_requestonly_func(self, view):
# 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
@@ -2957,10 +2974,9 @@ class DefaultViewDeriver(object):
return response
return _requestonly_view
- def adapt_attr(self):
+ def map_attr(self, view):
# 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
@@ -2980,10 +2996,9 @@ class DefaultViewDeriver(object):
return response
return _attr_view
- def adapt_rendered(self):
+ def map_rendered(self, view):
# 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):
@@ -3001,10 +3016,6 @@ class DefaultViewDeriver(object):
return response
return _rendered_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):
if IException.isEqualOrExtendedBy(o):
@@ -3059,3 +3070,15 @@ def is_response(ob):
hasattr(ob, 'status') ):
return True
return False
+
+# b/c
+def _map_view(view, registry, attr=None, renderer=None):
+ return DefaultViewMapper(registry=registry, attr=attr,
+ renderer=renderer)(view)
+
+# b/c
+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 """
+ return DefaultViewMapper(attr=attr).requestonly(view)
+
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index 32359ca94..10a324b28 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -120,6 +120,27 @@ class ITemplateRenderer(IRenderer):
accepts arbitrary keyword arguments and returns a string or
unicode object """
+class IViewMapper(Interface):
+ def __call__(self, object):
+ """ Provided with an arbitrary object (a function, class, or
+ instance), returns a callable with the call signature ``(context,
+ request)``. The callable returned should itself return a Response
+ object. An IViewMapper is returned by
+ :class:`pyramid.interfaces.IViewMapperFactory`."""
+
+class IViewMapperFactory(Interface):
+ def __call__(self, **kw):
+ """
+ Return an object which implements
+ :class:`pyramid.interfaces.IViewMapper`. ``kw`` will be a dictionary
+ containing view-specific arguments, such as ``permission``,
+ ``predicates``, ``attr``, ``renderer``, and other items. An
+ IViewMapperFactory is used by
+ :meth:`pyramid.config.Configurator.add_view` to provide a plugpoint
+ to extension developers who want to modify potential view callable
+ invocation signatures and response values.
+ """
+
# internal interfaces
class IRequest(Interface):