1import inspect 2import posixpath 3import operator 4import os 5import warnings 6 7from zope.interface import ( 8 Interface, 9 implementedBy, 10 implementer, 11 ) 12 13from zope.interface.interfaces import IInterface 14 15from pyramid.interfaces import ( 16 IException, 17 IExceptionViewClassifier, 18 IMultiView, 19 IPackageOverrides, 20 IRendererFactory, 21 IRequest, 22 IResponse, 23 IRouteRequest, 24 ISecuredView, 25 IStaticURLInfo, 26 IView, 27 IViewClassifier, 28 IViewDerivers, 29 IViewDeriverInfo, 30 IViewMapperFactory, 31 PHASE1_CONFIG, 32 ) 33 34from pyramid import renderers 35 36from pyramid.asset import resolve_asset_spec 37from pyramid.compat import ( 38 string_types, 39 urlparse, 40 url_quote, 41 WIN, 42 is_nonstr_iter, 43 ) 44 45from pyramid.decorator import reify 46 47from pyramid.exceptions import ( 48 ConfigurationError, 49 PredicateMismatch, 50 ) 51 52from pyramid.httpexceptions import ( 53 HTTPForbidden, 54 HTTPNotFound, 55 default_exceptionresponse_view, 56 ) 57 58from pyramid.registry import ( 59 predvalseq, 60 Deferred, 61 ) 62 63from pyramid.security import NO_PERMISSION_REQUIRED 64from pyramid.static import static_view 65 66from pyramid.url import parse_url_overrides 67 68from pyramid.view import AppendSlashNotFoundViewFactory 69 70import pyramid.util 71from pyramid.util import ( 72 viewdefaults, 73 action_method, 74 TopologicalSorter, 75 ) 76 77import pyramid.config.predicates 78import pyramid.viewderivers 79 80from pyramid.viewderivers import ( 81 INGRESS, 82 VIEW, 83 preserve_view_attrs, 84 view_description, 85 requestonly, 86 DefaultViewMapper, 87 wraps_view, 88) 89 90from pyramid.config.util import ( 91 DEFAULT_PHASH, 92 MAX_ORDER, 93 as_sorted_tuple, 94 ) 95 96urljoin = urlparse.urljoin 97url_parse = urlparse.urlparse 98 99DefaultViewMapper = DefaultViewMapper # bw-compat 100preserve_view_attrs = preserve_view_attrs # bw-compat 101requestonly = requestonly # bw-compat 102view_description = view_description # bw-compat 103 104@implementer(IMultiView) 105class MultiView(object): 106 107 def __init__(self, name): 108 self.name = name 109 self.media_views = {} 110 self.views = [] 111 self.accepts = [] 112 113 def __discriminator__(self, context, request): 114 # used by introspection systems like so: 115 # view = adapters.lookup(....) 116 # view.__discriminator__(context, request) -> view's discriminator 117 # so that superdynamic systems can feed the discriminator to 118 # the introspection system to get info about it 119 view = self.match(context, request) 120 return view.__discriminator__(context, request) 121 122 def add(self, view, order, accept=None, phash=None): 123 if phash is not None: 124 for i, (s, v, h) in enumerate(list(self.views)): 125 if phash == h: 126 self.views[i] = (order, view, phash) 127 return 128 129 if accept is None or '*' in accept: 130 self.views.append((order, view, phash)) 131 self.views.sort(key=operator.itemgetter(0)) 132 else: 133 subset = self.media_views.setdefault(accept, []) 134 for i, (s, v, h) in enumerate(list(subset)): 135 if phash == h: 136 subset[i] = (order, view, phash) 137 return 138 else: 139 subset.append((order, view, phash)) 140 subset.sort(key=operator.itemgetter(0)) 141 accepts = set(self.accepts) 142 accepts.add(accept) 143 self.accepts = list(accepts) # dedupe 144 145 def get_views(self, request): 146 if self.accepts and hasattr(request, 'accept'): 147 accepts = self.accepts[:] 148 views = [] 149 while accepts: 150 match = request.accept.best_match(accepts) 151 if match is None: 152 break 153 subset = self.media_views[match] 154 views.extend(subset) 155 accepts.remove(match) 156 views.extend(self.views) 157 return views 158 return self.views 159 160 def match(self, context, request): 161 for order, view, phash in self.get_views(request): 162 if not hasattr(view, '__predicated__'): 163 return view 164 if view.__predicated__(context, request): 165 return view 166 raise PredicateMismatch(self.name) 167 168 def __permitted__(self, context, request): 169 view = self.match(context, request) 170 if hasattr(view, '__permitted__'): 171 return view.__permitted__(context, request) 172 return True 173 174 def __call_permissive__(self, context, request): 175 view = self.match(context, request) 176 view = getattr(view, '__call_permissive__', view) 177 return view(context, request) 178 179 def __call__(self, context, request): 180 for order, view, phash in self.get_views(request): 181 try: 182 return view(context, request) 183 except PredicateMismatch: 184 continue 185 raise PredicateMismatch(self.name) 186 187class ViewsConfiguratorMixin(object): 188 @viewdefaults 189 @action_method 190 def add_view( 191 self, 192 view=None, 193 name="", 194 for_=None, 195 permission=None, 196 request_type=None, 197 route_name=None, 198 request_method=None, 199 request_param=None, 200 containment=None, 201 attr=None, 202 renderer=None, 203 wrapper=None, 204 xhr=None, 205 accept=None, 206 header=None, 207 path_info=None, 208 custom_predicates=(), 209 context=None, 210 decorator=None, 211 mapper=None, 212 http_cache=None, 213 match_param=None, 214 check_csrf=None, 215 require_csrf=None, 216 **view_options): 217 """ Add a :term:`view configuration` to the current 218 configuration state. Arguments to ``add_view`` are broken 219 down below into *predicate* arguments and *non-predicate* 220 arguments. Predicate arguments narrow the circumstances in 221 which the view callable will be invoked when a request is 222 presented to :app:`Pyramid`; non-predicate arguments are 223 informational. 224 225 Non-Predicate Arguments 226 227 view 228 229 A :term:`view callable` or a :term:`dotted Python name` 230 which refers to a view callable. This argument is required 231 unless a ``renderer`` argument also exists. If a 232 ``renderer`` argument is passed, and a ``view`` argument is 233 not provided, the view callable defaults to a callable that 234 returns an empty dictionary (see 235 :ref:`views_which_use_a_renderer`). 236 237 permission 238 239 A :term:`permission` that the user must possess in order to invoke 240 the :term:`view callable`. See :ref:`view_security_section` for 241 more information about view security and permissions. This is 242 often a string like ``view`` or ``edit``. 243 244 If ``permission`` is omitted, a *default* permission may be used 245 for this view registration if one was named as the 246 :class:`pyramid.config.Configurator` constructor's 247 ``default_permission`` argument, or if 248 :meth:`pyramid.config.Configurator.set_default_permission` was used 249 prior to this view registration. Pass the value 250 :data:`pyramid.security.NO_PERMISSION_REQUIRED` as the permission 251 argument to explicitly indicate that the view should always be 252 executable by entirely anonymous users, regardless of the default 253 permission, bypassing any :term:`authorization policy` that may be 254 in effect. 255 256 attr 257 258 This knob is most useful when the view definition is a class. 259 260 The view machinery defaults to using the ``__call__`` method 261 of the :term:`view callable` (or the function itself, if the 262 view callable is a function) to obtain a response. The 263 ``attr`` value allows you to vary the method attribute used 264 to obtain the response. For example, if your view was a 265 class, and the class has a method named ``index`` and you 266 wanted to use this method instead of the class' ``__call__`` 267 method to return the response, you'd say ``attr="index"`` in the 268 view configuration for the view. 269 270 renderer 271 272 This is either a single string term (e.g. ``json``) or a 273 string implying a path or :term:`asset specification` 274 (e.g. ``templates/views.pt``) naming a :term:`renderer` 275 implementation. If the ``renderer`` value does not contain 276 a dot ``.``, the specified string will be used to look up a 277 renderer implementation, and that renderer implementation 278 will be used to construct a response from the view return 279 value. If the ``renderer`` value contains a dot (``.``), 280 the specified term will be treated as a path, and the 281 filename extension of the last element in the path will be 282 used to look up the renderer implementation, which will be 283 passed the full path. The renderer implementation will be 284 used to construct a :term:`response` from the view return 285 value. 286 287 Note that if the view itself returns a :term:`response` (see 288 :ref:`the_response`), the specified renderer implementation 289 is never called. 290 291 When the renderer is a path, although a path is usually just 292 a simple relative pathname (e.g. ``templates/foo.pt``, 293 implying that a template named "foo.pt" is in the 294 "templates" directory relative to the directory of the 295 current :term:`package` of the Configurator), a path can be 296 absolute, starting with a slash on UNIX or a drive letter 297 prefix on Windows. The path can alternately be a 298 :term:`asset specification` in the form 299 ``some.dotted.package_name:relative/path``, making it 300 possible to address template assets which live in a 301 separate package. 302 303 The ``renderer`` attribute is optional. If it is not 304 defined, the "null" renderer is assumed (no rendering is 305 performed and the value is passed back to the upstream 306 :app:`Pyramid` machinery unmodified). 307 308 http_cache 309 310 .. versionadded:: 1.1 311 312 When you supply an ``http_cache`` value to a view configuration, 313 the ``Expires`` and ``Cache-Control`` headers of a response 314 generated by the associated view callable are modified. The value 315 for ``http_cache`` may be one of the following: 316 317 - A nonzero integer. If it's a nonzero integer, it's treated as a 318 number of seconds. This number of seconds will be used to 319 compute the ``Expires`` header and the ``Cache-Control: 320 max-age`` parameter of responses to requests which call this view. 321 For example: ``http_cache=3600`` instructs the requesting browser 322 to 'cache this response for an hour, please'. 323 324 - A ``datetime.timedelta`` instance. If it's a 325 ``datetime.timedelta`` instance, it will be converted into a 326 number of seconds, and that number of seconds will be used to 327 compute the ``Expires`` header and the ``Cache-Control: 328 max-age`` parameter of responses to requests which call this view. 329 For example: ``http_cache=datetime.timedelta(days=1)`` instructs 330 the requesting browser to 'cache this response for a day, please'. 331 332 - Zero (``0``). If the value is zero, the ``Cache-Control`` and 333 ``Expires`` headers present in all responses from this view will 334 be composed such that client browser cache (and any intermediate 335 caches) are instructed to never cache the response. 336 337 - A two-tuple. If it's a two tuple (e.g. ``http_cache=(1, 338 {'public':True})``), the first value in the tuple may be a 339 nonzero integer or a ``datetime.timedelta`` instance; in either 340 case this value will be used as the number of seconds to cache 341 the response. The second value in the tuple must be a 342 dictionary. The values present in the dictionary will be used as 343 input to the ``Cache-Control`` response header. For example: 344 ``http_cache=(3600, {'public':True})`` means 'cache for an hour, 345 and add ``public`` to the Cache-Control header of the response'. 346 All keys and values supported by the 347 ``webob.cachecontrol.CacheControl`` interface may be added to the 348 dictionary. Supplying ``{'public':True}`` is equivalent to 349 calling ``response.cache_control.public = True``. 350 351 Providing a non-tuple value as ``http_cache`` is equivalent to 352 calling ``response.cache_expires(value)`` within your view's body. 353 354 Providing a two-tuple value as ``http_cache`` is equivalent to 355 calling ``response.cache_expires(value[0], **value[1])`` within your 356 view's body. 357 358 If you wish to avoid influencing, the ``Expires`` header, and 359 instead wish to only influence ``Cache-Control`` headers, pass a 360 tuple as ``http_cache`` with the first element of ``None``, e.g.: 361 ``(None, {'public':True})``. 362 363 If you wish to prevent a view that uses ``http_cache`` in its 364 configuration from having its caching response headers changed by 365 this machinery, set ``response.cache_control.prevent_auto = True`` 366 before returning the response from the view. This effectively 367 disables any HTTP caching done by ``http_cache`` for that response. 368 369 require_csrf 370 371 .. versionadded:: 1.7 372 373 A boolean option or ``None``. Default: ``None``. 374 375 If this option is set to ``True`` then CSRF checks will be enabled 376 for requests to this view. The required token or header default to 377 ``csrf_token`` and ``X-CSRF-Token``, respectively. 378 379 CSRF checks only affect "unsafe" methods as defined by RFC2616. By 380 default, these methods are anything except 381 ``GET``, ``HEAD``, ``OPTIONS``, and ``TRACE``. 382 383 The defaults here may be overridden by 384 :meth:`pyramid.config.Configurator.set_default_csrf_options`. 385 386 This feature requires a configured :term:`session factory`. 387 388 If this option is set to ``False`` then CSRF checks will be disabled 389 regardless of the default ``require_csrf`` setting passed 390 to ``set_default_csrf_options``. 391 392 See :ref:`auto_csrf_checking` for more information. 393 394 wrapper 395 396 The :term:`view name` of a different :term:`view 397 configuration` which will receive the response body of this 398 view as the ``request.wrapped_body`` attribute of its own 399 :term:`request`, and the :term:`response` returned by this 400 view as the ``request.wrapped_response`` attribute of its 401 own request. Using a wrapper makes it possible to "chain" 402 views together to form a composite response. The response 403 of the outermost wrapper view will be returned to the user. 404 The wrapper view will be found as any view is found: see 405 :ref:`view_lookup`. The "best" wrapper view will be found 406 based on the lookup ordering: "under the hood" this wrapper 407 view is looked up via 408 ``pyramid.view.render_view_to_response(context, request, 409 'wrapper_viewname')``. The context and request of a wrapper 410 view is the same context and request of the inner view. If 411 this attribute is unspecified, no view wrapping is done. 412 413 decorator 414 415 A :term:`dotted Python name` to function (or the function itself, 416 or an iterable of the aforementioned) which will be used to 417 decorate the registered :term:`view callable`. The decorator 418 function(s) will be called with the view callable as a single 419 argument. The view callable it is passed will accept 420 ``(context, request)``. The decorator(s) must return a 421 replacement view callable which also accepts ``(context, 422 request)``. 423 424 If decorator is an iterable, the callables will be combined and 425 used in the order provided as a decorator. 426 For example:: 427 428 @view_config(..., 429 decorator=(decorator2, 430 decorator1)) 431 def myview(request): 432 .... 433 434 Is similar to doing:: 435 436 @view_config(...) 437 @decorator2 438 @decorator1 439 def myview(request): 440 ... 441 442 Except with the existing benefits of ``decorator=`` (having a common 443 decorator syntax for all view calling conventions and not having to 444 think about preserving function attributes such as ``__name__`` and 445 ``__module__`` within decorator logic). 446 447 All view callables in the decorator chain must return a response 448 object implementing :class:`pyramid.interfaces.IResponse` or raise 449 an exception: 450 451 .. code-block:: python 452 453 def log_timer(wrapped): 454 def wrapper(context, request): 455 start = time.time() 456 response = wrapped(context, request) 457 duration = time.time() - start 458 response.headers['X-View-Time'] = '%.3f' % (duration,) 459 log.info('view took %.3f seconds', duration) 460 return response 461 return wrapper 462 463 .. versionchanged:: 1.4a4 464 Passing an iterable. 465 466 mapper 467 468 A Python object or :term:`dotted Python name` which refers to a 469 :term:`view mapper`, or ``None``. By default it is ``None``, which 470 indicates that the view should use the default view mapper. This 471 plug-point is useful for Pyramid extension developers, but it's not 472 very useful for 'civilians' who are just developing stock Pyramid 473 applications. Pay no attention to the man behind the curtain. 474 475 accept 476 477 This value represents a match query for one or more mimetypes in the 478 ``Accept`` HTTP request header. If this value is specified, it must 479 be in one of the following forms: a mimetype match token in the form 480 ``text/plain``, a wildcard mimetype match token in the form 481 ``text/*`` or a match-all wildcard mimetype match token in the form 482 ``*/*``. If any of the forms matches the ``Accept`` header of the 483 request, or if the ``Accept`` header isn't set at all in the request, 484 this will match the current view. If this does not match the 485 ``Accept`` header of the request, view matching continues. 486 487 Predicate Arguments 488 489 name 490 491 The :term:`view name`. Read :ref:`traversal_chapter` to 492 understand the concept of a view name. 493 494 context 495 496 An object or a :term:`dotted Python name` referring to an 497 interface or class object that the :term:`context` must be 498 an instance of, *or* the :term:`interface` that the 499 :term:`context` must provide in order for this view to be 500 found and called. This predicate is true when the 501 :term:`context` is an instance of the represented class or 502 if the :term:`context` provides the represented interface; 503 it is otherwise false. This argument may also be provided 504 to ``add_view`` as ``for_`` (an older, still-supported 505 spelling). 506 507 route_name 508 509 This value must match the ``name`` of a :term:`route 510 configuration` declaration (see :ref:`urldispatch_chapter`) 511 that must match before this view will be called. 512 513 request_type 514 515 This value should be an :term:`interface` that the 516 :term:`request` must provide in order for this view to be 517 found and called. This value exists only for backwards 518 compatibility purposes. 519 520 request_method 521 522 This value can be either a string (such as ``"GET"``, ``"POST"``, 523 ``"PUT"``, ``"DELETE"``, ``"HEAD"`` or ``"OPTIONS"``) representing 524 an HTTP ``REQUEST_METHOD``, or a tuple containing one or more of 525 these strings. A view declaration with this argument ensures that 526 the view will only be called when the ``method`` attribute of the 527 request (aka the ``REQUEST_METHOD`` of the WSGI environment) matches 528 a supplied value. Note that use of ``GET`` also implies that the 529 view will respond to ``HEAD`` as of Pyramid 1.4. 530 531 .. versionchanged:: 1.2 532 The ability to pass a tuple of items as ``request_method``. 533 Previous versions allowed only a string. 534 535 request_param 536 537 This value can be any string or any sequence of strings. A view 538 declaration with this argument ensures that the view will only be 539 called when the :term:`request` has a key in the ``request.params`` 540 dictionary (an HTTP ``GET`` or ``POST`` variable) that has a 541 name which matches the supplied value (if the value is a string) 542 or values (if the value is a tuple). If any value 543 supplied has a ``=`` sign in it, 544 e.g. ``request_param="foo=123"``, then the key (``foo``) 545 must both exist in the ``request.params`` dictionary, *and* 546 the value must match the right hand side of the expression 547 (``123``) for the view to "match" the current request. 548 549 match_param 550 551 .. versionadded:: 1.2 552 553 This value can be a string of the format "key=value" or a tuple 554 containing one or more of these strings. 555 556 A view declaration with this argument ensures that the view will 557 only be called when the :term:`request` has key/value pairs in its 558 :term:`matchdict` that equal those supplied in the predicate. 559 e.g. ``match_param="action=edit"`` would require the ``action`` 560 parameter in the :term:`matchdict` match the right hand side of 561 the expression (``edit``) for the view to "match" the current 562 request. 563 564 If the ``match_param`` is a tuple, every key/value pair must match 565 for the predicate to pass. 566 567 containment 568 569 This value should be a Python class or :term:`interface` (or a 570 :term:`dotted Python name`) that an object in the 571 :term:`lineage` of the context must provide in order for this view 572 to be found and called. The nodes in your object graph must be 573 "location-aware" to use this feature. See 574 :ref:`location_aware` for more information about 575 location-awareness. 576 577 xhr 578 579 This value should be either ``True`` or ``False``. If this 580 value is specified and is ``True``, the :term:`request` 581 must possess an ``HTTP_X_REQUESTED_WITH`` (aka 582 ``X-Requested-With``) header that has the value 583 ``XMLHttpRequest`` for this view to be found and called. 584 This is useful for detecting AJAX requests issued from 585 jQuery, Prototype and other Javascript libraries. 586 587 header 588 589 This value represents an HTTP header name or a header 590 name/value pair. If the value contains a ``:`` (colon), it 591 will be considered a name/value pair 592 (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). The 593 value portion should be a regular expression. If the value 594 does not contain a colon, the entire value will be 595 considered to be the header name 596 (e.g. ``If-Modified-Since``). If the value evaluates to a 597 header name only without a value, the header specified by 598 the name must be present in the request for this predicate 599 to be true. If the value evaluates to a header name/value 600 pair, the header specified by the name must be present in 601 the request *and* the regular expression specified as the 602 value must match the header value. Whether or not the value 603 represents a header name or a header name/value pair, the 604 case of the header name is not significant. 605 606 path_info 607 608 This value represents a regular expression pattern that will 609 be tested against the ``PATH_INFO`` WSGI environment 610 variable. If the regex matches, this predicate will be 611 ``True``. 612 613 check_csrf 614 615 .. deprecated:: 1.7 616 Use the ``require_csrf`` option or see :ref:`auto_csrf_checking` 617 instead to have :class:`pyramid.exceptions.BadCSRFToken` 618 exceptions raised. 619 620 If specified, this value should be one of ``None``, ``True``, 621 ``False``, or a string representing the 'check name'. If the value 622 is ``True`` or a string, CSRF checking will be performed. If the 623 value is ``False`` or ``None``, CSRF checking will not be performed. 624 625 If the value provided is a string, that string will be used as the 626 'check name'. If the value provided is ``True``, ``csrf_token`` will 627 be used as the check name. 628 629 If CSRF checking is performed, the checked value will be the value 630 of ``request.params[check_name]``. This value will be compared 631 against the value of ``request.session.get_csrf_token()``, and the 632 check will pass if these two values are the same. If the check 633 passes, the associated view will be permitted to execute. If the 634 check fails, the associated view will not be permitted to execute. 635 636 Note that using this feature requires a :term:`session factory` to 637 have been configured. 638 639 .. versionadded:: 1.4a2 640 641 physical_path 642 643 If specified, this value should be a string or a tuple representing 644 the :term:`physical path` of the context found via traversal for this 645 predicate to match as true. For example: ``physical_path='/'`` or 646 ``physical_path='/a/b/c'`` or ``physical_path=('', 'a', 'b', 'c')``. 647 This is not a path prefix match or a regex, it's a whole-path match. 648 It's useful when you want to always potentially show a view when some 649 object is traversed to, but you can't be sure about what kind of 650 object it will be, so you can't use the ``context`` predicate. The 651 individual path elements inbetween slash characters or in tuple 652 elements should be the Unicode representation of the name of the 653 resource and should not be encoded in any way. 654 655 .. versionadded:: 1.4a3 656 657 effective_principals 658 659 If specified, this value should be a :term:`principal` identifier or 660 a sequence of principal identifiers. If the 661 :attr:`pyramid.request.Request.effective_principals` property 662 indicates that every principal named in the argument list is present 663 in the current request, this predicate will return True; otherwise it 664 will return False. For example: 665 ``effective_principals=pyramid.security.Authenticated`` or 666 ``effective_principals=('fred', 'group:admins')``. 667 668 .. versionadded:: 1.4a4 669 670 custom_predicates 671 672 .. deprecated:: 1.5 673 This value should be a sequence of references to custom 674 predicate callables. Use custom predicates when no set of 675 predefined predicates do what you need. Custom predicates 676 can be combined with predefined predicates as necessary. 677 Each custom predicate callable should accept two arguments: 678 ``context`` and ``request`` and should return either 679 ``True`` or ``False`` after doing arbitrary evaluation of 680 the context and/or the request. The ``predicates`` argument 681 to this method and the ability to register third-party view 682 predicates via 683 :meth:`pyramid.config.Configurator.add_view_predicate` 684 obsoletes this argument, but it is kept around for backwards 685 compatibility. 686 687 view_options: 688 689 Pass a key/value pair here to use a third-party predicate or set a 690 value for a view deriver. See 691 :meth:`pyramid.config.Configurator.add_view_predicate` and 692 :meth:`pyramid.config.Configurator.add_view_deriver`. See 693 :ref:`view_and_route_predicates` for more information about 694 third-party predicates and :ref:`view_derivers` for information 695 about view derivers. 696 697 .. versionadded: 1.4a1 698 699 .. versionchanged: 1.7 700 701 Support setting view deriver options. Previously, only custom 702 view predicate values could be supplied. 703 704 """ 705 if custom_predicates: 706 warnings.warn( 707 ('The "custom_predicates" argument to Configurator.add_view ' 708 'is deprecated as of Pyramid 1.5. Use ' 709 '"config.add_view_predicate" and use the registered ' 710 'view predicate as a predicate argument to add_view instead. ' 711 'See "Adding A Third Party View, Route, or Subscriber ' 712 'Predicate" in the "Hooks" chapter of the documentation ' 713 'for more information.'), 714 DeprecationWarning, 715 stacklevel=4, 716 ) 717 718 if check_csrf is not None: 719 warnings.warn( 720 ('The "check_csrf" argument to Configurator.add_view is ' 721 'deprecated as of Pyramid 1.7. Use the "require_csrf" option ' 722 'instead or see "Checking CSRF Tokens Automatically" in the ' 723 '"Sessions" chapter of the documentation for more ' 724 'information.'), 725 DeprecationWarning, 726 stacklevel=4, 727 ) 728 729 view = self.maybe_dotted(view) 730 context = self.maybe_dotted(context) 731 for_ = self.maybe_dotted(for_) 732 containment = self.maybe_dotted(containment) 733 mapper = self.maybe_dotted(mapper) 734 735 def combine(*decorators): 736 def decorated(view_callable): 737 # reversed() allows a more natural ordering in the api 738 for decorator in reversed(decorators): 739 view_callable = decorator(view_callable) 740 return view_callable 741 return decorated 742 743 if is_nonstr_iter(decorator): 744 decorator = combine(*map(self.maybe_dotted, decorator)) 745 else: 746 decorator = self.maybe_dotted(decorator) 747 748 if not view: 749 if renderer: 750 def view(context, request): 751 return {} 752 else: 753 raise ConfigurationError('"view" was not specified and ' 754 'no "renderer" specified') 755 756 if request_type is not None: 757 request_type = self.maybe_dotted(request_type) 758 if not IInterface.providedBy(request_type): 759 raise ConfigurationError( 760 'request_type must be an interface, not %s' % request_type) 761 762 if context is None: 763 context = for_ 764 765 r_context = context 766 if r_context is None: 767 r_context = Interface 768 if not IInterface.providedBy(r_context): 769 r_context = implementedBy(r_context) 770 771 if isinstance(renderer, string_types): 772 renderer = renderers.RendererHelper( 773 name=renderer, package=self.package, 774 registry=self.registry) 775 776 if accept is not None: 777 accept = accept.lower() 778 779 introspectables = [] 780 ovals = view_options.copy() 781 ovals.update(dict( 782 xhr=xhr, 783 request_method=request_method, 784 path_info=path_info, 785 request_param=request_param, 786 header=header, 787 accept=accept, 788 containment=containment, 789 request_type=request_type, 790 match_param=match_param, 791 check_csrf=check_csrf, 792 custom=predvalseq(custom_predicates), 793 )) 794 795 def discrim_func(): 796 # We need to defer the discriminator until we know what the phash 797 # is. It can't be computed any sooner because thirdparty 798 # predicates/view derivers may not yet exist when add_view is 799 # called. 800 valid_predicates = predlist.names() 801 pvals = {} 802 dvals = {} 803 804 for (k, v) in ovals.items(): 805 if k in valid_predicates: 806 pvals[k] = v 807 else: 808 dvals[k] = v 809 810 self._check_view_options(**dvals) 811 812 order, preds, phash = predlist.make(self, **pvals) 813 814 view_intr.update({ 815 'phash': phash, 816 'order': order, 817 'predicates': preds, 818 }) 819 return ('view', context, name, route_name, phash) 820 821 discriminator = Deferred(discrim_func) 822 823 if inspect.isclass(view) and attr: 824 view_desc = 'method %r of %s' % ( 825 attr, self.object_description(view)) 826 else: 827 view_desc = self.object_description(view) 828 829 tmpl_intr = None 830 831 view_intr = self.introspectable('views', 832 discriminator, 833 view_desc, 834 'view') 835 view_intr.update(dict( 836 name=name, 837 context=context, 838 containment=containment, 839 request_param=request_param, 840 request_methods=request_method, 841 route_name=route_name, 842 attr=attr, 843 xhr=xhr, 844 accept=accept, 845 header=header, 846 path_info=path_info, 847 match_param=match_param, 848 check_csrf=check_csrf, 849 http_cache=http_cache, 850 require_csrf=require_csrf, 851 callable=view, 852 mapper=mapper, 853 decorator=decorator, 854 )) 855 view_intr.update(view_options) 856 introspectables.append(view_intr) 857 predlist = self.get_predlist('view') 858 859 def register(permission=permission, renderer=renderer): 860 request_iface = IRequest 861 if route_name is not None: 862 request_iface = self.registry.queryUtility(IRouteRequest, 863 name=route_name) 864 if request_iface is None: 865 # route configuration should have already happened in 866 # phase 2 867 raise ConfigurationError( 868 'No route named %s found for view registration' % 869 route_name) 870 871 if renderer is None: 872 # use default renderer if one exists (reg'd in phase 1) 873 if self.registry.queryUtility(IRendererFactory) is not None: 874 renderer = renderers.RendererHelper( 875 name=None, 876 package=self.package, 877 registry=self.registry 878 ) 879 880 # added by discrim_func above during conflict resolving 881 preds = view_intr['predicates'] 882 order = view_intr['order'] 883 phash = view_intr['phash'] 884 885 # __no_permission_required__ handled by _secure_view 886 derived_view = self._derive_view( 887 view, 888 permission=permission, 889 predicates=preds, 890 attr=attr, 891 context=context, 892 renderer=renderer, 893 wrapper_viewname=wrapper, 894 viewname=name, 895 accept=accept, 896 order=order, 897 phash=phash, 898 decorator=decorator, 899 mapper=mapper, 900 http_cache=http_cache, 901 require_csrf=require_csrf, 902 extra_options=ovals, 903 ) 904 derived_view.__discriminator__ = lambda *arg: discriminator 905 # __discriminator__ is used by superdynamic systems 906 # that require it for introspection after manual view lookup; 907 # see also MultiView.__discriminator__ 908 view_intr['derived_callable'] = derived_view 909 910 registered = self.registry.adapters.registered 911 912 # A multiviews is a set of views which are registered for 913 # exactly the same context type/request type/name triad. Each 914 # consituent view in a multiview differs only by the 915 # predicates which it possesses. 916 917 # To find a previously registered view for a context 918 # type/request type/name triad, we need to use the 919 # ``registered`` method of the adapter registry rather than 920 # ``lookup``. ``registered`` ignores interface inheritance 921 # for the required and provided arguments, returning only a 922 # view registered previously with the *exact* triad we pass 923 # in. 924 925 # We need to do this three times, because we use three 926 # different interfaces as the ``provided`` interface while 927 # doing registrations, and ``registered`` performs exact 928 # matches on all the arguments it receives. 929 930 old_view = None 931 932 for view_type in (IView, ISecuredView, IMultiView): 933 old_view = registered((IViewClassifier, request_iface, 934 r_context), view_type, name) 935 if old_view is not None: 936 break 937 938 isexc = isexception(context) 939 940 def regclosure(): 941 if hasattr(derived_view, '__call_permissive__'): 942 view_iface = ISecuredView 943 else: 944 view_iface = IView 945 self.registry.registerAdapter( 946 derived_view, 947 (IViewClassifier, request_iface, context), view_iface, name 948 ) 949 if isexc: 950 self.registry.registerAdapter( 951 derived_view, 952 (IExceptionViewClassifier, request_iface, context), 953 view_iface, name) 954 955 is_multiview = IMultiView.providedBy(old_view) 956 old_phash = getattr(old_view, '__phash__', DEFAULT_PHASH) 957 958 if old_view is None: 959 # - No component was yet registered for any of our I*View 960 # interfaces exactly; this is the first view for this 961 # triad. 962 regclosure() 963 964 elif (not is_multiview) and (old_phash == phash): 965 # - A single view component was previously registered with 966 # the same predicate hash as this view; this registration 967 # is therefore an override. 968 regclosure() 969 970 else: 971 # - A view or multiview was already registered for this 972 # triad, and the new view is not an override. 973 974 # XXX we could try to be more efficient here and register 975 # a non-secured view for a multiview if none of the 976 # multiview's consituent views have a permission 977 # associated with them, but this code is getting pretty 978 # rough already 979 if is_multiview: 980 multiview = old_view 981 else: 982 multiview = MultiView(name) 983 old_accept = getattr(old_view, '__accept__', None) 984 old_order = getattr(old_view, '__order__', MAX_ORDER) 985 multiview.add(old_view, old_order, old_accept, old_phash) 986 multiview.add(derived_view, order, accept, phash) 987 for view_type in (IView, ISecuredView): 988 # unregister any existing views 989 self.registry.adapters.unregister( 990 (IViewClassifier, request_iface, r_context), 991 view_type, name=name) 992 if isexc: 993 self.registry.adapters.unregister( 994 (IExceptionViewClassifier, request_iface, 995 r_context), view_type, name=name) 996 self.registry.registerAdapter( 997 multiview, 998 (IViewClassifier, request_iface, context), 999 IMultiView, name=name) 1000 if isexc: 1001 self.registry.registerAdapter( 1002 multiview, 1003 (IExceptionViewClassifier, request_iface, context), 1004 IMultiView, name=name) 1005 1006 self.registry._clear_view_lookup_cache() 1007 renderer_type = getattr(renderer, 'type', None) # gard against None 1008 intrspc = self.introspector 1009 if ( 1010 renderer_type is not None and 1011 tmpl_intr is not None and 1012 intrspc is not None and 1013 intrspc.get('renderer factories', renderer_type) is not None 1014 ): 1015 # allow failure of registered template factories to be deferred 1016 # until view execution, like other bad renderer factories; if 1017 # we tried to relate this to an existing renderer factory 1018 # without checking if it the factory actually existed, we'd end 1019 # up with a KeyError at startup time, which is inconsistent 1020 # with how other bad renderer registrations behave (they throw 1021 # a ValueError at view execution time) 1022 tmpl_intr.relate('renderer factories', renderer.type) 1023 1024 if mapper: 1025 mapper_intr = self.introspectable( 1026 'view mappers', 1027 discriminator, 1028 'view mapper for %s' % view_desc, 1029 'view mapper' 1030 ) 1031 mapper_intr['mapper'] = mapper 1032 mapper_intr.relate('views', discriminator) 1033 introspectables.append(mapper_intr) 1034 if route_name: 1035 view_intr.relate('routes', route_name) # see add_route 1036 if renderer is not None and renderer.name and '.' in renderer.name: 1037 # the renderer is a template 1038 tmpl_intr = self.introspectable( 1039 'templates', 1040 discriminator, 1041 renderer.name, 1042 'template' 1043 ) 1044 tmpl_intr.relate('views', discriminator) 1045 tmpl_intr['name'] = renderer.name 1046 tmpl_intr['type'] = renderer.type 1047 tmpl_intr['renderer'] = renderer 1048 introspectables.append(tmpl_intr) 1049 if permission is not None: 1050 # if a permission exists, register a permission introspectable 1051 perm_intr = self.introspectable( 1052 'permissions', 1053 permission, 1054 permission, 1055 'permission' 1056 ) 1057 perm_intr['value'] = permission 1058 perm_intr.relate('views', discriminator) 1059 introspectables.append(perm_intr) 1060 self.action(discriminator, register, introspectables=introspectables) 1061 1062 def _check_view_options(self, **kw): 1063 # we only need to validate deriver options because the predicates 1064 # were checked by the predlist 1065 derivers = self.registry.getUtility(IViewDerivers) 1066 for deriver in derivers.values(): 1067 for opt in getattr(deriver, 'options', []): 1068 kw.pop(opt, None) 1069 if kw: 1070 raise ConfigurationError('Unknown view options: %s' % (kw,)) 1071 1072 def _apply_view_derivers(self, info): 1073 d = pyramid.viewderivers 1074 1075 # These derivers are not really derivers and so have fixed order 1076 outer_derivers = [('attr_wrapped_view', d.attr_wrapped_view), 1077 ('predicated_view', d.predicated_view)] 1078 1079 view = info.original_view 1080 derivers = self.registry.getUtility(IViewDerivers) 1081 for name, deriver in reversed(outer_derivers + derivers.sorted()): 1082 view = wraps_view(deriver)(view, info) 1083 return view 1084 1085 @action_method 1086 def add_view_predicate(self, name, factory, weighs_more_than=None, 1087 weighs_less_than=None): 1088 """ 1089 .. versionadded:: 1.4 1090 1091 Adds a view predicate factory. The associated view predicate can 1092 later be named as a keyword argument to 1093 :meth:`pyramid.config.Configurator.add_view` in the 1094 ``predicates`` anonyous keyword argument dictionary. 1095 1096 ``name`` should be the name of the predicate. It must be a valid 1097 Python identifier (it will be used as a keyword argument to 1098 ``add_view`` by others). 1099 1100 ``factory`` should be a :term:`predicate factory` or :term:`dotted 1101 Python name` which refers to a predicate factory. 1102 1103 See :ref:`view_and_route_predicates` for more information. 1104 """ 1105 self._add_predicate( 1106 'view', 1107 name, 1108 factory, 1109 weighs_more_than=weighs_more_than, 1110 weighs_less_than=weighs_less_than 1111 ) 1112 1113 def add_default_view_predicates(self): 1114 p = pyramid.config.predicates 1115 for (name, factory) in ( 1116 ('xhr', p.XHRPredicate), 1117 ('request_method', p.RequestMethodPredicate), 1118 ('path_info', p.PathInfoPredicate), 1119 ('request_param', p.RequestParamPredicate), 1120 ('header', p.HeaderPredicate), 1121 ('accept', p.AcceptPredicate), 1122 ('containment', p.ContainmentPredicate), 1123 ('request_type', p.RequestTypePredicate), 1124 ('match_param', p.MatchParamPredicate), 1125 ('check_csrf', p.CheckCSRFTokenPredicate), 1126 ('physical_path', p.PhysicalPathPredicate), 1127 ('effective_principals', p.EffectivePrincipalsPredicate), 1128 ('custom', p.CustomPredicate), 1129 ): 1130 self.add_view_predicate(name, factory) 1131 1132 @action_method 1133 def add_view_deriver(self, deriver, name=None, under=None, over=None): 1134 """ 1135 .. versionadded:: 1.7 1136 1137 Add a :term:`view deriver` to the view pipeline. View derivers are 1138 a feature used by extension authors to wrap views in custom code 1139 controllable by view-specific options. 1140 1141 ``deriver`` should be a callable conforming to the 1142 :class:`pyramid.interfaces.IViewDeriver` interface. 1143 1144 ``name`` should be the name of the view deriver. There are no 1145 restrictions on the name of a view deriver. If left unspecified, the 1146 name will be constructed from the name of the ``deriver``. 1147 1148 The ``under`` and ``over`` options can be used to control the ordering 1149 of view derivers by providing hints about where in the view pipeline 1150 the deriver is used. Each option may be a string or a list of strings. 1151 At least one view deriver in each, the over and under directions, must 1152 exist to fully satisfy the constraints. 1153 1154 ``under`` means closer to the user-defined :term:`view callable`, 1155 and ``over`` means closer to view pipeline ingress. 1156 1157 The default value for ``over`` is ``rendered_view`` and ``under`` is 1158 ``decorated_view``. This places the deriver somewhere between the two 1159 in the view pipeline. If the deriver should be placed elsewhere in the 1160 pipeline, such as above ``decorated_view``, then you MUST also specify 1161 ``under`` to something earlier in the order, or a 1162 ``CyclicDependencyError`` will be raised when trying to sort the 1163 derivers. 1164 1165 See :ref:`view_derivers` for more information. 1166 1167 """ 1168 deriver = self.maybe_dotted(deriver) 1169 1170 if name is None: 1171 name = deriver.__name__ 1172 1173 if name in (INGRESS, VIEW): 1174 raise ConfigurationError('%s is a reserved view deriver name' 1175 % name) 1176 1177 if under is None: 1178 under = 'decorated_view' 1179 1180 if over is None: 1181 over = 'rendered_view' 1182 1183 over = as_sorted_tuple(over) 1184 under = as_sorted_tuple(under) 1185 1186 if INGRESS in over: 1187 raise ConfigurationError('%s cannot be over INGRESS' % name) 1188 1189 # ensure everything is always over mapped_view 1190 if VIEW in over and name != 'mapped_view': 1191 over = as_sorted_tuple(over + ('mapped_view',)) 1192 1193 if VIEW in under: 1194 raise ConfigurationError('%s cannot be under VIEW' % name) 1195 if 'mapped_view' in under: 1196 raise ConfigurationError('%s cannot be under "mapped_view"' % name) 1197 1198 discriminator = ('view deriver', name) 1199 intr = self.introspectable( 1200 'view derivers', 1201 name, 1202 name, 1203 'view deriver') 1204 intr['name'] = name 1205 intr['deriver'] = deriver 1206 intr['under'] = under 1207 intr['over'] = over 1208 def register(): 1209 derivers = self.registry.queryUtility(IViewDerivers) 1210 if derivers is None: 1211 derivers = TopologicalSorter( 1212 default_before=None, 1213 default_after=INGRESS, 1214 first=INGRESS, 1215 last=VIEW, 1216 ) 1217 self.registry.registerUtility(derivers, IViewDerivers) 1218 derivers.add(name, deriver, before=over, after=under) 1219 self.action(discriminator, register, introspectables=(intr,), 1220 order=PHASE1_CONFIG) # must be registered before add_view 1221 1222 def add_default_view_derivers(self): 1223 d = pyramid.viewderivers 1224 derivers = [ 1225 ('secured_view', d.secured_view), 1226 ('owrapped_view', d.owrapped_view), 1227 ('http_cached_view', d.http_cached_view), 1228 ('decorated_view', d.decorated_view), 1229 ('rendered_view', d.rendered_view), 1230 ('mapped_view', d.mapped_view), 1231 ] 1232 last = INGRESS 1233 for name, deriver in derivers: 1234 self.add_view_deriver( 1235 deriver, 1236 name=name, 1237 under=last, 1238 over=VIEW, 1239 ) 1240 last = name 1241 1242 # leave the csrf_view loosely coupled to the rest of the pipeline 1243 # by ensuring nothing in the default pipeline depends on the order 1244 # of the csrf_view 1245 self.add_view_deriver( 1246 d.csrf_view, 1247 'csrf_view', 1248 under='secured_view', 1249 over='owrapped_view', 1250 ) 1251 1252 def derive_view(self, view, attr=None, renderer=None): 1253 """ 1254 Create a :term:`view callable` using the function, instance, 1255 or class (or :term:`dotted Python name` referring to the same) 1256 provided as ``view`` object. 1257 1258 .. warning:: 1259 1260 This method is typically only used by :app:`Pyramid` framework 1261 extension authors, not by :app:`Pyramid` application developers. 1262 1263 This is API is useful to framework extenders who create 1264 pluggable systems which need to register 'proxy' view 1265 callables for functions, instances, or classes which meet the 1266 requirements of being a :app:`Pyramid` view callable. For 1267 example, a ``some_other_framework`` function in another 1268 framework may want to allow a user to supply a view callable, 1269 but he may want to wrap the view callable in his own before 1270 registering the wrapper as a :app:`Pyramid` view callable. 1271 Because a :app:`Pyramid` view callable can be any of a 1272 number of valid objects, the framework extender will not know 1273 how to call the user-supplied object. Running it through 1274 ``derive_view`` normalizes it to a callable which accepts two 1275 arguments: ``context`` and ``request``. 1276 1277 For example: 1278 1279 .. code-block:: python 1280 1281 def some_other_framework(user_supplied_view): 1282 config = Configurator(reg) 1283 proxy_view = config.derive_view(user_supplied_view) 1284 def my_wrapper(context, request): 1285 do_something_that_mutates(request) 1286 return proxy_view(context, request) 1287 config.add_view(my_wrapper) 1288 1289 The ``view`` object provided should be one of the following: 1290 1291 - A function or another non-class callable object that accepts 1292 a :term:`request` as a single positional argument and which 1293 returns a :term:`response` object. 1294 1295 - A function or other non-class callable object that accepts 1296 two positional arguments, ``context, request`` and which 1297 returns a :term:`response` object. 1298 1299 - A class which accepts a single positional argument in its 1300 constructor named ``request``, and which has a ``__call__`` 1301 method that accepts no arguments that returns a 1302 :term:`response` object. 1303 1304 - A class which accepts two positional arguments named 1305 ``context, request``, and which has a ``__call__`` method 1306 that accepts no arguments that returns a :term:`response` 1307 object. 1308 1309 - A :term:`dotted Python name` which refers to any of the 1310 kinds of objects above. 1311 1312 This API returns a callable which accepts the arguments 1313 ``context, request`` and which returns the result of calling 1314 the provided ``view`` object. 1315 1316 The ``attr`` keyword argument is most useful when the view 1317 object is a class. It names the method that should be used as 1318 the callable. If ``attr`` is not provided, the attribute 1319 effectively defaults to ``__call__``. See 1320 :ref:`class_as_view` for more information. 1321 1322 The ``renderer`` keyword argument should be a renderer 1323 name. If supplied, it will cause the returned callable to use 1324 a :term:`renderer` to convert the user-supplied view result to 1325 a :term:`response` object. If a ``renderer`` argument is not 1326 supplied, the user-supplied view must itself return a 1327 :term:`response` object. """ 1328 return self._derive_view(view, attr=attr, renderer=renderer) 1329 1330 # b/w compat 1331 def _derive_view(self, view, permission=None, predicates=(), 1332 attr=None, renderer=None, wrapper_viewname=None, 1333 viewname=None, accept=None, order=MAX_ORDER, 1334 phash=DEFAULT_PHASH, decorator=None, 1335 mapper=None, http_cache=None, context=None, 1336 require_csrf=None, extra_options=None): 1337 view = self.maybe_dotted(view) 1338 mapper = self.maybe_dotted(mapper) 1339 if isinstance(renderer, string_types): 1340 renderer = renderers.RendererHelper( 1341 name=renderer, package=self.package, 1342 registry=self.registry) 1343 if renderer is None: 1344 # use default renderer if one exists 1345 if self.registry.queryUtility(IRendererFactory) is not None: 1346 renderer = renderers.RendererHelper( 1347 name=None, 1348 package=self.package, 1349 registry=self.registry) 1350 1351 options = dict( 1352 view=view, 1353 context=context, 1354 permission=permission, 1355 attr=attr, 1356 renderer=renderer, 1357 wrapper=wrapper_viewname, 1358 name=viewname, 1359 accept=accept, 1360 mapper=mapper, 1361 decorator=decorator, 1362 http_cache=http_cache, 1363 require_csrf=require_csrf, 1364 ) 1365 if extra_options: 1366 options.update(extra_options) 1367 1368 info = ViewDeriverInfo( 1369 view=view, 1370 registry=self.registry, 1371 package=self.package, 1372 predicates=predicates, 1373 options=options, 1374 ) 1375 1376 # order and phash are only necessary for the predicated view and 1377 # are not really view deriver options 1378 info.order = order 1379 info.phash = phash 1380 1381 return self._apply_view_derivers(info) 1382 1383 @viewdefaults 1384 @action_method 1385 def add_forbidden_view( 1386 self, 1387 view=None, 1388 attr=None, 1389 renderer=None, 1390 wrapper=None, 1391 route_name=None, 1392 request_type=None, 1393 request_method=None, 1394 request_param=None, 1395 containment=None, 1396 xhr=None, 1397 accept=None, 1398 header=None, 1399 path_info=None, 1400 custom_predicates=(), 1401 decorator=None, 1402 mapper=None, 1403 match_param=None, 1404 **view_options 1405 ): 1406 """ Add a forbidden view to the current configuration state. The 1407 view will be called when Pyramid or application code raises a 1408 :exc:`pyramid.httpexceptions.HTTPForbidden` exception and the set of 1409 circumstances implied by the predicates provided are matched. The 1410 simplest example is: 1411 1412 .. code-block:: python 1413 1414 def forbidden(request): 1415 return Response('Forbidden', status='403 Forbidden') 1416 1417 config.add_forbidden_view(forbidden) 1418 1419 If ``view`` argument is not provided, the view callable defaults to 1420 :func:`~pyramid.httpexceptions.default_exceptionresponse_view`. 1421 1422 All arguments have the same meaning as 1423 :meth:`pyramid.config.Configurator.add_view` and each predicate 1424 argument restricts the set of circumstances under which this forbidden 1425 view will be invoked. Unlike 1426 :meth:`pyramid.config.Configurator.add_view`, this method will raise 1427 an exception if passed ``name``, ``permission``, ``context``, 1428 ``for_``, or ``http_cache`` keyword arguments. These argument values 1429 make no sense in the context of a forbidden view. 1430 1431 .. versionadded:: 1.3 1432 """ 1433 for arg in ( 1434 'name', 'permission', 'context', 'for_', 'http_cache', 1435 'require_csrf', 1436 ): 1437 if arg in view_options: 1438 raise ConfigurationError( 1439 '%s may not be used as an argument to add_forbidden_view' 1440 % arg 1441 ) 1442 1443 if view is None: 1444 view = default_exceptionresponse_view 1445 1446 settings = dict( 1447 view=view, 1448 context=HTTPForbidden, 1449 wrapper=wrapper, 1450 request_type=request_type, 1451 request_method=request_method, 1452 request_param=request_param, 1453 containment=containment, 1454 xhr=xhr, 1455 accept=accept, 1456 header=header, 1457 path_info=path_info, 1458 custom_predicates=custom_predicates, 1459 decorator=decorator, 1460 mapper=mapper, 1461 match_param=match_param, 1462 route_name=route_name, 1463 permission=NO_PERMISSION_REQUIRED, 1464 require_csrf=False, 1465 attr=attr, 1466 renderer=renderer, 1467 ) 1468 settings.update(view_options) 1469 return self.add_view(**settings) 1470 1471 set_forbidden_view = add_forbidden_view # deprecated sorta-bw-compat alias 1472 1473 @viewdefaults 1474 @action_method 1475 def add_notfound_view( 1476 self, 1477 view=None, 1478 attr=None, 1479 renderer=None, 1480 wrapper=None, 1481 route_name=None, 1482 request_type=None, 1483 request_method=None, 1484 request_param=None, 1485 containment=None, 1486 xhr=None, 1487 accept=None, 1488 header=None, 1489 path_info=None, 1490 custom_predicates=(), 1491 decorator=None, 1492 mapper=None, 1493 match_param=None, 1494 append_slash=False, 1495 **view_options 1496 ): 1497 """ Add a default Not Found View to the current configuration state. 1498 The view will be called when Pyramid or application code raises an 1499 :exc:`pyramid.httpexceptions.HTTPNotFound` exception (e.g. when a 1500 view cannot be found for the request). The simplest example is: 1501 1502 .. code-block:: python 1503 1504 def notfound(request): 1505 return Response('Not Found', status='404 Not Found') 1506 1507 config.add_notfound_view(notfound) 1508 1509 If ``view`` argument is not provided, the view callable defaults to 1510 :func:`~pyramid.httpexceptions.default_exceptionresponse_view`. 1511 1512 All arguments except ``append_slash`` have the same meaning as 1513 :meth:`pyramid.config.Configurator.add_view` and each predicate 1514 argument restricts the set of circumstances under which this notfound 1515 view will be invoked. Unlike 1516 :meth:`pyramid.config.Configurator.add_view`, this method will raise 1517 an exception if passed ``name``, ``permission``, ``context``, 1518 ``for_``, or ``http_cache`` keyword arguments. These argument values 1519 make no sense in the context of a Not Found View. 1520 1521 If ``append_slash`` is ``True``, when this Not Found View is invoked, 1522 and the current path info does not end in a slash, the notfound logic 1523 will attempt to find a :term:`route` that matches the request's path 1524 info suffixed with a slash. If such a route exists, Pyramid will 1525 issue a redirect to the URL implied by the route; if it does not, 1526 Pyramid will return the result of the view callable provided as 1527 ``view``, as normal. 1528 1529 If the argument provided as ``append_slash`` is not a boolean but 1530 instead implements :class:`~pyramid.interfaces.IResponse`, the 1531 append_slash logic will behave as if ``append_slash=True`` was passed, 1532 but the provided class will be used as the response class instead of 1533 the default :class:`~pyramid.httpexceptions.HTTPFound` response class 1534 when a redirect is performed. For example: 1535 1536 .. code-block:: python 1537 1538 from pyramid.httpexceptions import HTTPMovedPermanently 1539 config.add_notfound_view(append_slash=HTTPMovedPermanently) 1540 1541 The above means that a redirect to a slash-appended route will be 1542 attempted, but instead of :class:`~pyramid.httpexceptions.HTTPFound` 1543 being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will 1544 be used` for the redirect response if a slash-appended route is found. 1545 1546 .. versionchanged:: 1.6 1547 .. versionadded:: 1.3 1548 """ 1549 for arg in ( 1550 'name', 'permission', 'context', 'for_', 'http_cache', 1551 'require_csrf', 1552 ): 1553 if arg in view_options: 1554 raise ConfigurationError( 1555 '%s may not be used as an argument to add_notfound_view' 1556 % arg 1557 ) 1558 1559 if view is None: 1560 view = default_exceptionresponse_view 1561 1562 settings = dict( 1563 view=view, 1564 context=HTTPNotFound, 1565 wrapper=wrapper, 1566 request_type=request_type, 1567 request_method=request_method, 1568 request_param=request_param, 1569 containment=containment, 1570 xhr=xhr, 1571 accept=accept, 1572 header=header, 1573 path_info=path_info, 1574 custom_predicates=custom_predicates, 1575 decorator=decorator, 1576 mapper=mapper, 1577 match_param=match_param, 1578 route_name=route_name, 1579 permission=NO_PERMISSION_REQUIRED, 1580 require_csrf=False, 1581 ) 1582 settings.update(view_options) 1583 if append_slash: 1584 view = self._derive_view(view, attr=attr, renderer=renderer) 1585 if IResponse.implementedBy(append_slash): 1586 view = AppendSlashNotFoundViewFactory( 1587 view, redirect_class=append_slash, 1588 ) 1589 else: 1590 view = AppendSlashNotFoundViewFactory(view) 1591 settings['view'] = view 1592 else: 1593 settings['attr'] = attr 1594 settings['renderer'] = renderer 1595 return self.add_view(**settings) 1596 1597 set_notfound_view = add_notfound_view # deprecated sorta-bw-compat alias 1598 1599 @action_method 1600 def set_view_mapper(self, mapper): 1601 """ 1602 Setting a :term:`view mapper` makes it possible to make use of 1603 :term:`view callable` objects which implement different call 1604 signatures than the ones supported by :app:`Pyramid` as described in 1605 its narrative documentation. 1606 1607 The ``mapper`` argument should be an object implementing 1608 :class:`pyramid.interfaces.IViewMapperFactory` or a :term:`dotted 1609 Python name` to such an object. The provided ``mapper`` will become 1610 the default view mapper to be used by all subsequent :term:`view 1611 configuration` registrations. 1612 1613 .. seealso:: 1614 1615 See also :ref:`using_a_view_mapper`. 1616 1617 .. note:: 1618 1619 Using the ``default_view_mapper`` argument to the 1620 :class:`pyramid.config.Configurator` constructor 1621 can be used to achieve the same purpose. 1622 """ 1623 mapper = self.maybe_dotted(mapper) 1624 def register(): 1625 self.registry.registerUtility(mapper, IViewMapperFactory) 1626 # IViewMapperFactory is looked up as the result of view config 1627 # in phase 3 1628 intr = self.introspectable('view mappers', 1629 IViewMapperFactory, 1630 self.object_description(mapper), 1631 'default view mapper') 1632 intr['mapper'] = mapper 1633 self.action(IViewMapperFactory, register, order=PHASE1_CONFIG, 1634 introspectables=(intr,)) 1635 1636 @action_method 1637 def add_static_view(self, name, path, **kw): 1638 """ Add a view used to render static assets such as images 1639 and CSS files. 1640 1641 The ``name`` argument is a string representing an 1642 application-relative local URL prefix. It may alternately be a full 1643 URL. 1644 1645 The ``path`` argument is the path on disk where the static files 1646 reside. This can be an absolute path, a package-relative path, or a 1647 :term:`asset specification`. 1648 1649 The ``cache_max_age`` keyword argument is input to set the 1650 ``Expires`` and ``Cache-Control`` headers for static assets served. 1651 Note that this argument has no effect when the ``name`` is a *url 1652 prefix*. By default, this argument is ``None``, meaning that no 1653 particular Expires or Cache-Control headers are set in the response. 1654 1655 The ``permission`` keyword argument is used to specify the 1656 :term:`permission` required by a user to execute the static view. By 1657 default, it is the string 1658 :data:`pyramid.security.NO_PERMISSION_REQUIRED`, a special sentinel 1659 which indicates that, even if a :term:`default permission` exists for 1660 the current application, the static view should be renderered to 1661 completely anonymous users. This default value is permissive 1662 because, in most web apps, static assets seldom need protection from 1663 viewing. If ``permission`` is specified, the security checking will 1664 be performed against the default root factory ACL. 1665 1666 Any other keyword arguments sent to ``add_static_view`` are passed on 1667 to :meth:`pyramid.config.Configurator.add_route` (e.g. ``factory``, 1668 perhaps to define a custom factory with a custom ACL for this static 1669 view). 1670 1671 *Usage* 1672 1673 The ``add_static_view`` function is typically used in conjunction 1674 with the :meth:`pyramid.request.Request.static_url` method. 1675 ``add_static_view`` adds a view which renders a static asset when 1676 some URL is visited; :meth:`pyramid.request.Request.static_url` 1677 generates a URL to that asset. 1678 1679 The ``name`` argument to ``add_static_view`` is usually a simple URL 1680 prefix (e.g. ``'images'``). When this is the case, the 1681 :meth:`pyramid.request.Request.static_url` API will generate a URL 1682 which points to a Pyramid view, which will serve up a set of assets 1683 that live in the package itself. For example: 1684 1685 .. code-block:: python 1686 1687 add_static_view('images', 'mypackage:images/') 1688 1689 Code that registers such a view can generate URLs to the view via 1690 :meth:`pyramid.request.Request.static_url`: 1691 1692 .. code-block:: python 1693 1694 request.static_url('mypackage:images/logo.png') 1695 1696 When ``add_static_view`` is called with a ``name`` argument that 1697 represents a URL prefix, as it is above, subsequent calls to 1698 :meth:`pyramid.request.Request.static_url` with paths that start with 1699 the ``path`` argument passed to ``add_static_view`` will generate a 1700 URL something like ``http://<Pyramid app URL>/images/logo.png``, 1701 which will cause the ``logo.png`` file in the ``images`` subdirectory 1702 of the ``mypackage`` package to be served. 1703 1704 ``add_static_view`` can alternately be used with a ``name`` argument 1705 which is a *URL*, causing static assets to be served from an external 1706 webserver. This happens when the ``name`` argument is a fully 1707 qualified URL (e.g. starts with ``http://`` or similar). In this 1708 mode, the ``name`` is used as the prefix of the full URL when 1709 generating a URL using :meth:`pyramid.request.Request.static_url`. 1710 Furthermore, if a protocol-relative URL (e.g. ``//example.com/images``) 1711 is used as the ``name`` argument, the generated URL will use the 1712 protocol of the request (http or https, respectively). 1713 1714 For example, if ``add_static_view`` is called like so: 1715 1716 .. code-block:: python 1717 1718 add_static_view('http://example.com/images', 'mypackage:images/') 1719 1720 Subsequently, the URLs generated by 1721 :meth:`pyramid.request.Request.static_url` for that static view will 1722 be prefixed with ``http://example.com/images`` (the external webserver 1723 listening on ``example.com`` must be itself configured to respond 1724 properly to such a request.): 1725 1726 .. code-block:: python 1727 1728 static_url('mypackage:images/logo.png', request) 1729 1730 See :ref:`static_assets_section` for more information. 1731 """ 1732 spec = self._make_spec(path) 1733 info = self._get_static_info() 1734 info.add(self, name, spec, **kw) 1735 1736 def add_cache_buster(self, path, cachebust, explicit=False): 1737 """ 1738 Add a cache buster to a set of files on disk. 1739 1740 The ``path`` should be the path on disk where the static files 1741 reside. This can be an absolute path, a package-relative path, or a 1742 :term:`asset specification`. 1743 1744 The ``cachebust`` argument may be set to cause 1745 :meth:`~pyramid.request.Request.static_url` to use cache busting when 1746 generating URLs. See :ref:`cache_busting` for general information 1747 about cache busting. The value of the ``cachebust`` argument must 1748 be an object which implements 1749 :class:`~pyramid.interfaces.ICacheBuster`. 1750 1751 If ``explicit`` is set to ``True`` then the ``path`` for the cache 1752 buster will be matched based on the ``rawspec`` instead of the 1753 ``pathspec`` as defined in the 1754 :class:`~pyramid.interfaces.ICacheBuster` interface. 1755 Default: ``False``. 1756 1757 """ 1758 spec = self._make_spec(path) 1759 info = self._get_static_info() 1760 info.add_cache_buster(self, spec, cachebust, explicit=explicit) 1761 1762 def _get_static_info(self): 1763 info = self.registry.queryUtility(IStaticURLInfo) 1764 if info is None: 1765 info = StaticURLInfo() 1766 self.registry.registerUtility(info, IStaticURLInfo) 1767 return info 1768 1769def isexception(o): 1770 if IInterface.providedBy(o): 1771 if IException.isEqualOrExtendedBy(o): 1772 return True 1773 return ( 1774 isinstance(o, Exception) or 1775 (inspect.isclass(o) and (issubclass(o, Exception))) 1776 ) 1777 1778@implementer(IViewDeriverInfo) 1779class ViewDeriverInfo(object): 1780 def __init__(self, view, registry, package, predicates, options): 1781 self.original_view = view 1782 self.registry = registry 1783 self.package = package 1784 self.predicates = predicates or [] 1785 self.options = options or {} 1786 1787 @reify 1788 def settings(self): 1789 return self.registry.settings 1790 1791@implementer(IStaticURLInfo) 1792class StaticURLInfo(object): 1793 def __init__(self): 1794 self.registrations = [] 1795 self.cache_busters = [] 1796 1797 def generate(self, path, request, **kw): 1798 for (url, spec, route_name) in self.registrations: 1799 if path.startswith(spec): 1800 subpath = path[len(spec):] 1801 if WIN: # pragma: no cover 1802 subpath = subpath.replace('\\', '/') # windows 1803 if self.cache_busters: 1804 subpath, kw = self._bust_asset_path( 1805 request, spec, subpath, kw) 1806 if url is None: 1807 kw['subpath'] = subpath 1808 return request.route_url(route_name, **kw) 1809 else: 1810 app_url, scheme, host, port, qs, anchor = \ 1811 parse_url_overrides(kw) 1812 parsed = url_parse(url) 1813 if not parsed.scheme: 1814 url = urlparse.urlunparse(parsed._replace( 1815 scheme=request.environ['wsgi.url_scheme'])) 1816 subpath = url_quote(subpath) 1817 result = urljoin(url, subpath) 1818 return result + qs + anchor 1819 1820 raise ValueError('No static URL definition matching %s' % path) 1821 1822 def add(self, config, name, spec, **extra): 1823 # This feature only allows for the serving of a directory and 1824 # the files contained within, not of a single asset; 1825 # appending a slash here if the spec doesn't have one is 1826 # required for proper prefix matching done in ``generate`` 1827 # (``subpath = path[len(spec):]``). 1828 if os.path.isabs(spec): # FBO windows 1829 sep = os.sep 1830 else: 1831 sep = '/' 1832 if not spec.endswith(sep) and not spec.endswith(':'): 1833 spec = spec + sep 1834 1835 # we also make sure the name ends with a slash, purely as a 1836 # convenience: a name that is a url is required to end in a 1837 # slash, so that ``urljoin(name, subpath))`` will work above 1838 # when the name is a URL, and it doesn't hurt things for it to 1839 # have a name that ends in a slash if it's used as a route 1840 # name instead of a URL. 1841 if not name.endswith('/'): 1842 # make sure it ends with a slash 1843 name = name + '/' 1844 1845 if url_parse(name).netloc: 1846 # it's a URL 1847 # url, spec, route_name 1848 url = name 1849 route_name = None 1850 else: 1851 # it's a view name 1852 url = None 1853 cache_max_age = extra.pop('cache_max_age', None) 1854 1855 # create a view 1856 view = static_view(spec, cache_max_age=cache_max_age, 1857 use_subpath=True) 1858 1859 # Mutate extra to allow factory, etc to be passed through here. 1860 # Treat permission specially because we'd like to default to 1861 # permissiveness (see docs of config.add_static_view). 1862 permission = extra.pop('permission', None) 1863 if permission is None: 1864 permission = NO_PERMISSION_REQUIRED 1865 1866 context = extra.pop('context', None) 1867 if context is None: 1868 context = extra.pop('for_', None) 1869 1870 renderer = extra.pop('renderer', None) 1871 1872 # register a route using the computed view, permission, and 1873 # pattern, plus any extras passed to us via add_static_view 1874 pattern = "%s*subpath" % name # name already ends with slash 1875 if config.route_prefix: 1876 route_name = '__%s/%s' % (config.route_prefix, name) 1877 else: 1878 route_name = '__%s' % name 1879 config.add_route(route_name, pattern, **extra) 1880 config.add_view( 1881 route_name=route_name, 1882 view=view, 1883 permission=permission, 1884 context=context, 1885 renderer=renderer, 1886 ) 1887 1888 def register(): 1889 registrations = self.registrations 1890 1891 names = [t[0] for t in registrations] 1892 1893 if name in names: 1894 idx = names.index(name) 1895 registrations.pop(idx) 1896 1897 # url, spec, route_name 1898 registrations.append((url, spec, route_name)) 1899 1900 intr = config.introspectable('static views', 1901 name, 1902 'static view for %r' % name, 1903 'static view') 1904 intr['name'] = name 1905 intr['spec'] = spec 1906 1907 config.action(None, callable=register, introspectables=(intr,)) 1908 1909 def add_cache_buster(self, config, spec, cachebust, explicit=False): 1910 # ensure the spec always has a trailing slash as we only support 1911 # adding cache busters to folders, not files 1912 if os.path.isabs(spec): # FBO windows 1913 sep = os.sep 1914 else: 1915 sep = '/' 1916 if not spec.endswith(sep) and not spec.endswith(':'): 1917 spec = spec + sep 1918 1919 def register(): 1920 if config.registry.settings.get('pyramid.prevent_cachebust'): 1921 return 1922 1923 cache_busters = self.cache_busters 1924 1925 # find duplicate cache buster (old_idx) 1926 # and insertion location (new_idx) 1927 new_idx, old_idx = len(cache_busters), None 1928 for idx, (spec_, cb_, explicit_) in enumerate(cache_busters): 1929 # if we find an identical (spec, explicit) then use it 1930 if spec == spec_ and explicit == explicit_: 1931 old_idx = new_idx = idx 1932 break 1933 1934 # past all explicit==False specs then add to the end 1935 elif not explicit and explicit_: 1936 new_idx = idx 1937 break 1938 1939 # explicit matches and spec is shorter 1940 elif explicit == explicit_ and len(spec) < len(spec_): 1941 new_idx = idx 1942 break 1943 1944 if old_idx is not None: 1945 cache_busters.pop(old_idx) 1946 1947 cache_busters.insert(new_idx, (spec, cachebust, explicit)) 1948 1949 intr = config.introspectable('cache busters', 1950 spec, 1951 'cache buster for %r' % spec, 1952 'cache buster') 1953 intr['cachebust'] = cachebust 1954 intr['path'] = spec 1955 intr['explicit'] = explicit 1956 1957 config.action(None, callable=register, introspectables=(intr,)) 1958 1959 def _bust_asset_path(self, request, spec, subpath, kw): 1960 registry = request.registry 1961 pkg_name, pkg_subpath = resolve_asset_spec(spec) 1962 rawspec = None 1963 1964 if pkg_name is not None: 1965 pathspec = '{0}:{1}{2}'.format(pkg_name, pkg_subpath, subpath) 1966 overrides = registry.queryUtility(IPackageOverrides, name=pkg_name) 1967 if overrides is not None: 1968 resource_name = posixpath.join(pkg_subpath, subpath) 1969 sources = overrides.filtered_sources(resource_name) 1970 for source, filtered_path in sources: 1971 rawspec = source.get_path(filtered_path) 1972 if hasattr(source, 'pkg_name'): 1973 rawspec = '{0}:{1}'.format(source.pkg_name, rawspec) 1974 break 1975 1976 else: 1977 pathspec = pkg_subpath + subpath 1978 1979 if rawspec is None: 1980 rawspec = pathspec 1981 1982 kw['pathspec'] = pathspec 1983 kw['rawspec'] = rawspec 1984 for spec_, cachebust, explicit in reversed(self.cache_busters): 1985 if ( 1986 (explicit and rawspec.startswith(spec_)) or 1987 (not explicit and pathspec.startswith(spec_)) 1988 ): 1989 subpath, kw = cachebust(request, subpath, kw) 1990 break 1991 return subpath, kw 1992