| | |
| | | import warnings |
| | | |
| | | from webob.acceptparse import Accept |
| | | from zope.interface import ( |
| | | Interface, |
| | | implementedBy, |
| | | implementer, |
| | | ) |
| | | from zope.interface import Interface, implementedBy, implementer |
| | | from zope.interface.interfaces import IInterface |
| | | |
| | | from pyramid.interfaces import ( |
| | |
| | | IViewDeriverInfo, |
| | | IViewMapperFactory, |
| | | PHASE1_CONFIG, |
| | | ) |
| | | ) |
| | | |
| | | from pyramid import renderers |
| | | |
| | |
| | | url_quote, |
| | | WIN, |
| | | is_nonstr_iter, |
| | | ) |
| | | ) |
| | | |
| | | from pyramid.decorator import reify |
| | | |
| | | from pyramid.exceptions import ( |
| | | ConfigurationError, |
| | | PredicateMismatch, |
| | | ) |
| | | from pyramid.exceptions import ConfigurationError, PredicateMismatch |
| | | |
| | | from pyramid.httpexceptions import ( |
| | | HTTPForbidden, |
| | | HTTPNotFound, |
| | | default_exceptionresponse_view, |
| | | ) |
| | | ) |
| | | |
| | | from pyramid.registry import Deferred |
| | | |
| | |
| | | |
| | | from pyramid.view import AppendSlashNotFoundViewFactory |
| | | |
| | | from pyramid.util import ( |
| | | as_sorted_tuple, |
| | | TopologicalSorter, |
| | | ) |
| | | from pyramid.util import as_sorted_tuple, TopologicalSorter |
| | | |
| | | import pyramid.predicates |
| | | import pyramid.viewderivers |
| | |
| | | wraps_view, |
| | | ) |
| | | |
| | | from pyramid.config.util import ( |
| | | action_method, |
| | | from pyramid.config.actions import action_method |
| | | from pyramid.config.predicates import ( |
| | | DEFAULT_PHASH, |
| | | MAX_ORDER, |
| | | normalize_accept_offer, |
| | | predvalseq, |
| | | sort_accept_offers, |
| | | ) |
| | | ) |
| | | |
| | | urljoin = urlparse.urljoin |
| | | url_parse = urlparse.urlparse |
| | | |
| | | DefaultViewMapper = DefaultViewMapper # bw-compat |
| | | preserve_view_attrs = preserve_view_attrs # bw-compat |
| | | requestonly = requestonly # bw-compat |
| | | view_description = view_description # bw-compat |
| | | DefaultViewMapper = DefaultViewMapper # bw-compat |
| | | preserve_view_attrs = preserve_view_attrs # bw-compat |
| | | requestonly = requestonly # bw-compat |
| | | view_description = view_description # bw-compat |
| | | |
| | | |
| | | @implementer(IMultiView) |
| | | class MultiView(object): |
| | | |
| | | def __init__(self, name): |
| | | self.name = name |
| | | self.media_views = {} |
| | |
| | | continue |
| | | raise PredicateMismatch(self.name) |
| | | |
| | | |
| | | def attr_wrapped_view(view, info): |
| | | accept, order, phash = (info.options.get('accept', None), |
| | | getattr(info, 'order', MAX_ORDER), |
| | | getattr(info, 'phash', DEFAULT_PHASH)) |
| | | accept, order, phash = ( |
| | | info.options.get('accept', None), |
| | | getattr(info, 'order', MAX_ORDER), |
| | | getattr(info, '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 |
| | | 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 |
| | |
| | | attr_view.__permission__ = info.options.get('permission') |
| | | return attr_view |
| | | |
| | | |
| | | attr_wrapped_view.options = ('accept', 'attr', 'permission') |
| | | |
| | | |
| | | def predicated_view(view, info): |
| | | preds = info.predicates |
| | | if not preds: |
| | | return view |
| | | |
| | | def predicate_wrapper(context, request): |
| | | for predicate in preds: |
| | | if not predicate(context, request): |
| | | view_name = getattr(view, '__name__', view) |
| | | raise PredicateMismatch( |
| | | 'predicate mismatch for view %s (%s)' % ( |
| | | view_name, predicate.text())) |
| | | 'predicate mismatch for view %s (%s)' |
| | | % (view_name, predicate.text()) |
| | | ) |
| | | return view(context, request) |
| | | |
| | | def checker(context, request): |
| | | return all((predicate(context, request) for predicate in |
| | | preds)) |
| | | return all((predicate(context, request) for predicate in preds)) |
| | | |
| | | predicate_wrapper.__predicated__ = checker |
| | | predicate_wrapper.__predicates__ = preds |
| | | return predicate_wrapper |
| | | |
| | | |
| | | def viewdefaults(wrapped): |
| | | """ Decorator for add_view-like methods which takes into account |
| | | __view_defaults__ attached to view it is passed. Not a documented API but |
| | | used by some external systems.""" |
| | | |
| | | def wrapper(self, *arg, **kw): |
| | | defaults = {} |
| | | if arg: |
| | |
| | | if inspect.isclass(view): |
| | | defaults = getattr(view, '__view_defaults__', {}).copy() |
| | | if '_backframes' not in kw: |
| | | kw['_backframes'] = 1 # for action_method |
| | | kw['_backframes'] = 1 # for action_method |
| | | defaults.update(kw) |
| | | return wrapped(self, *arg, **defaults) |
| | | |
| | | return functools.wraps(wrapped)(wrapper) |
| | | |
| | | |
| | | def combine_decorators(*decorators): |
| | | def decorated(view_callable): |
| | |
| | | for decorator in reversed(decorators): |
| | | view_callable = decorator(view_callable) |
| | | return view_callable |
| | | |
| | | return decorated |
| | | |
| | | |
| | | class ViewsConfiguratorMixin(object): |
| | | @viewdefaults |
| | |
| | | check_csrf=None, |
| | | require_csrf=None, |
| | | exception_only=False, |
| | | **view_options): |
| | | **view_options |
| | | ): |
| | | """ Add a :term:`view configuration` to the current |
| | | configuration state. Arguments to ``add_view`` are broken |
| | | down below into *predicate* arguments and *non-predicate* |
| | |
| | | If CSRF checking is performed, the checked value will be the value of |
| | | ``request.params[check_name]``. This value will be compared against |
| | | the value of ``policy.get_csrf_token()`` (where ``policy`` is an |
| | | implementation of :meth:`pyramid.interfaces.ICSRFStoragePolicy`), and the |
| | | check will pass if these two values are the same. If the check |
| | | implementation of :meth:`pyramid.interfaces.ICSRFStoragePolicy`), and |
| | | the check will pass if these two values are the same. If the check |
| | | passes, the associated view will be permitted to execute. If the |
| | | check fails, the associated view will not be permitted to execute. |
| | | |
| | |
| | | """ |
| | | if custom_predicates: |
| | | warnings.warn( |
| | | ('The "custom_predicates" argument to Configurator.add_view ' |
| | | 'is deprecated as of Pyramid 1.5. Use ' |
| | | '"config.add_view_predicate" and use the registered ' |
| | | 'view predicate as a predicate argument to add_view instead. ' |
| | | 'See "Adding A Third Party View, Route, or Subscriber ' |
| | | 'Predicate" in the "Hooks" chapter of the documentation ' |
| | | 'for more information.'), |
| | | ( |
| | | 'The "custom_predicates" argument to ' |
| | | 'Configurator.add_view is deprecated as of Pyramid 1.5. ' |
| | | 'Use "config.add_view_predicate" and use the registered ' |
| | | 'view predicate as a predicate argument to add_view ' |
| | | 'instead. See "Adding A Third Party View, Route, or ' |
| | | 'Subscriber Predicate" in the "Hooks" chapter of the ' |
| | | 'documentation for more information.' |
| | | ), |
| | | DeprecationWarning, |
| | | stacklevel=4, |
| | | ) |
| | | ) |
| | | |
| | | if check_csrf is not None: |
| | | warnings.warn( |
| | | ('The "check_csrf" argument to Configurator.add_view is ' |
| | | 'deprecated as of Pyramid 1.7. Use the "require_csrf" option ' |
| | | 'instead or see "Checking CSRF Tokens Automatically" in the ' |
| | | '"Sessions" chapter of the documentation for more ' |
| | | 'information.'), |
| | | ( |
| | | 'The "check_csrf" argument to Configurator.add_view is ' |
| | | 'deprecated as of Pyramid 1.7. Use the "require_csrf" ' |
| | | 'option instead or see "Checking CSRF Tokens ' |
| | | 'Automatically" in the "Sessions" chapter of the ' |
| | | 'documentation for more information.' |
| | | ), |
| | | DeprecationWarning, |
| | | stacklevel=4, |
| | | ) |
| | | ) |
| | | |
| | | if accept is not None: |
| | | if is_nonstr_iter(accept): |
| | | raise ConfigurationError( |
| | | 'A list is not supported in the "accept" view predicate.', |
| | | 'A list is not supported in the "accept" view predicate.' |
| | | ) |
| | | if '*' in accept: |
| | | warnings.warn( |
| | | ('Passing a media range to the "accept" argument of ' |
| | | 'Configurator.add_view is deprecated as of Pyramid 1.10. ' |
| | | 'Use explicit media types to avoid ambiguities in ' |
| | | 'content negotiation that may impact your users.'), |
| | | ( |
| | | 'Passing a media range to the "accept" argument of ' |
| | | 'Configurator.add_view is deprecated as of ' |
| | | 'Pyramid 1.10. Use explicit media types to avoid ' |
| | | 'ambiguities in content negotiation that may impact ' |
| | | 'your users.' |
| | | ), |
| | | DeprecationWarning, |
| | | stacklevel=4, |
| | | ) |
| | | ) |
| | | # XXX when media ranges are gone, switch allow_range=False |
| | | accept = normalize_accept_offer(accept, allow_range=True) |
| | | |
| | |
| | | |
| | | if not view: |
| | | if renderer: |
| | | |
| | | def view(context, request): |
| | | return {} |
| | | |
| | | else: |
| | | raise ConfigurationError('"view" was not specified and ' |
| | | 'no "renderer" specified') |
| | | raise ConfigurationError( |
| | | '"view" was not specified and ' 'no "renderer" specified' |
| | | ) |
| | | |
| | | if request_type is not None: |
| | | request_type = self.maybe_dotted(request_type) |
| | | if not IInterface.providedBy(request_type): |
| | | raise ConfigurationError( |
| | | 'request_type must be an interface, not %s' % request_type) |
| | | 'request_type must be an interface, not %s' % request_type |
| | | ) |
| | | |
| | | if context is None: |
| | | context = for_ |
| | |
| | | if exception_only and not isexc: |
| | | raise ConfigurationError( |
| | | 'view "context" must be an exception type when ' |
| | | '"exception_only" is True') |
| | | '"exception_only" is True' |
| | | ) |
| | | |
| | | r_context = context |
| | | if r_context is None: |
| | |
| | | |
| | | if isinstance(renderer, string_types): |
| | | renderer = renderers.RendererHelper( |
| | | name=renderer, package=self.package, |
| | | registry=self.registry) |
| | | name=renderer, package=self.package, registry=self.registry |
| | | ) |
| | | |
| | | introspectables = [] |
| | | ovals = view_options.copy() |
| | | ovals.update(dict( |
| | | xhr=xhr, |
| | | request_method=request_method, |
| | | path_info=path_info, |
| | | request_param=request_param, |
| | | header=header, |
| | | accept=accept, |
| | | containment=containment, |
| | | request_type=request_type, |
| | | match_param=match_param, |
| | | check_csrf=check_csrf, |
| | | custom=predvalseq(custom_predicates), |
| | | )) |
| | | ovals.update( |
| | | dict( |
| | | xhr=xhr, |
| | | request_method=request_method, |
| | | path_info=path_info, |
| | | request_param=request_param, |
| | | header=header, |
| | | accept=accept, |
| | | containment=containment, |
| | | request_type=request_type, |
| | | match_param=match_param, |
| | | check_csrf=check_csrf, |
| | | custom=predvalseq(custom_predicates), |
| | | ) |
| | | ) |
| | | |
| | | def discrim_func(): |
| | | # We need to defer the discriminator until we know what the phash |
| | |
| | | |
| | | order, preds, phash = predlist.make(self, **pvals) |
| | | |
| | | view_intr.update({ |
| | | 'phash': phash, |
| | | 'order': order, |
| | | 'predicates': preds, |
| | | }) |
| | | view_intr.update( |
| | | {'phash': phash, 'order': order, 'predicates': preds} |
| | | ) |
| | | return ('view', context, name, route_name, phash) |
| | | |
| | | discriminator = Deferred(discrim_func) |
| | | |
| | | if inspect.isclass(view) and attr: |
| | | view_desc = 'method %r of %s' % ( |
| | | attr, self.object_description(view)) |
| | | attr, |
| | | self.object_description(view), |
| | | ) |
| | | else: |
| | | view_desc = self.object_description(view) |
| | | |
| | | tmpl_intr = None |
| | | |
| | | view_intr = self.introspectable('views', |
| | | discriminator, |
| | | view_desc, |
| | | 'view') |
| | | view_intr.update(dict( |
| | | name=name, |
| | | context=context, |
| | | exception_only=exception_only, |
| | | containment=containment, |
| | | request_param=request_param, |
| | | request_methods=request_method, |
| | | route_name=route_name, |
| | | attr=attr, |
| | | xhr=xhr, |
| | | accept=accept, |
| | | header=header, |
| | | path_info=path_info, |
| | | match_param=match_param, |
| | | check_csrf=check_csrf, |
| | | http_cache=http_cache, |
| | | require_csrf=require_csrf, |
| | | callable=view, |
| | | mapper=mapper, |
| | | decorator=decorator, |
| | | )) |
| | | view_intr = self.introspectable( |
| | | 'views', discriminator, view_desc, 'view' |
| | | ) |
| | | view_intr.update( |
| | | dict( |
| | | name=name, |
| | | context=context, |
| | | exception_only=exception_only, |
| | | containment=containment, |
| | | request_param=request_param, |
| | | request_methods=request_method, |
| | | route_name=route_name, |
| | | attr=attr, |
| | | xhr=xhr, |
| | | accept=accept, |
| | | header=header, |
| | | path_info=path_info, |
| | | match_param=match_param, |
| | | check_csrf=check_csrf, |
| | | http_cache=http_cache, |
| | | require_csrf=require_csrf, |
| | | callable=view, |
| | | mapper=mapper, |
| | | decorator=decorator, |
| | | ) |
| | | ) |
| | | view_intr.update(view_options) |
| | | introspectables.append(view_intr) |
| | | |
| | | def register(permission=permission, renderer=renderer): |
| | | request_iface = IRequest |
| | | if route_name is not None: |
| | | request_iface = self.registry.queryUtility(IRouteRequest, |
| | | name=route_name) |
| | | request_iface = self.registry.queryUtility( |
| | | IRouteRequest, name=route_name |
| | | ) |
| | | if request_iface is None: |
| | | # route configuration should have already happened in |
| | | # phase 2 |
| | | raise ConfigurationError( |
| | | 'No route named %s found for view registration' % |
| | | route_name) |
| | | 'No route named %s found for view registration' |
| | | % route_name |
| | | ) |
| | | |
| | | if renderer is None: |
| | | # use default renderer if one exists (reg'd in phase 1) |
| | | if self.registry.queryUtility(IRendererFactory) is not None: |
| | | renderer = renderers.RendererHelper( |
| | | name=None, |
| | | package=self.package, |
| | | registry=self.registry |
| | | ) |
| | | name=None, package=self.package, registry=self.registry |
| | | ) |
| | | |
| | | renderer_type = getattr(renderer, 'type', None) |
| | | intrspc = self.introspector |
| | | if ( |
| | | renderer_type is not None and |
| | | tmpl_intr is not None and |
| | | intrspc is not None and |
| | | intrspc.get('renderer factories', renderer_type) is not None |
| | | ): |
| | | renderer_type is not None |
| | | and tmpl_intr is not None |
| | | and intrspc is not None |
| | | and intrspc.get('renderer factories', renderer_type) |
| | | is not None |
| | | ): |
| | | # allow failure of registered template factories to be deferred |
| | | # until view execution, like other bad renderer factories; if |
| | | # we tried to relate this to an existing renderer factory |
| | |
| | | register_view(IViewClassifier, request_iface, derived_view) |
| | | if isexc: |
| | | derived_exc_view = derive_view(True, renderer) |
| | | register_view(IExceptionViewClassifier, request_iface, |
| | | derived_exc_view) |
| | | register_view( |
| | | IExceptionViewClassifier, request_iface, derived_exc_view |
| | | ) |
| | | |
| | | if exception_only: |
| | | derived_view = derived_exc_view |
| | |
| | | |
| | | for view_type in (IView, ISecuredView, IMultiView): |
| | | old_view = registered( |
| | | (classifier, request_iface, r_context), |
| | | view_type, name) |
| | | (classifier, request_iface, r_context), view_type, name |
| | | ) |
| | | if old_view is not None: |
| | | break |
| | | |
| | |
| | | derived_view, |
| | | (classifier, request_iface, context), |
| | | view_iface, |
| | | name |
| | | ) |
| | | name, |
| | | ) |
| | | |
| | | else: |
| | | # - A view or multiview was already registered for this |
| | |
| | | # unregister any existing views |
| | | self.registry.adapters.unregister( |
| | | (classifier, request_iface, r_context), |
| | | view_type, name=name) |
| | | view_type, |
| | | name=name, |
| | | ) |
| | | self.registry.registerAdapter( |
| | | multiview, |
| | | (classifier, request_iface, context), |
| | | IMultiView, name=name) |
| | | IMultiView, |
| | | name=name, |
| | | ) |
| | | |
| | | if mapper: |
| | | mapper_intr = self.introspectable( |
| | | 'view mappers', |
| | | discriminator, |
| | | 'view mapper for %s' % view_desc, |
| | | 'view mapper' |
| | | ) |
| | | 'view mapper', |
| | | ) |
| | | mapper_intr['mapper'] = mapper |
| | | mapper_intr.relate('views', discriminator) |
| | | introspectables.append(mapper_intr) |
| | | if route_name: |
| | | view_intr.relate('routes', route_name) # see add_route |
| | | view_intr.relate('routes', route_name) # see add_route |
| | | if renderer is not None and renderer.name and '.' in renderer.name: |
| | | # the renderer is a template |
| | | tmpl_intr = self.introspectable( |
| | | 'templates', |
| | | discriminator, |
| | | renderer.name, |
| | | 'template' |
| | | ) |
| | | 'templates', discriminator, renderer.name, 'template' |
| | | ) |
| | | tmpl_intr.relate('views', discriminator) |
| | | tmpl_intr['name'] = renderer.name |
| | | tmpl_intr['type'] = renderer.type |
| | |
| | | if permission is not None: |
| | | # if a permission exists, register a permission introspectable |
| | | perm_intr = self.introspectable( |
| | | 'permissions', |
| | | permission, |
| | | permission, |
| | | 'permission' |
| | | ) |
| | | 'permissions', permission, permission, 'permission' |
| | | ) |
| | | perm_intr['value'] = permission |
| | | perm_intr.relate('views', discriminator) |
| | | introspectables.append(perm_intr) |
| | |
| | | |
| | | def _apply_view_derivers(self, info): |
| | | # These derivers are not really derivers and so have fixed order |
| | | outer_derivers = [('attr_wrapped_view', attr_wrapped_view), |
| | | ('predicated_view', predicated_view)] |
| | | outer_derivers = [ |
| | | ('attr_wrapped_view', attr_wrapped_view), |
| | | ('predicated_view', predicated_view), |
| | | ] |
| | | |
| | | view = info.original_view |
| | | derivers = self.registry.getUtility(IViewDerivers) |
| | |
| | | return view |
| | | |
| | | @action_method |
| | | def add_view_predicate(self, name, factory, weighs_more_than=None, |
| | | weighs_less_than=None): |
| | | def add_view_predicate( |
| | | self, name, factory, weighs_more_than=None, weighs_less_than=None |
| | | ): |
| | | """ |
| | | .. versionadded:: 1.4 |
| | | |
| | |
| | | name, |
| | | factory, |
| | | weighs_more_than=weighs_more_than, |
| | | weighs_less_than=weighs_less_than |
| | | ) |
| | | weighs_less_than=weighs_less_than, |
| | | ) |
| | | |
| | | def add_default_view_predicates(self): |
| | | p = pyramid.predicates |
| | |
| | | ('physical_path', p.PhysicalPathPredicate), |
| | | ('effective_principals', p.EffectivePrincipalsPredicate), |
| | | ('custom', p.CustomPredicate), |
| | | ): |
| | | ): |
| | | self.add_view_predicate(name, factory) |
| | | |
| | | def add_default_accept_view_order(self): |
| | |
| | | |
| | | @action_method |
| | | def add_accept_view_order( |
| | | self, |
| | | value, |
| | | weighs_more_than=None, |
| | | weighs_less_than=None, |
| | | self, value, weighs_more_than=None, weighs_less_than=None |
| | | ): |
| | | """ |
| | | Specify an ordering preference for the ``accept`` view option used |
| | |
| | | .. versionadded:: 1.10 |
| | | |
| | | """ |
| | | |
| | | def check_type(than): |
| | | than_type, than_subtype, than_params = Accept.parse_offer(than) |
| | | # text/plain vs text/html;charset=utf8 |
| | | if bool(offer_params) ^ bool(than_params): |
| | | raise ConfigurationError( |
| | | 'cannot compare a media type with params to one without ' |
| | | 'params') |
| | | 'params' |
| | | ) |
| | | # text/plain;charset=utf8 vs text/html;charset=utf8 |
| | | if offer_params and ( |
| | | offer_subtype != than_subtype or offer_type != than_type |
| | | ): |
| | | raise ConfigurationError( |
| | | 'cannot compare params across different media types') |
| | | 'cannot compare params across different media types' |
| | | ) |
| | | |
| | | def normalize_types(thans): |
| | | thans = [normalize_accept_offer(than) for than in thans] |
| | |
| | | |
| | | discriminator = ('accept view order', value) |
| | | intr = self.introspectable( |
| | | 'accept view order', |
| | | value, |
| | | value, |
| | | 'accept view order') |
| | | 'accept view order', value, value, 'accept view order' |
| | | ) |
| | | intr['value'] = value |
| | | intr['weighs_more_than'] = weighs_more_than |
| | | intr['weighs_less_than'] = weighs_less_than |
| | | |
| | | def register(): |
| | | sorter = self.registry.queryUtility(IAcceptOrder) |
| | | if sorter is None: |
| | | sorter = TopologicalSorter() |
| | | self.registry.registerUtility(sorter, IAcceptOrder) |
| | | sorter.add( |
| | | value, value, |
| | | before=weighs_more_than, |
| | | after=weighs_less_than, |
| | | value, value, before=weighs_more_than, after=weighs_less_than |
| | | ) |
| | | self.action(discriminator, register, introspectables=(intr,), |
| | | order=PHASE1_CONFIG) # must be registered before add_view |
| | | |
| | | self.action( |
| | | discriminator, |
| | | register, |
| | | introspectables=(intr,), |
| | | order=PHASE1_CONFIG, |
| | | ) # must be registered before add_view |
| | | |
| | | @action_method |
| | | def add_view_deriver(self, deriver, name=None, under=None, over=None): |
| | |
| | | name = deriver.__name__ |
| | | |
| | | if name in (INGRESS, VIEW): |
| | | raise ConfigurationError('%s is a reserved view deriver name' |
| | | % name) |
| | | raise ConfigurationError( |
| | | '%s is a reserved view deriver name' % name |
| | | ) |
| | | |
| | | if under is None: |
| | | under = 'decorated_view' |
| | |
| | | raise ConfigurationError('%s cannot be under "mapped_view"' % name) |
| | | |
| | | discriminator = ('view deriver', name) |
| | | intr = self.introspectable( |
| | | 'view derivers', |
| | | name, |
| | | name, |
| | | 'view deriver') |
| | | intr = self.introspectable('view derivers', name, name, 'view deriver') |
| | | intr['name'] = name |
| | | intr['deriver'] = deriver |
| | | intr['under'] = under |
| | | intr['over'] = over |
| | | |
| | | def register(): |
| | | derivers = self.registry.queryUtility(IViewDerivers) |
| | | if derivers is None: |
| | |
| | | ) |
| | | self.registry.registerUtility(derivers, IViewDerivers) |
| | | derivers.add(name, deriver, before=over, after=under) |
| | | self.action(discriminator, register, introspectables=(intr,), |
| | | order=PHASE1_CONFIG) # must be registered before add_view |
| | | |
| | | self.action( |
| | | discriminator, |
| | | register, |
| | | introspectables=(intr,), |
| | | order=PHASE1_CONFIG, |
| | | ) # must be registered before add_view |
| | | |
| | | def add_default_view_derivers(self): |
| | | d = pyramid.viewderivers |
| | |
| | | ] |
| | | last = INGRESS |
| | | for name, deriver in derivers: |
| | | self.add_view_deriver( |
| | | deriver, |
| | | name=name, |
| | | under=last, |
| | | over=VIEW, |
| | | ) |
| | | self.add_view_deriver(deriver, name=name, under=last, over=VIEW) |
| | | last = name |
| | | |
| | | # leave the csrf_view loosely coupled to the rest of the pipeline |
| | |
| | | return self._derive_view(view, attr=attr, renderer=renderer) |
| | | |
| | | # 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, decorator=None, route_name=None, |
| | | mapper=None, http_cache=None, context=None, |
| | | require_csrf=None, exception_only=False, |
| | | extra_options=None): |
| | | 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, |
| | | decorator=None, |
| | | route_name=None, |
| | | mapper=None, |
| | | http_cache=None, |
| | | context=None, |
| | | require_csrf=None, |
| | | exception_only=False, |
| | | extra_options=None, |
| | | ): |
| | | view = self.maybe_dotted(view) |
| | | mapper = self.maybe_dotted(mapper) |
| | | if isinstance(renderer, string_types): |
| | | renderer = renderers.RendererHelper( |
| | | name=renderer, package=self.package, |
| | | registry=self.registry) |
| | | name=renderer, package=self.package, registry=self.registry |
| | | ) |
| | | if renderer is None: |
| | | # use default renderer if one exists |
| | | if self.registry.queryUtility(IRendererFactory) is not None: |
| | | renderer = renderers.RendererHelper( |
| | | name=None, |
| | | package=self.package, |
| | | registry=self.registry) |
| | | name=None, package=self.package, registry=self.registry |
| | | ) |
| | | |
| | | options = dict( |
| | | view=view, |
| | |
| | | decorator=decorator, |
| | | http_cache=http_cache, |
| | | require_csrf=require_csrf, |
| | | route_name=route_name |
| | | route_name=route_name, |
| | | ) |
| | | if extra_options: |
| | | options.update(extra_options) |
| | |
| | | mapper=None, |
| | | match_param=None, |
| | | **view_options |
| | | ): |
| | | ): |
| | | """ Add a forbidden view to the current configuration state. The |
| | | view will be called when Pyramid or application code raises a |
| | | :exc:`pyramid.httpexceptions.HTTPForbidden` exception and the set of |
| | |
| | | The view is created using ``exception_only=True``. |
| | | """ |
| | | for arg in ( |
| | | 'name', 'permission', 'context', 'for_', 'require_csrf', |
| | | 'name', |
| | | 'permission', |
| | | 'context', |
| | | 'for_', |
| | | 'require_csrf', |
| | | 'exception_only', |
| | | ): |
| | | if arg in view_options: |
| | | raise ConfigurationError( |
| | | '%s may not be used as an argument to add_forbidden_view' |
| | | % (arg,)) |
| | | % (arg,) |
| | | ) |
| | | |
| | | if view is None: |
| | | view = default_exceptionresponse_view |
| | |
| | | require_csrf=False, |
| | | attr=attr, |
| | | renderer=renderer, |
| | | ) |
| | | ) |
| | | settings.update(view_options) |
| | | return self.add_view(**settings) |
| | | |
| | | set_forbidden_view = add_forbidden_view # deprecated sorta-bw-compat alias |
| | | set_forbidden_view = add_forbidden_view # deprecated sorta-bw-compat alias |
| | | |
| | | @viewdefaults |
| | | @action_method |
| | |
| | | match_param=None, |
| | | append_slash=False, |
| | | **view_options |
| | | ): |
| | | ): |
| | | """ Add a default :term:`Not Found View` to the current configuration |
| | | state. The view will be called when Pyramid or application code raises |
| | | an :exc:`pyramid.httpexceptions.HTTPNotFound` exception (e.g., when a |
| | |
| | | config.add_notfound_view(append_slash=HTTPMovedPermanently) |
| | | |
| | | The above means that a redirect to a slash-appended route will be |
| | | attempted, but instead of :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect` |
| | | attempted, but instead of |
| | | :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect` |
| | | being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will |
| | | be used` for the redirect response if a slash-appended route is found. |
| | | |
| | |
| | | |
| | | .. versionchanged: 1.10 |
| | | |
| | | Default response was changed from :class:`~pyramid.httpexceptions.HTTPFound` |
| | | Default response was changed from |
| | | :class:`~pyramid.httpexceptions.HTTPFound` |
| | | to :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect`. |
| | | |
| | | """ |
| | | for arg in ( |
| | | 'name', 'permission', 'context', 'for_', 'require_csrf', |
| | | 'name', |
| | | 'permission', |
| | | 'context', |
| | | 'for_', |
| | | 'require_csrf', |
| | | 'exception_only', |
| | | ): |
| | | if arg in view_options: |
| | | raise ConfigurationError( |
| | | '%s may not be used as an argument to add_notfound_view' |
| | | % (arg,)) |
| | | % (arg,) |
| | | ) |
| | | |
| | | if view is None: |
| | | view = default_exceptionresponse_view |
| | |
| | | route_name=route_name, |
| | | permission=NO_PERMISSION_REQUIRED, |
| | | require_csrf=False, |
| | | ) |
| | | ) |
| | | settings.update(view_options) |
| | | if append_slash: |
| | | view = self._derive_view(view, attr=attr, renderer=renderer) |
| | | if IResponse.implementedBy(append_slash): |
| | | view = AppendSlashNotFoundViewFactory( |
| | | view, redirect_class=append_slash, |
| | | view, redirect_class=append_slash |
| | | ) |
| | | else: |
| | | view = AppendSlashNotFoundViewFactory(view) |
| | |
| | | settings['renderer'] = renderer |
| | | return self.add_view(**settings) |
| | | |
| | | set_notfound_view = add_notfound_view # deprecated sorta-bw-compat alias |
| | | set_notfound_view = add_notfound_view # deprecated sorta-bw-compat alias |
| | | |
| | | @viewdefaults |
| | | @action_method |
| | |
| | | context=None, |
| | | # force all other arguments to be specified as key=value |
| | | **view_options |
| | | ): |
| | | ): |
| | | """ Add an :term:`exception view` for the specified ``exception`` to |
| | | the current configuration state. The view will be called when Pyramid |
| | | or application code raises the given exception. |
| | |
| | | .. versionadded:: 1.8 |
| | | """ |
| | | for arg in ( |
| | | 'name', 'for_', 'exception_only', 'require_csrf', 'permission', |
| | | 'name', |
| | | 'for_', |
| | | 'exception_only', |
| | | 'require_csrf', |
| | | 'permission', |
| | | ): |
| | | if arg in view_options: |
| | | raise ConfigurationError( |
| | | '%s may not be used as an argument to add_exception_view' |
| | | % (arg,)) |
| | | % (arg,) |
| | | ) |
| | | if context is None: |
| | | context = Exception |
| | | view_options.update(dict( |
| | | view=view, |
| | | context=context, |
| | | exception_only=True, |
| | | permission=NO_PERMISSION_REQUIRED, |
| | | require_csrf=False, |
| | | )) |
| | | view_options.update( |
| | | dict( |
| | | view=view, |
| | | context=context, |
| | | exception_only=True, |
| | | permission=NO_PERMISSION_REQUIRED, |
| | | require_csrf=False, |
| | | ) |
| | | ) |
| | | return self.add_view(**view_options) |
| | | |
| | | @action_method |
| | |
| | | can be used to achieve the same purpose. |
| | | """ |
| | | mapper = self.maybe_dotted(mapper) |
| | | |
| | | def register(): |
| | | self.registry.registerUtility(mapper, IViewMapperFactory) |
| | | |
| | | # IViewMapperFactory is looked up as the result of view config |
| | | # in phase 3 |
| | | intr = self.introspectable('view mappers', |
| | | IViewMapperFactory, |
| | | self.object_description(mapper), |
| | | 'default view mapper') |
| | | intr = self.introspectable( |
| | | 'view mappers', |
| | | IViewMapperFactory, |
| | | self.object_description(mapper), |
| | | 'default view mapper', |
| | | ) |
| | | intr['mapper'] = mapper |
| | | self.action(IViewMapperFactory, register, order=PHASE1_CONFIG, |
| | | introspectables=(intr,)) |
| | | self.action( |
| | | IViewMapperFactory, |
| | | register, |
| | | order=PHASE1_CONFIG, |
| | | introspectables=(intr,), |
| | | ) |
| | | |
| | | @action_method |
| | | def add_static_view(self, name, path, **kw): |
| | |
| | | self.registry.registerUtility(info, IStaticURLInfo) |
| | | return info |
| | | |
| | | |
| | | def isexception(o): |
| | | if IInterface.providedBy(o): |
| | | if IException.isEqualOrExtendedBy(o): |
| | | return True |
| | | return ( |
| | | isinstance(o, Exception) or |
| | | (inspect.isclass(o) and (issubclass(o, Exception))) |
| | | ) |
| | | return isinstance(o, Exception) or ( |
| | | inspect.isclass(o) and (issubclass(o, Exception)) |
| | | ) |
| | | |
| | | |
| | | def runtime_exc_view(view, excview): |
| | | # create a view callable which can pretend to be both a normal view |
| | |
| | | fn = getattr(selected_view, attr, None) |
| | | if fn is not None: |
| | | return fn(context, request) |
| | | |
| | | return wrapper |
| | | |
| | | # these methods are dynamic per-request and should dispatch to their |
| | |
| | | wrapper_view.__predicates__ = wrap_fn('__predicates__') |
| | | return wrapper_view |
| | | |
| | | |
| | | @implementer(IViewDeriverInfo) |
| | | class ViewDeriverInfo(object): |
| | | def __init__(self, |
| | | view, |
| | | registry, |
| | | package, |
| | | predicates, |
| | | exception_only, |
| | | options, |
| | | ): |
| | | def __init__( |
| | | self, view, registry, package, predicates, exception_only, options |
| | | ): |
| | | self.original_view = view |
| | | self.registry = registry |
| | | self.package = package |
| | |
| | | def settings(self): |
| | | return self.registry.settings |
| | | |
| | | |
| | | @implementer(IStaticURLInfo) |
| | | class StaticURLInfo(object): |
| | | def __init__(self): |
| | |
| | | def generate(self, path, request, **kw): |
| | | for (url, spec, route_name) in self.registrations: |
| | | if path.startswith(spec): |
| | | subpath = path[len(spec):] |
| | | if WIN: # pragma: no cover |
| | | subpath = subpath.replace('\\', '/') # windows |
| | | subpath = path[len(spec) :] |
| | | if WIN: # pragma: no cover |
| | | subpath = subpath.replace('\\', '/') # windows |
| | | if self.cache_busters: |
| | | subpath, kw = self._bust_asset_path( |
| | | request, spec, subpath, kw) |
| | | request, spec, subpath, kw |
| | | ) |
| | | if url is None: |
| | | kw['subpath'] = subpath |
| | | return request.route_url(route_name, **kw) |
| | |
| | | app_url, qs, anchor = parse_url_overrides(request, kw) |
| | | parsed = url_parse(url) |
| | | if not parsed.scheme: |
| | | url = urlparse.urlunparse(parsed._replace( |
| | | scheme=request.environ['wsgi.url_scheme'])) |
| | | url = urlparse.urlunparse( |
| | | parsed._replace( |
| | | scheme=request.environ['wsgi.url_scheme'] |
| | | ) |
| | | ) |
| | | subpath = url_quote(subpath) |
| | | result = urljoin(url, subpath) |
| | | return result + qs + anchor |
| | |
| | | # appending a slash here if the spec doesn't have one is |
| | | # required for proper prefix matching done in ``generate`` |
| | | # (``subpath = path[len(spec):]``). |
| | | if os.path.isabs(spec): # FBO windows |
| | | if os.path.isabs(spec): # FBO windows |
| | | sep = os.sep |
| | | else: |
| | | sep = '/' |
| | |
| | | cache_max_age = extra.pop('cache_max_age', None) |
| | | |
| | | # create a view |
| | | view = static_view(spec, cache_max_age=cache_max_age, |
| | | use_subpath=True) |
| | | view = static_view( |
| | | spec, cache_max_age=cache_max_age, use_subpath=True |
| | | ) |
| | | |
| | | # Mutate extra to allow factory, etc to be passed through here. |
| | | # Treat permission specially because we'd like to default to |
| | |
| | | |
| | | # register a route using the computed view, permission, and |
| | | # pattern, plus any extras passed to us via add_static_view |
| | | pattern = "%s*subpath" % name # name already ends with slash |
| | | pattern = "%s*subpath" % name # name already ends with slash |
| | | if config.route_prefix: |
| | | route_name = '__%s/%s' % (config.route_prefix, name) |
| | | else: |
| | |
| | | # url, spec, route_name |
| | | registrations.append((url, spec, route_name)) |
| | | |
| | | intr = config.introspectable('static views', |
| | | name, |
| | | 'static view for %r' % name, |
| | | 'static view') |
| | | intr = config.introspectable( |
| | | 'static views', name, 'static view for %r' % name, 'static view' |
| | | ) |
| | | intr['name'] = name |
| | | intr['spec'] = spec |
| | | |
| | |
| | | def add_cache_buster(self, config, spec, cachebust, explicit=False): |
| | | # ensure the spec always has a trailing slash as we only support |
| | | # adding cache busters to folders, not files |
| | | if os.path.isabs(spec): # FBO windows |
| | | if os.path.isabs(spec): # FBO windows |
| | | sep = os.sep |
| | | else: |
| | | sep = '/' |
| | |
| | | |
| | | cache_busters.insert(new_idx, (spec, cachebust, explicit)) |
| | | |
| | | intr = config.introspectable('cache busters', |
| | | spec, |
| | | 'cache buster for %r' % spec, |
| | | 'cache buster') |
| | | intr = config.introspectable( |
| | | 'cache busters', spec, 'cache buster for %r' % spec, 'cache buster' |
| | | ) |
| | | intr['cachebust'] = cachebust |
| | | intr['path'] = spec |
| | | intr['explicit'] = explicit |
| | |
| | | kw['pathspec'] = pathspec |
| | | kw['rawspec'] = rawspec |
| | | for spec_, cachebust, explicit in reversed(self.cache_busters): |
| | | if ( |
| | | (explicit and rawspec.startswith(spec_)) or |
| | | (not explicit and pathspec.startswith(spec_)) |
| | | if (explicit and rawspec.startswith(spec_)) or ( |
| | | not explicit and pathspec.startswith(spec_) |
| | | ): |
| | | subpath, kw = cachebust(request, subpath, kw) |
| | | break |