summaryrefslogtreecommitdiff
path: root/repoze/bfg/configuration.py
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-11-17 21:59:54 +0000
committerChris McDonough <chrism@agendaless.com>2009-11-17 21:59:54 +0000
commitcbfafba1514ce2ce2b87aadb0093c06210219372 (patch)
tree41e83cf95cf4cf8920396e644a219518689e8b89 /repoze/bfg/configuration.py
parenta937e7d039f2c9e62e1a2771b2e6b23412ab709a (diff)
downloadpyramid-cbfafba1514ce2ce2b87aadb0093c06210219372.tar.gz
pyramid-cbfafba1514ce2ce2b87aadb0093c06210219372.tar.bz2
pyramid-cbfafba1514ce2ce2b87aadb0093c06210219372.zip
Move configuration methods into Configurator.
Diffstat (limited to 'repoze/bfg/configuration.py')
-rw-r--r--repoze/bfg/configuration.py787
1 files changed, 687 insertions, 100 deletions
diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py
index 2b74017dc..495445ff6 100644
--- a/repoze/bfg/configuration.py
+++ b/repoze/bfg/configuration.py
@@ -1,130 +1,67 @@
import os
+import re
import sys
import threading
+import inspect
import zope.component
from zope.configuration import xmlconfig
+from zope.configuration.exceptions import ConfigurationError
from zope.configuration.config import ConfigurationMachine
from zope.component import getGlobalSiteManager
from zope.component import getSiteManager
+from zope.interface import Interface
+from zope.interface import implementedBy
+from zope.interface.interfaces import IInterface
+
from repoze.bfg.interfaces import IAuthenticationPolicy
from repoze.bfg.interfaces import IAuthorizationPolicy
from repoze.bfg.interfaces import IDefaultRootFactory
+from repoze.bfg.interfaces import IForbiddenView
from repoze.bfg.interfaces import ILogger
+from repoze.bfg.interfaces import IMultiView
+from repoze.bfg.interfaces import INotFoundView
+from repoze.bfg.interfaces import IPackageOverrides
+from repoze.bfg.interfaces import IRendererFactory
+from repoze.bfg.interfaces import IRequest
from repoze.bfg.interfaces import IRootFactory
+from repoze.bfg.interfaces import IRouteRequest
from repoze.bfg.interfaces import IRoutesMapper
from repoze.bfg.interfaces import ISettings
+from repoze.bfg.interfaces import ISecuredView
+from repoze.bfg.interfaces import ITemplateRendererFactory
+from repoze.bfg.interfaces import IView
+from repoze.bfg.interfaces import IViewPermission
-from repoze.bfg.authorization import ACLAuthorizationPolicy
+from repoze.bfg import chameleon_zpt
+from repoze.bfg import chameleon_text
+from repoze.bfg import renderers
+from repoze.bfg.compat import all
+from repoze.bfg.exceptions import Forbidden
+from repoze.bfg.exceptions import NotFound
from repoze.bfg.log import make_stream_logger
-from repoze.bfg.registry import Registry
-from repoze.bfg.registry import DefaultRootFactory
+from repoze.bfg.request import route_request_iface
+from repoze.bfg.resource import PackageOverrides
from repoze.bfg.settings import Settings
from repoze.bfg.settings import get_options
+from repoze.bfg.settings import get_settings
+from repoze.bfg.static import StaticRootFactory
from repoze.bfg.threadlocal import get_current_registry
from repoze.bfg.threadlocal import manager
-from repoze.bfg.urldispatch import RoutesRootFactory
-
-def make_registry(root_factory, package=None, filename='configure.zcml',
- authentication_policy=None, authorization_policy=None,
- options=None, registry=None, debug_logger=None,
- manager=manager, os=os, lock=threading.Lock()):
- # registry, debug_logger, manager, os and lock *only* for unittests
- if options is None:
- options = {}
-
- if not 'configure_zcml' in options:
- options['configure_zcml'] = filename
+from repoze.bfg.traversal import find_interface
+from repoze.bfg.urldispatch import RoutesMapper
+from repoze.bfg.view import MultiView
+from repoze.bfg.view import decorate_view
+from repoze.bfg.view import rendered_response
+from repoze.bfg.view import render_view_to_response
+from repoze.bfg.view import requestonly
+from repoze.bfg.view import static as static_view
- settings = Settings(get_options(options))
- filename = settings['configure_zcml']
-
- # not os.path.isabs below for windows systems
- if (':' in filename) and (not os.path.isabs(filename)):
- package, filename = filename.split(':', 1)
- __import__(package)
- package = sys.modules[package]
+import martian
- if registry is None:
- regname = filename
- if package:
- regname = package.__name__
- registry = Registry(regname)
-
- registry.registerUtility(settings, ISettings)
-
- if debug_logger is None:
- debug_logger = make_stream_logger('repoze.bfg.debug', sys.stderr)
- registry.registerUtility(debug_logger, ILogger, 'repoze.bfg.debug')
-
- if root_factory is None:
- root_factory = DefaultRootFactory
-
- mapper = RoutesRootFactory(root_factory)
- registry.registerUtility(mapper, IRoutesMapper)
- # register the *default* root factory so apps can find it later
- registry.registerUtility(root_factory, IDefaultRootFactory)
-
- if authentication_policy:
- debug_logger.warn(
- 'The "authentication_policy" and "authorization_policy" '
- 'arguments to repoze.bfg.router.make_app have been deprecated '
- 'in repoze.bfg version 1.0. Instead of using these arguments to '
- 'configure an authorization/authentication policy pair, use '
- 'a pair of ZCML directives (such as "authtktauthenticationpolicy" '
- 'and "aclauthorizationpolicy" documented within the Security '
- 'chapter in the BFG documentation. If you need to use a custom '
- 'authentication or authorization policy, you should make a ZCML '
- 'directive for it and use that directive within your '
- 'application\'s ZCML')
- registry.registerUtility(authentication_policy, IAuthenticationPolicy)
- if authorization_policy is None:
- authorization_policy = ACLAuthorizationPolicy()
- registry.registerUtility(authorization_policy, IAuthorizationPolicy)
-
- # We push our ZCML-defined configuration into an app-local
- # component registry in order to allow more than one bfg app to live
- # in the same process space without one unnecessarily stomping on
- # the other's component registrations (although I suspect directives
- # that have side effects are going to fail). The only way to do
- # that currently is to override zope.component.getGlobalSiteManager
- # for the duration of the ZCML includes. We acquire a lock in case
- # another make_app runs in a different thread simultaneously, in a
- # vain attempt to prevent mixing of registrations. There's not much
- # we can do about non-makeRegistry code that tries to use the global
- # site manager API directly in a different thread while we hold the
- # lock. Those registrations will end up in our application's
- # registry.
-
- lock.acquire()
- manager.push({'registry':registry, 'request':None})
- try:
- getSiteManager.sethook(get_current_registry)
- zope.component.getGlobalSiteManager = get_current_registry
- zcml_configure(filename, package)
- finally:
- # intentional: do not call getSiteManager.reset(); executing
- # this function means we're taking over getSiteManager for the
- # lifetime of this process
- zope.component.getGlobalSiteManager = getGlobalSiteManager
- lock.release()
- manager.pop()
-
- mapper = registry.getUtility(IRoutesMapper)
-
- if mapper.has_routes():
- # if the user had any <route/> statements in his configuration,
- # use the RoutesRootFactory as the IRootFactory; otherwise use the
- # default root factory (optimization; we don't want to go through
- # the Routes logic if we know there are no routes to match)
- root_factory = mapper
-
- registry.registerUtility(root_factory, IRootFactory)
-
- return registry
def zcml_configure(name, package):
""" Given a ZCML filename as ``name`` and a Python package as
@@ -140,3 +77,653 @@ def zcml_configure(name, package):
context.execute_actions(clear=False)
return context.actions
+class Configurator(object):
+ """ A wrapper around the registry that performs configuration tasks """
+ def __init__(self, registry):
+ self.reg = registry
+
+ def default_configuration(self, root_factory, package=None,
+ filename='configure.zcml', settings=None,
+ debug_logger=None, manager=manager, os=os,
+ lock=threading.Lock()):
+
+ # registry, debug_logger, manager, os and lock *only* for unittests
+ if settings is None:
+ settings = {}
+
+ if not 'configure_zcml' in settings:
+ settings['configure_zcml'] = filename
+
+ settings = Settings(get_options(settings))
+ filename = settings['configure_zcml']
+
+ # not os.path.isabs below for windows systems
+ if (':' in filename) and (not os.path.isabs(filename)):
+ package, filename = filename.split(':', 1)
+ __import__(package)
+ package = sys.modules[package]
+
+ self.settings(settings)
+ self.debug_logger(debug_logger)
+ self.root_factory(root_factory or DefaultRootFactory)
+ self.renderer(chameleon_zpt.renderer_factory, '.pt')
+ self.renderer(chameleon_text.renderer_factory, '.txt')
+ self.renderer(renderers.json_renderer_factory, 'json')
+ self.renderer(renderers.string_renderer_factory, 'string')
+
+ # We push our ZCML-defined configuration into an app-local
+ # component registry in order to allow more than one bfg app to live
+ # in the same process space without one unnecessarily stomping on
+ # the other's component registrations (although I suspect directives
+ # that have side effects are going to fail). The only way to do
+ # that currently is to override zope.component.getGlobalSiteManager
+ # for the duration of the ZCML includes. We acquire a lock in case
+ # another make_app runs in a different thread simultaneously, in a
+ # vain attempt to prevent mixing of registrations. There's not much
+ # we can do about non-makeRegistry code that tries to use the global
+ # site manager API directly in a different thread while we hold the
+ # lock. Those registrations will end up in our application's
+ # registry.
+
+ lock.acquire()
+ manager.push({'registry':self.reg, 'request':None})
+ try:
+ getSiteManager.sethook(get_current_registry)
+ zope.component.getGlobalSiteManager = get_current_registry
+ zcml_configure(filename, package)
+ finally:
+ # intentional: do not call getSiteManager.reset(); executing
+ # this function means we're taking over getSiteManager for the
+ # lifetime of this process
+ zope.component.getGlobalSiteManager = getGlobalSiteManager
+ lock.release()
+ manager.pop()
+
+ def view(self, permission=None, for_=None, view=None, name="",
+ request_type=None, route_name=None, request_method=None,
+ request_param=None, containment=None, attr=None,
+ renderer=None, wrapper=None, xhr=False, accept=None,
+ header=None, path_info=None, _info=u''):
+
+ if not view:
+ if renderer:
+ def view(context, request):
+ return {}
+ else:
+ raise ConfigurationError('"view" was not specified and '
+ 'no "renderer" specified')
+
+ if request_type in ('GET', 'HEAD', 'PUT', 'POST', 'DELETE'):
+ # b/w compat for 1.0
+ request_method = request_type
+ request_type = None
+
+ if request_type is None:
+ if route_name is None:
+ request_type = IRequest
+ else:
+ request_type = self.reg.queryUtility(IRouteRequest,
+ name=route_name)
+ if request_type is None:
+ request_type = route_request_iface(route_name)
+ self.reg.registerUtility(request_type, IRouteRequest,
+ name=route_name)
+
+ score, predicates = _make_predicates(
+ xhr=xhr, request_method=request_method, path_info=path_info,
+ request_param=request_param, header=header, accept=accept,
+ containment=containment)
+
+ derived_view = self.derive_view(view, permission, predicates, attr,
+ renderer, wrapper, name)
+ r_for_ = for_
+ r_request_type = request_type
+ if r_for_ is None:
+ r_for_ = Interface
+ if not IInterface.providedBy(r_for_):
+ r_for_ = implementedBy(r_for_)
+ if not IInterface.providedBy(r_request_type):
+ r_request_type = implementedBy(r_request_type)
+ old_view = self.reg.adapters.lookup((r_for_, r_request_type),
+ IView,name=name)
+ if old_view is None:
+ if hasattr(derived_view, '__call_permissive__'):
+ self.reg.registerAdapter(derived_view, (for_, request_type),
+ ISecuredView, name, info=_info)
+ if hasattr(derived_view, '__permitted__'):
+ # bw compat
+ self.reg.registerAdapter(
+ derived_view.__permitted__,
+ (for_, request_type), IViewPermission,
+ name, info=_info)
+ else:
+ self.reg.registerAdapter(derived_view, (for_, request_type),
+ IView, name, info=_info)
+ else:
+ # XXX we could try to be more efficient here and register
+ # a non-secured view for a multiview if none of the
+ # multiview's consituent views have a permission
+ # associated with them, but this code is getting pretty
+ # rough already
+ if IMultiView.providedBy(old_view):
+ multiview = old_view
+ else:
+ multiview = MultiView(name)
+ multiview.add(old_view, sys.maxint)
+ multiview.add(derived_view, score)
+ for i in (IView, ISecuredView):
+ # unregister any existing views
+ self.reg.adapters.unregister((r_for_, r_request_type), i,
+ name=name)
+ self.reg.registerAdapter(multiview, (for_, request_type),
+ IMultiView, name, info=_info)
+ # b/w compat
+ self.reg.registerAdapter(multiview.__permitted__,
+ (for_, request_type), IViewPermission,
+ name, info=_info)
+
+ def map_view(self, view, attr=None, renderer_name=None):
+ wrapped_view = view
+
+ renderer = None
+
+ if renderer_name is None:
+ # global default renderer
+ factory = self.reg.queryUtility(IRendererFactory)
+ if factory is not None:
+ renderer_name = ''
+ renderer = factory(renderer_name)
+ else:
+ renderer = self.renderer_from_name(renderer_name)
+
+ 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 _bfg_class_requestonly_view(context, request):
+ inst = view(request)
+ if attr is None:
+ response = inst()
+ else:
+ response = getattr(inst, attr)()
+ if renderer is not None:
+ response = rendered_response(renderer,
+ response, inst,
+ context, request,
+ renderer_name)
+ 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)
+ if attr is None:
+ response = inst()
+ else:
+ response = getattr(inst, attr)()
+ if renderer is not None:
+ response = rendered_response(renderer,
+ response, inst,
+ context, request,
+ renderer_name)
+ return response
+ wrapped_view = _bfg_class_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):
+ if attr is None:
+ response = view(request)
+ else:
+ response = getattr(view, attr)(request)
+
+ if renderer is not None:
+ response = rendered_response(renderer,
+ response, view,
+ context, request,
+ renderer_name)
+ return response
+ wrapped_view = _bfg_requestonly_view
+
+ elif attr:
+ def _bfg_attr_view(context, request):
+ response = getattr(view, attr)(context, request)
+ if renderer is not None:
+ response = rendered_response(renderer,
+ response, view,
+ context, request,
+ renderer_name)
+ return response
+ wrapped_view = _bfg_attr_view
+
+ elif renderer is not None:
+ def _rendered_view(context, request):
+ response = view(context, request)
+ response = rendered_response(renderer,
+ response, view,
+ context, request,
+ renderer_name)
+ return response
+ wrapped_view = _rendered_view
+
+ decorate_view(wrapped_view, view)
+ return wrapped_view
+
+ def renderer_from_name(self, path):
+ name = os.path.splitext(path)[1]
+ if not name:
+ name = path
+ factory = self.reg.queryUtility(IRendererFactory, name=name)
+ if factory is None:
+ raise ValueError('No renderer for renderer name %r' % name)
+ return factory(path)
+
+ def derive_view(self, original_view, permission=None, predicates=(),
+ attr=None, renderer_name=None, wrapper_viewname=None,
+ viewname=None):
+ mapped_view = self.map_view(original_view, attr, renderer_name)
+ owrapped_view = self.owrap_view(mapped_view, viewname, wrapper_viewname)
+ secured_view = self.secure_view(owrapped_view, permission)
+ debug_view = self.authdebug_view(secured_view, permission)
+ derived_view = self.predicate_wrap(debug_view, predicates)
+ return derived_view
+
+ def owrap_view(self, 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(self, view, predicates):
+ if not predicates:
+ return view
+ def _wrapped(context, request):
+ if all((predicate(context, request) for predicate in predicates)):
+ return view(context, request)
+ raise NotFound('predicate mismatch for view %s' % view)
+ def checker(context, request):
+ return all((predicate(context, request) for predicate in
+ predicates))
+ _wrapped.__predicated__ = checker
+ decorate_view(_wrapped, view)
+ return _wrapped
+
+ def secure_view(self, view, permission):
+ wrapped_view = view
+ authn_policy = self.reg.queryUtility(IAuthenticationPolicy)
+ authz_policy = self.reg.queryUtility(IAuthorizationPolicy)
+ 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(self, view, permission):
+ wrapped_view = view
+ authn_policy = self.reg.queryUtility(IAuthenticationPolicy)
+ authz_policy = self.reg.queryUtility(IAuthorizationPolicy)
+ settings = get_settings()
+ debug_authorization = False
+ if settings is not None:
+ debug_authorization = settings.get('debug_authorization', False)
+ if debug_authorization:
+ 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 = self.reg.queryUtility(ILogger, 'repoze.bfg.debug')
+ 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 wrapped_view
+
+ def route(self, name, path, view=None, view_for=None,
+ permission=None, factory=None, request_type=None, for_=None,
+ header=None, xhr=False, accept=None, path_info=None,
+ request_method=None, request_param=None,
+ view_permission=None, view_request_type=None,
+ view_request_method=None, view_request_param=None,
+ view_containment=None, view_attr=None,
+ renderer=None, view_renderer=None, view_header=None,
+ view_accept=None, view_xhr=False,
+ view_path_info=None, _info=u''):
+ # the strange ordering of the request kw args above is for b/w
+ # compatibility purposes.
+ # these are route predicates; if they do not match, the next route
+ # in the routelist will be tried
+ _, predicates = _make_predicates(xhr=xhr,
+ request_method=request_method,
+ path_info=path_info,
+ request_param=request_param,
+ header=header,
+ accept=accept)
+
+ if request_type in ('GET', 'HEAD', 'PUT', 'POST', 'DELETE'):
+ # b/w compat for 1.0
+ view_request_method = request_type
+ request_type = None
+
+ request_iface = self.reg.queryUtility(IRouteRequest, name=name)
+ if request_iface is None:
+ request_iface = route_request_iface(name)
+ self.reg.registerUtility(request_iface, IRouteRequest, name=name)
+
+ if view:
+ view_for = view_for or for_
+ view_request_type = view_request_type or request_type
+ view_permission = view_permission or permission
+ view_renderer = view_renderer or renderer
+ self.view(
+ permission=view_permission,
+ for_=view_for,
+ view=view,
+ name='',
+ request_type=view_request_type,
+ route_name=name,
+ request_method=view_request_method,
+ request_param=view_request_param,
+ containment=view_containment,
+ attr=view_attr,
+ renderer=view_renderer,
+ header=view_header,
+ accept=view_accept,
+ xhr=view_xhr,
+ path_info=view_path_info,
+ info=_info,
+ )
+
+ mapper = self.reg.queryUtility(IRoutesMapper)
+ if mapper is None:
+ mapper = RoutesMapper()
+ self.reg.registerUtility(mapper, IRoutesMapper)
+ mapper.connect(path, name, factory, predicates=predicates)
+
+ def scan(self, package, _info=u'', martian=martian):
+ # martian overrideable only for unit tests
+ multi_grokker = BFGMultiGrokker()
+ multi_grokker.register(BFGViewGrokker())
+ module_grokker = martian.ModuleGrokker(grokker=multi_grokker)
+ martian.grok_dotted_name(
+ package.__name__, grokker=module_grokker,
+ _info=_info, _configurator=self,
+ exclude_filter=lambda name: name.startswith('.'))
+
+ def authentication_policy(self, policy, _info=u''):
+ self.reg.registerUtility(policy, IAuthenticationPolicy, info=_info)
+
+ def authorization_policy(self, policy, _info=u''):
+ self.reg.registerUtility(policy, IAuthorizationPolicy, info=_info)
+
+ def renderer(self, factory, name, _info=u''):
+ iface = IRendererFactory
+ if name.startswith('.'):
+ iface = ITemplateRendererFactory
+ self.reg.registerUtility(factory, iface, name=name, info=_info)
+
+ def resource(self, to_override, override_with, _override=None,
+ _info=u''):
+ if to_override == override_with:
+ raise ConfigurationError('You cannot override a resource with '
+ 'itself')
+
+ package = to_override
+ path = ''
+ if ':' in to_override:
+ package, path = to_override.split(':', 1)
+
+ override_package = override_with
+ override_prefix = ''
+ if ':' in override_with:
+ override_package, override_prefix = override_with.split(':', 1)
+
+ if path and path.endswith('/'):
+ if override_prefix and (not override_prefix.endswith('/')):
+ raise ConfigurationError(
+ 'A directory cannot be overridden with a file (put a slash '
+ 'at the end of override_with if necessary)')
+
+ if override_prefix and override_prefix.endswith('/'):
+ if path and (not path.endswith('/')):
+ raise ConfigurationError(
+ 'A file cannot be overridden with a directory (put a slash '
+ 'at the end of to_override if necessary)')
+
+ __import__(package)
+ __import__(override_package)
+ package = sys.modules[package]
+ override_package = sys.modules[override_package]
+
+ if _override is not None:
+ _override(package, path, override_package, override_prefix)
+ else:
+ self._override(package, path, override_package, override_prefix)
+
+ def _override(self, package, path, override_package, override_prefix,
+ _info=u'', PackageOverrides=PackageOverrides):
+ pkg_name = package.__name__
+ override_pkg_name = override_package.__name__
+ override = self.reg.queryUtility(IPackageOverrides, name=pkg_name)
+ if override is None:
+ override = PackageOverrides(package)
+ self.reg.registerUtility(override, IPackageOverrides,
+ name=pkg_name, info=_info)
+ override.insert(path, override_pkg_name, override_prefix)
+
+
+ def notfound(self, view=None, attr=None, renderer=None, wrapper=None,
+ _info=u''):
+ self.view_utility(view, attr, renderer, wrapper, INotFoundView,
+ _info=_info)
+
+ def forbidden(self, view=None, attr=None, renderer=None, wrapper=None,
+ _info=u''):
+ self.view_utility(view, attr, renderer, wrapper,
+ IForbiddenView, _info=_info)
+
+ def view_utility(self, view, attr, renderer, wrapper, iface, _info=u''):
+ if not view:
+ if renderer:
+ def view(context, request):
+ return {}
+ else:
+ raise ConfigurationError('"view" attribute was not specified and '
+ 'no renderer specified')
+
+ derived_view = self.derive_view(view, attr=attr, renderer_name=renderer,
+ wrapper_viewname=wrapper)
+ self.reg.registerUtility(derived_view, iface, '', info=_info)
+
+ def static(self, name, path, cache_max_age=3600, _info=u''):
+ view = static_view(path, cache_max_age=cache_max_age)
+ self.route(name, "%s*subpath" % name, view=view,
+ view_for=StaticRootFactory, factory=StaticRootFactory(path),
+ _info=_info)
+
+ def settings(self, settings):
+ self.reg.registerUtility(settings, ISettings)
+
+ def debug_logger(self, logger):
+ if logger is None:
+ logger = make_stream_logger('repoze.bfg.debug', sys.stderr)
+ self.reg.registerUtility(logger, ILogger, 'repoze.bfg.debug')
+
+ def root_factory(self, factory):
+ self.reg.registerUtility(factory, IRootFactory)
+ self.reg.registerUtility(factory, IDefaultRootFactory) # b/c
+
+def _make_predicates(xhr=None, request_method=None, path_info=None,
+ request_param=None, header=None, accept=None,
+ containment=None):
+ # Predicates are added to the predicate list in (presumed)
+ # computation expense order. All predicates associated with a
+ # view must evaluate true for the view to "match" a request.
+ # Elsewhere in the code, we evaluate them using a generator
+ # expression. The fastest predicate should be evaluated first,
+ # then the next fastest, and so on, as if one returns false, the
+ # remainder of the predicates won't need to be evaluated.
+
+ # Each predicate is associated with a weight value. The weight
+ # symbolizes the relative potential "importance" of the predicate
+ # to all other predicates. A larger weight indicates greater
+ # importance. These weights are subtracted from an aggregate
+ # 'weight' variable. The aggregate weight is then divided by the
+ # length of the predicate list to compute a "score" for this view.
+ # The score represents the ordering in which a "multiview" ( a
+ # collection of views that share the same context/request/name
+ # triad but differ in other ways via predicates) will attempt to
+ # call its set of views. Views with lower scores will be tried
+ # first. The intent is to a) ensure that views with more
+ # predicates are always evaluated before views with fewer
+ # predicates and b) to ensure a stable call ordering of views that
+ # share the same number of predicates.
+
+ # Views which do not have any predicates get a score of
+ # sys.maxint, meaning that they will be tried very last.
+
+ predicates = []
+ weight = sys.maxint
+
+ if xhr:
+ def xhr_predicate(context, request):
+ return request.is_xhr
+ weight = weight - 10
+ predicates.append(xhr_predicate)
+
+ if request_method is not None:
+ def request_method_predicate(context, request):
+ return request.method == request_method
+ weight = weight - 20
+ predicates.append(request_method_predicate)
+
+ if path_info is not None:
+ try:
+ path_info_val = re.compile(path_info)
+ except re.error, why:
+ raise ConfigurationError(why[0])
+ def path_info_predicate(context, request):
+ return path_info_val.match(request.path_info) is not None
+ weight = weight - 30
+ predicates.append(path_info_predicate)
+
+ if request_param is not None:
+ request_param_val = None
+ if '=' in request_param:
+ request_param, request_param_val = request_param.split('=', 1)
+ def request_param_predicate(context, request):
+ if request_param_val is None:
+ return request_param in request.params
+ return request.params.get(request_param) == request_param_val
+ weight = weight - 40
+ predicates.append(request_param_predicate)
+
+ if header is not None:
+ header_name = header
+ header_val = None
+ if ':' in header:
+ header_name, header_val = header.split(':', 1)
+ try:
+ header_val = re.compile(header_val)
+ except re.error, why:
+ raise ConfigurationError(why[0])
+ def header_predicate(context, request):
+ if header_val is None:
+ return header_name in request.headers
+ val = request.headers.get(header_name)
+ return header_val.match(val) is not None
+ weight = weight - 50
+ predicates.append(header_predicate)
+
+ if accept is not None:
+ def accept_predicate(context, request):
+ return accept in request.accept
+ weight = weight - 60
+ predicates.append(accept_predicate)
+
+ if containment is not None:
+ def containment_predicate(context, request):
+ return find_interface(context, containment) is not None
+ weight = weight - 70
+ predicates.append(containment_predicate)
+
+ # this will be == sys.maxint if no predicates
+ score = weight / (len(predicates) + 1)
+ return score, predicates
+
+class BFGViewMarker(object):
+ pass
+
+class BFGMultiGrokker(martian.core.MultiInstanceOrClassGrokkerBase):
+ def get_bases(self, obj):
+ if hasattr(obj, '__bfg_view_settings__'):
+ return [BFGViewMarker]
+ return []
+
+class BFGViewGrokker(martian.InstanceGrokker):
+ martian.component(BFGViewMarker)
+ def grok(self, name, obj, **kw):
+ config = getattr(obj, '__bfg_view_settings__', [])
+ for settings in config:
+ config = kw['_configurator']
+ info = kw.get('_info', u'')
+ config.view(view=obj, _info=info, **settings)
+ return bool(config)
+
+class DefaultRootFactory:
+ __parent__ = None
+ __name__ = None
+ def __init__(self, request):
+ matchdict = getattr(request, 'matchdict', {})
+ # provide backwards compatibility for applications which
+ # used routes (at least apps without any custom "context
+ # factory") in BFG 0.9.X and before
+ self.__dict__.update(matchdict)
+