diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-11-17 21:59:54 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-11-17 21:59:54 +0000 |
| commit | cbfafba1514ce2ce2b87aadb0093c06210219372 (patch) | |
| tree | 41e83cf95cf4cf8920396e644a219518689e8b89 /repoze/bfg/configuration.py | |
| parent | a937e7d039f2c9e62e1a2771b2e6b23412ab709a (diff) | |
| download | pyramid-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.py | 787 |
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) + |
