Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import functools 

2import inspect 

3import posixpath 

4import operator 

5import os 

6import warnings 

7 

8from webob.acceptparse import Accept 

9from zope.interface import Interface, implementedBy, implementer 

10from zope.interface.interfaces import IInterface 

11 

12from pyramid.interfaces import ( 

13 IAcceptOrder, 

14 IExceptionViewClassifier, 

15 IException, 

16 IMultiView, 

17 IPackageOverrides, 

18 IRendererFactory, 

19 IRequest, 

20 IResponse, 

21 IRouteRequest, 

22 ISecuredView, 

23 IStaticURLInfo, 

24 IView, 

25 IViewClassifier, 

26 IViewDerivers, 

27 IViewDeriverInfo, 

28 IViewMapperFactory, 

29 PHASE1_CONFIG, 

30) 

31 

32from pyramid import renderers 

33 

34from pyramid.asset import resolve_asset_spec 

35from pyramid.compat import ( 

36 string_types, 

37 urlparse, 

38 url_quote, 

39 WIN, 

40 is_nonstr_iter, 

41) 

42 

43from pyramid.decorator import reify 

44 

45from pyramid.exceptions import ConfigurationError, PredicateMismatch 

46 

47from pyramid.httpexceptions import ( 

48 HTTPForbidden, 

49 HTTPNotFound, 

50 default_exceptionresponse_view, 

51) 

52 

53from pyramid.registry import Deferred 

54 

55from pyramid.security import NO_PERMISSION_REQUIRED 

56from pyramid.static import static_view 

57 

58from pyramid.url import parse_url_overrides 

59 

60from pyramid.view import AppendSlashNotFoundViewFactory 

61 

62from pyramid.util import as_sorted_tuple, TopologicalSorter 

63 

64import pyramid.predicates 

65import pyramid.viewderivers 

66 

67from pyramid.viewderivers import ( 

68 INGRESS, 

69 VIEW, 

70 preserve_view_attrs, 

71 view_description, 

72 requestonly, 

73 DefaultViewMapper, 

74 wraps_view, 

75) 

76 

77from pyramid.config.actions import action_method 

78from pyramid.config.predicates import ( 

79 DEFAULT_PHASH, 

80 MAX_ORDER, 

81 normalize_accept_offer, 

82 predvalseq, 

83 sort_accept_offers, 

84) 

85 

86urljoin = urlparse.urljoin 

87url_parse = urlparse.urlparse 

88 

89DefaultViewMapper = DefaultViewMapper # bw-compat 

90preserve_view_attrs = preserve_view_attrs # bw-compat 

91requestonly = requestonly # bw-compat 

92view_description = view_description # bw-compat 

93 

94 

95@implementer(IMultiView) 

96class MultiView(object): 

97 def __init__(self, name): 

98 self.name = name 

99 self.media_views = {} 

100 self.views = [] 

101 self.accepts = [] 

102 

103 def __discriminator__(self, context, request): 

104 # used by introspection systems like so: 

105 # view = adapters.lookup(....) 

106 # view.__discriminator__(context, request) -> view's discriminator 

107 # so that superdynamic systems can feed the discriminator to 

108 # the introspection system to get info about it 

109 view = self.match(context, request) 

110 return view.__discriminator__(context, request) 

111 

112 def add(self, view, order, phash=None, accept=None, accept_order=None): 

113 if phash is not None: 

114 for i, (s, v, h) in enumerate(list(self.views)): 

115 if phash == h: 

116 self.views[i] = (order, view, phash) 

117 return 

118 

119 if accept is None or '*' in accept: 

120 self.views.append((order, view, phash)) 

121 self.views.sort(key=operator.itemgetter(0)) 

122 else: 

123 subset = self.media_views.setdefault(accept, []) 

124 for i, (s, v, h) in enumerate(list(subset)): 

125 if phash == h: 

126 subset[i] = (order, view, phash) 

127 return 

128 else: 

129 subset.append((order, view, phash)) 

130 subset.sort(key=operator.itemgetter(0)) 

131 # dedupe accepts and sort appropriately 

132 accepts = set(self.accepts) 

133 accepts.add(accept) 

134 if accept_order: 

135 accept_order = [v for _, v in accept_order.sorted()] 

136 self.accepts = sort_accept_offers(accepts, accept_order) 

137 

138 def get_views(self, request): 

139 if self.accepts and hasattr(request, 'accept'): 

140 views = [] 

141 for offer, _ in request.accept.acceptable_offers(self.accepts): 

142 views.extend(self.media_views[offer]) 

143 views.extend(self.views) 

144 return views 

145 return self.views 

146 

147 def match(self, context, request): 

148 for order, view, phash in self.get_views(request): 

149 if not hasattr(view, '__predicated__'): 

150 return view 

151 if view.__predicated__(context, request): 

152 return view 

153 raise PredicateMismatch(self.name) 

154 

155 def __permitted__(self, context, request): 

156 view = self.match(context, request) 

157 if hasattr(view, '__permitted__'): 

158 return view.__permitted__(context, request) 

159 return True 

160 

161 def __call_permissive__(self, context, request): 

162 view = self.match(context, request) 

163 view = getattr(view, '__call_permissive__', view) 

164 return view(context, request) 

165 

166 def __call__(self, context, request): 

167 for order, view, phash in self.get_views(request): 

168 try: 

169 return view(context, request) 

170 except PredicateMismatch: 

171 continue 

172 raise PredicateMismatch(self.name) 

173 

174 

175def attr_wrapped_view(view, info): 

176 accept, order, phash = ( 

177 info.options.get('accept', None), 

178 getattr(info, 'order', MAX_ORDER), 

179 getattr(info, 'phash', DEFAULT_PHASH), 

180 ) 

181 # this is a little silly but we don't want to decorate the original 

182 # function with attributes that indicate accept, order, and phash, 

183 # so we use a wrapper 

184 if (accept is None) and (order == MAX_ORDER) and (phash == DEFAULT_PHASH): 

185 return view # defaults 

186 

187 def attr_view(context, request): 

188 return view(context, request) 

189 

190 attr_view.__accept__ = accept 

191 attr_view.__order__ = order 

192 attr_view.__phash__ = phash 

193 attr_view.__view_attr__ = info.options.get('attr') 

194 attr_view.__permission__ = info.options.get('permission') 

195 return attr_view 

196 

197 

198attr_wrapped_view.options = ('accept', 'attr', 'permission') 

199 

200 

201def predicated_view(view, info): 

202 preds = info.predicates 

203 if not preds: 

204 return view 

205 

206 def predicate_wrapper(context, request): 

207 for predicate in preds: 

208 if not predicate(context, request): 

209 view_name = getattr(view, '__name__', view) 

210 raise PredicateMismatch( 

211 'predicate mismatch for view %s (%s)' 

212 % (view_name, predicate.text()) 

213 ) 

214 return view(context, request) 

215 

216 def checker(context, request): 

217 return all((predicate(context, request) for predicate in preds)) 

218 

219 predicate_wrapper.__predicated__ = checker 

220 predicate_wrapper.__predicates__ = preds 

221 return predicate_wrapper 

222 

223 

224def viewdefaults(wrapped): 

225 """ Decorator for add_view-like methods which takes into account 

226 __view_defaults__ attached to view it is passed. Not a documented API but 

227 used by some external systems.""" 

228 

229 def wrapper(self, *arg, **kw): 

230 defaults = {} 

231 if arg: 

232 view = arg[0] 

233 else: 

234 view = kw.get('view') 

235 view = self.maybe_dotted(view) 

236 if inspect.isclass(view): 

237 defaults = getattr(view, '__view_defaults__', {}).copy() 

238 if '_backframes' not in kw: 

239 kw['_backframes'] = 1 # for action_method 

240 defaults.update(kw) 

241 return wrapped(self, *arg, **defaults) 

242 

243 return functools.wraps(wrapped)(wrapper) 

244 

245 

246def combine_decorators(*decorators): 

247 def decorated(view_callable): 

248 # reversed() allows a more natural ordering in the api 

249 for decorator in reversed(decorators): 

250 view_callable = decorator(view_callable) 

251 return view_callable 

252 

253 return decorated 

254 

255 

256class ViewsConfiguratorMixin(object): 

257 @viewdefaults 

258 @action_method 

259 def add_view( 

260 self, 

261 view=None, 

262 name="", 

263 for_=None, 

264 permission=None, 

265 request_type=None, 

266 route_name=None, 

267 request_method=None, 

268 request_param=None, 

269 containment=None, 

270 attr=None, 

271 renderer=None, 

272 wrapper=None, 

273 xhr=None, 

274 accept=None, 

275 header=None, 

276 path_info=None, 

277 custom_predicates=(), 

278 context=None, 

279 decorator=None, 

280 mapper=None, 

281 http_cache=None, 

282 match_param=None, 

283 check_csrf=None, 

284 require_csrf=None, 

285 exception_only=False, 

286 **view_options 

287 ): 

288 """ Add a :term:`view configuration` to the current 

289 configuration state. Arguments to ``add_view`` are broken 

290 down below into *predicate* arguments and *non-predicate* 

291 arguments. Predicate arguments narrow the circumstances in 

292 which the view callable will be invoked when a request is 

293 presented to :app:`Pyramid`; non-predicate arguments are 

294 informational. 

295 

296 Non-Predicate Arguments 

297 

298 view 

299 

300 A :term:`view callable` or a :term:`dotted Python name` 

301 which refers to a view callable. This argument is required 

302 unless a ``renderer`` argument also exists. If a 

303 ``renderer`` argument is passed, and a ``view`` argument is 

304 not provided, the view callable defaults to a callable that 

305 returns an empty dictionary (see 

306 :ref:`views_which_use_a_renderer`). 

307 

308 permission 

309 

310 A :term:`permission` that the user must possess in order to invoke 

311 the :term:`view callable`. See :ref:`view_security_section` for 

312 more information about view security and permissions. This is 

313 often a string like ``view`` or ``edit``. 

314 

315 If ``permission`` is omitted, a *default* permission may be used 

316 for this view registration if one was named as the 

317 :class:`pyramid.config.Configurator` constructor's 

318 ``default_permission`` argument, or if 

319 :meth:`pyramid.config.Configurator.set_default_permission` was used 

320 prior to this view registration. Pass the value 

321 :data:`pyramid.security.NO_PERMISSION_REQUIRED` as the permission 

322 argument to explicitly indicate that the view should always be 

323 executable by entirely anonymous users, regardless of the default 

324 permission, bypassing any :term:`authorization policy` that may be 

325 in effect. 

326 

327 attr 

328 

329 This knob is most useful when the view definition is a class. 

330 

331 The view machinery defaults to using the ``__call__`` method 

332 of the :term:`view callable` (or the function itself, if the 

333 view callable is a function) to obtain a response. The 

334 ``attr`` value allows you to vary the method attribute used 

335 to obtain the response. For example, if your view was a 

336 class, and the class has a method named ``index`` and you 

337 wanted to use this method instead of the class' ``__call__`` 

338 method to return the response, you'd say ``attr="index"`` in the 

339 view configuration for the view. 

340 

341 renderer 

342 

343 This is either a single string term (e.g. ``json``) or a 

344 string implying a path or :term:`asset specification` 

345 (e.g. ``templates/views.pt``) naming a :term:`renderer` 

346 implementation. If the ``renderer`` value does not contain 

347 a dot ``.``, the specified string will be used to look up a 

348 renderer implementation, and that renderer implementation 

349 will be used to construct a response from the view return 

350 value. If the ``renderer`` value contains a dot (``.``), 

351 the specified term will be treated as a path, and the 

352 filename extension of the last element in the path will be 

353 used to look up the renderer implementation, which will be 

354 passed the full path. The renderer implementation will be 

355 used to construct a :term:`response` from the view return 

356 value. 

357 

358 Note that if the view itself returns a :term:`response` (see 

359 :ref:`the_response`), the specified renderer implementation 

360 is never called. 

361 

362 When the renderer is a path, although a path is usually just 

363 a simple relative pathname (e.g. ``templates/foo.pt``, 

364 implying that a template named "foo.pt" is in the 

365 "templates" directory relative to the directory of the 

366 current :term:`package` of the Configurator), a path can be 

367 absolute, starting with a slash on UNIX or a drive letter 

368 prefix on Windows. The path can alternately be a 

369 :term:`asset specification` in the form 

370 ``some.dotted.package_name:relative/path``, making it 

371 possible to address template assets which live in a 

372 separate package. 

373 

374 The ``renderer`` attribute is optional. If it is not 

375 defined, the "null" renderer is assumed (no rendering is 

376 performed and the value is passed back to the upstream 

377 :app:`Pyramid` machinery unmodified). 

378 

379 http_cache 

380 

381 .. versionadded:: 1.1 

382 

383 When you supply an ``http_cache`` value to a view configuration, 

384 the ``Expires`` and ``Cache-Control`` headers of a response 

385 generated by the associated view callable are modified. The value 

386 for ``http_cache`` may be one of the following: 

387 

388 - A nonzero integer. If it's a nonzero integer, it's treated as a 

389 number of seconds. This number of seconds will be used to 

390 compute the ``Expires`` header and the ``Cache-Control: 

391 max-age`` parameter of responses to requests which call this view. 

392 For example: ``http_cache=3600`` instructs the requesting browser 

393 to 'cache this response for an hour, please'. 

394 

395 - A ``datetime.timedelta`` instance. If it's a 

396 ``datetime.timedelta`` instance, it will be converted into a 

397 number of seconds, and that number of seconds will be used to 

398 compute the ``Expires`` header and the ``Cache-Control: 

399 max-age`` parameter of responses to requests which call this view. 

400 For example: ``http_cache=datetime.timedelta(days=1)`` instructs 

401 the requesting browser to 'cache this response for a day, please'. 

402 

403 - Zero (``0``). If the value is zero, the ``Cache-Control`` and 

404 ``Expires`` headers present in all responses from this view will 

405 be composed such that client browser cache (and any intermediate 

406 caches) are instructed to never cache the response. 

407 

408 - A two-tuple. If it's a two tuple (e.g. ``http_cache=(1, 

409 {'public':True})``), the first value in the tuple may be a 

410 nonzero integer or a ``datetime.timedelta`` instance; in either 

411 case this value will be used as the number of seconds to cache 

412 the response. The second value in the tuple must be a 

413 dictionary. The values present in the dictionary will be used as 

414 input to the ``Cache-Control`` response header. For example: 

415 ``http_cache=(3600, {'public':True})`` means 'cache for an hour, 

416 and add ``public`` to the Cache-Control header of the response'. 

417 All keys and values supported by the 

418 ``webob.cachecontrol.CacheControl`` interface may be added to the 

419 dictionary. Supplying ``{'public':True}`` is equivalent to 

420 calling ``response.cache_control.public = True``. 

421 

422 Providing a non-tuple value as ``http_cache`` is equivalent to 

423 calling ``response.cache_expires(value)`` within your view's body. 

424 

425 Providing a two-tuple value as ``http_cache`` is equivalent to 

426 calling ``response.cache_expires(value[0], **value[1])`` within your 

427 view's body. 

428 

429 If you wish to avoid influencing, the ``Expires`` header, and 

430 instead wish to only influence ``Cache-Control`` headers, pass a 

431 tuple as ``http_cache`` with the first element of ``None``, e.g.: 

432 ``(None, {'public':True})``. 

433 

434 If you wish to prevent a view that uses ``http_cache`` in its 

435 configuration from having its caching response headers changed by 

436 this machinery, set ``response.cache_control.prevent_auto = True`` 

437 before returning the response from the view. This effectively 

438 disables any HTTP caching done by ``http_cache`` for that response. 

439 

440 require_csrf 

441 

442 .. versionadded:: 1.7 

443 

444 A boolean option or ``None``. Default: ``None``. 

445 

446 If this option is set to ``True`` then CSRF checks will be enabled 

447 for requests to this view. The required token or header default to 

448 ``csrf_token`` and ``X-CSRF-Token``, respectively. 

449 

450 CSRF checks only affect "unsafe" methods as defined by RFC2616. By 

451 default, these methods are anything except 

452 ``GET``, ``HEAD``, ``OPTIONS``, and ``TRACE``. 

453 

454 The defaults here may be overridden by 

455 :meth:`pyramid.config.Configurator.set_default_csrf_options`. 

456 

457 This feature requires a configured :term:`session factory`. 

458 

459 If this option is set to ``False`` then CSRF checks will be disabled 

460 regardless of the default ``require_csrf`` setting passed 

461 to ``set_default_csrf_options``. 

462 

463 See :ref:`auto_csrf_checking` for more information. 

464 

465 wrapper 

466 

467 The :term:`view name` of a different :term:`view 

468 configuration` which will receive the response body of this 

469 view as the ``request.wrapped_body`` attribute of its own 

470 :term:`request`, and the :term:`response` returned by this 

471 view as the ``request.wrapped_response`` attribute of its 

472 own request. Using a wrapper makes it possible to "chain" 

473 views together to form a composite response. The response 

474 of the outermost wrapper view will be returned to the user. 

475 The wrapper view will be found as any view is found: see 

476 :ref:`view_lookup`. The "best" wrapper view will be found 

477 based on the lookup ordering: "under the hood" this wrapper 

478 view is looked up via 

479 ``pyramid.view.render_view_to_response(context, request, 

480 'wrapper_viewname')``. The context and request of a wrapper 

481 view is the same context and request of the inner view. If 

482 this attribute is unspecified, no view wrapping is done. 

483 

484 decorator 

485 

486 A :term:`dotted Python name` to function (or the function itself, 

487 or an iterable of the aforementioned) which will be used to 

488 decorate the registered :term:`view callable`. The decorator 

489 function(s) will be called with the view callable as a single 

490 argument. The view callable it is passed will accept 

491 ``(context, request)``. The decorator(s) must return a 

492 replacement view callable which also accepts ``(context, 

493 request)``. 

494 

495 If decorator is an iterable, the callables will be combined and 

496 used in the order provided as a decorator. 

497 For example:: 

498 

499 @view_config(..., 

500 decorator=(decorator2, 

501 decorator1)) 

502 def myview(request): 

503 .... 

504 

505 Is similar to doing:: 

506 

507 @view_config(...) 

508 @decorator2 

509 @decorator1 

510 def myview(request): 

511 ... 

512 

513 Except with the existing benefits of ``decorator=`` (having a common 

514 decorator syntax for all view calling conventions and not having to 

515 think about preserving function attributes such as ``__name__`` and 

516 ``__module__`` within decorator logic). 

517 

518 An important distinction is that each decorator will receive a 

519 response object implementing :class:`pyramid.interfaces.IResponse` 

520 instead of the raw value returned from the view callable. All 

521 decorators in the chain must return a response object or raise an 

522 exception: 

523 

524 .. code-block:: python 

525 

526 def log_timer(wrapped): 

527 def wrapper(context, request): 

528 start = time.time() 

529 response = wrapped(context, request) 

530 duration = time.time() - start 

531 response.headers['X-View-Time'] = '%.3f' % (duration,) 

532 log.info('view took %.3f seconds', duration) 

533 return response 

534 return wrapper 

535 

536 .. versionchanged:: 1.4a4 

537 Passing an iterable. 

538 

539 mapper 

540 

541 A Python object or :term:`dotted Python name` which refers to a 

542 :term:`view mapper`, or ``None``. By default it is ``None``, which 

543 indicates that the view should use the default view mapper. This 

544 plug-point is useful for Pyramid extension developers, but it's not 

545 very useful for 'civilians' who are just developing stock Pyramid 

546 applications. Pay no attention to the man behind the curtain. 

547 

548 accept 

549 

550 A :term:`media type` that will be matched against the ``Accept`` 

551 HTTP request header. If this value is specified, it must be a 

552 specific media type such as ``text/html`` or ``text/html;level=1``. 

553 If the media type is acceptable by the ``Accept`` header of the 

554 request, or if the ``Accept`` header isn't set at all in the request, 

555 this predicate will match. If this does not match the ``Accept`` 

556 header of the request, view matching continues. 

557 

558 If ``accept`` is not specified, the ``HTTP_ACCEPT`` HTTP header is 

559 not taken into consideration when deciding whether or not to invoke 

560 the associated view callable. 

561 

562 The ``accept`` argument is technically not a predicate and does 

563 not support wrapping with :func:`pyramid.config.not_`. 

564 

565 See :ref:`accept_content_negotiation` for more information. 

566 

567 .. versionchanged:: 1.10 

568 

569 Specifying a media range is deprecated and will be removed in 

570 :app:`Pyramid` 2.0. Use explicit media types to avoid any 

571 ambiguities in content negotiation. 

572 

573 exception_only 

574 

575 .. versionadded:: 1.8 

576 

577 When this value is ``True``, the ``context`` argument must be 

578 a subclass of ``Exception``. This flag indicates that only an 

579 :term:`exception view` should be created, and that this view should 

580 not match if the traversal :term:`context` matches the ``context`` 

581 argument. If the ``context`` is a subclass of ``Exception`` and 

582 this value is ``False`` (the default), then a view will be 

583 registered to match the traversal :term:`context` as well. 

584 

585 Predicate Arguments 

586 

587 name 

588 

589 The :term:`view name`. Read :ref:`traversal_chapter` to 

590 understand the concept of a view name. 

591 

592 context 

593 

594 An object or a :term:`dotted Python name` referring to an 

595 interface or class object that the :term:`context` must be 

596 an instance of, *or* the :term:`interface` that the 

597 :term:`context` must provide in order for this view to be 

598 found and called. This predicate is true when the 

599 :term:`context` is an instance of the represented class or 

600 if the :term:`context` provides the represented interface; 

601 it is otherwise false. This argument may also be provided 

602 to ``add_view`` as ``for_`` (an older, still-supported 

603 spelling). If the view should *only* match when handling 

604 exceptions, then set the ``exception_only`` to ``True``. 

605 

606 route_name 

607 

608 This value must match the ``name`` of a :term:`route 

609 configuration` declaration (see :ref:`urldispatch_chapter`) 

610 that must match before this view will be called. 

611 

612 request_type 

613 

614 This value should be an :term:`interface` that the 

615 :term:`request` must provide in order for this view to be 

616 found and called. This value exists only for backwards 

617 compatibility purposes. 

618 

619 request_method 

620 

621 This value can be either a string (such as ``"GET"``, ``"POST"``, 

622 ``"PUT"``, ``"DELETE"``, ``"HEAD"`` or ``"OPTIONS"``) representing 

623 an HTTP ``REQUEST_METHOD``, or a tuple containing one or more of 

624 these strings. A view declaration with this argument ensures that 

625 the view will only be called when the ``method`` attribute of the 

626 request (aka the ``REQUEST_METHOD`` of the WSGI environment) matches 

627 a supplied value. Note that use of ``GET`` also implies that the 

628 view will respond to ``HEAD`` as of Pyramid 1.4. 

629 

630 .. versionchanged:: 1.2 

631 The ability to pass a tuple of items as ``request_method``. 

632 Previous versions allowed only a string. 

633 

634 request_param 

635 

636 This value can be any string or any sequence of strings. A view 

637 declaration with this argument ensures that the view will only be 

638 called when the :term:`request` has a key in the ``request.params`` 

639 dictionary (an HTTP ``GET`` or ``POST`` variable) that has a 

640 name which matches the supplied value (if the value is a string) 

641 or values (if the value is a tuple). If any value 

642 supplied has a ``=`` sign in it, 

643 e.g. ``request_param="foo=123"``, then the key (``foo``) 

644 must both exist in the ``request.params`` dictionary, *and* 

645 the value must match the right hand side of the expression 

646 (``123``) for the view to "match" the current request. 

647 

648 match_param 

649 

650 .. versionadded:: 1.2 

651 

652 This value can be a string of the format "key=value" or a tuple 

653 containing one or more of these strings. 

654 

655 A view declaration with this argument ensures that the view will 

656 only be called when the :term:`request` has key/value pairs in its 

657 :term:`matchdict` that equal those supplied in the predicate. 

658 e.g. ``match_param="action=edit"`` would require the ``action`` 

659 parameter in the :term:`matchdict` match the right hand side of 

660 the expression (``edit``) for the view to "match" the current 

661 request. 

662 

663 If the ``match_param`` is a tuple, every key/value pair must match 

664 for the predicate to pass. 

665 

666 containment 

667 

668 This value should be a Python class or :term:`interface` (or a 

669 :term:`dotted Python name`) that an object in the 

670 :term:`lineage` of the context must provide in order for this view 

671 to be found and called. The nodes in your object graph must be 

672 "location-aware" to use this feature. See 

673 :ref:`location_aware` for more information about 

674 location-awareness. 

675 

676 xhr 

677 

678 This value should be either ``True`` or ``False``. If this 

679 value is specified and is ``True``, the :term:`request` 

680 must possess an ``HTTP_X_REQUESTED_WITH`` (aka 

681 ``X-Requested-With``) header that has the value 

682 ``XMLHttpRequest`` for this view to be found and called. 

683 This is useful for detecting AJAX requests issued from 

684 jQuery, Prototype and other Javascript libraries. 

685 

686 header 

687 

688 This value represents an HTTP header name or a header 

689 name/value pair. If the value contains a ``:`` (colon), it 

690 will be considered a name/value pair 

691 (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). The 

692 value portion should be a regular expression. If the value 

693 does not contain a colon, the entire value will be 

694 considered to be the header name 

695 (e.g. ``If-Modified-Since``). If the value evaluates to a 

696 header name only without a value, the header specified by 

697 the name must be present in the request for this predicate 

698 to be true. If the value evaluates to a header name/value 

699 pair, the header specified by the name must be present in 

700 the request *and* the regular expression specified as the 

701 value must match the header value. Whether or not the value 

702 represents a header name or a header name/value pair, the 

703 case of the header name is not significant. 

704 

705 path_info 

706 

707 This value represents a regular expression pattern that will 

708 be tested against the ``PATH_INFO`` WSGI environment 

709 variable. If the regex matches, this predicate will be 

710 ``True``. 

711 

712 check_csrf 

713 

714 .. deprecated:: 1.7 

715 Use the ``require_csrf`` option or see :ref:`auto_csrf_checking` 

716 instead to have :class:`pyramid.exceptions.BadCSRFToken` 

717 exceptions raised. 

718 

719 If specified, this value should be one of ``None``, ``True``, 

720 ``False``, or a string representing the 'check name'. If the value 

721 is ``True`` or a string, CSRF checking will be performed. If the 

722 value is ``False`` or ``None``, CSRF checking will not be performed. 

723 

724 If the value provided is a string, that string will be used as the 

725 'check name'. If the value provided is ``True``, ``csrf_token`` will 

726 be used as the check name. 

727 

728 If CSRF checking is performed, the checked value will be the value of 

729 ``request.params[check_name]``. This value will be compared against 

730 the value of ``policy.get_csrf_token()`` (where ``policy`` is an 

731 implementation of :meth:`pyramid.interfaces.ICSRFStoragePolicy`), and 

732 the check will pass if these two values are the same. If the check 

733 passes, the associated view will be permitted to execute. If the 

734 check fails, the associated view will not be permitted to execute. 

735 

736 .. versionadded:: 1.4a2 

737 

738 .. versionchanged:: 1.9 

739 This feature requires either a :term:`session factory` to have been 

740 configured, or a :term:`CSRF storage policy` other than the default 

741 to be in use. 

742 

743 

744 physical_path 

745 

746 If specified, this value should be a string or a tuple representing 

747 the :term:`physical path` of the context found via traversal for this 

748 predicate to match as true. For example: ``physical_path='/'`` or 

749 ``physical_path='/a/b/c'`` or ``physical_path=('', 'a', 'b', 'c')``. 

750 This is not a path prefix match or a regex, it's a whole-path match. 

751 It's useful when you want to always potentially show a view when some 

752 object is traversed to, but you can't be sure about what kind of 

753 object it will be, so you can't use the ``context`` predicate. The 

754 individual path elements inbetween slash characters or in tuple 

755 elements should be the Unicode representation of the name of the 

756 resource and should not be encoded in any way. 

757 

758 .. versionadded:: 1.4a3 

759 

760 effective_principals 

761 

762 If specified, this value should be a :term:`principal` identifier or 

763 a sequence of principal identifiers. If the 

764 :attr:`pyramid.request.Request.effective_principals` property 

765 indicates that every principal named in the argument list is present 

766 in the current request, this predicate will return True; otherwise it 

767 will return False. For example: 

768 ``effective_principals=pyramid.security.Authenticated`` or 

769 ``effective_principals=('fred', 'group:admins')``. 

770 

771 .. versionadded:: 1.4a4 

772 

773 custom_predicates 

774 

775 .. deprecated:: 1.5 

776 This value should be a sequence of references to custom 

777 predicate callables. Use custom predicates when no set of 

778 predefined predicates do what you need. Custom predicates 

779 can be combined with predefined predicates as necessary. 

780 Each custom predicate callable should accept two arguments: 

781 ``context`` and ``request`` and should return either 

782 ``True`` or ``False`` after doing arbitrary evaluation of 

783 the context and/or the request. The ``predicates`` argument 

784 to this method and the ability to register third-party view 

785 predicates via 

786 :meth:`pyramid.config.Configurator.add_view_predicate` 

787 obsoletes this argument, but it is kept around for backwards 

788 compatibility. 

789 

790 view_options 

791 

792 Pass a key/value pair here to use a third-party predicate or set a 

793 value for a view deriver. See 

794 :meth:`pyramid.config.Configurator.add_view_predicate` and 

795 :meth:`pyramid.config.Configurator.add_view_deriver`. See 

796 :ref:`view_and_route_predicates` for more information about 

797 third-party predicates and :ref:`view_derivers` for information 

798 about view derivers. 

799 

800 .. versionadded: 1.4a1 

801 

802 .. versionchanged: 1.7 

803 

804 Support setting view deriver options. Previously, only custom 

805 view predicate values could be supplied. 

806 

807 """ 

808 if custom_predicates: 

809 warnings.warn( 

810 ( 

811 'The "custom_predicates" argument to ' 

812 'Configurator.add_view is deprecated as of Pyramid 1.5. ' 

813 'Use "config.add_view_predicate" and use the registered ' 

814 'view predicate as a predicate argument to add_view ' 

815 'instead. See "Adding A Third Party View, Route, or ' 

816 'Subscriber Predicate" in the "Hooks" chapter of the ' 

817 'documentation for more information.' 

818 ), 

819 DeprecationWarning, 

820 stacklevel=4, 

821 ) 

822 

823 if check_csrf is not None: 

824 warnings.warn( 

825 ( 

826 'The "check_csrf" argument to Configurator.add_view is ' 

827 'deprecated as of Pyramid 1.7. Use the "require_csrf" ' 

828 'option instead or see "Checking CSRF Tokens ' 

829 'Automatically" in the "Sessions" chapter of the ' 

830 'documentation for more information.' 

831 ), 

832 DeprecationWarning, 

833 stacklevel=4, 

834 ) 

835 

836 if accept is not None: 

837 if is_nonstr_iter(accept): 

838 raise ConfigurationError( 

839 'A list is not supported in the "accept" view predicate.' 

840 ) 

841 if '*' in accept: 

842 warnings.warn( 

843 ( 

844 'Passing a media range to the "accept" argument of ' 

845 'Configurator.add_view is deprecated as of ' 

846 'Pyramid 1.10. Use explicit media types to avoid ' 

847 'ambiguities in content negotiation that may impact ' 

848 'your users.' 

849 ), 

850 DeprecationWarning, 

851 stacklevel=4, 

852 ) 

853 # XXX when media ranges are gone, switch allow_range=False 

854 accept = normalize_accept_offer(accept, allow_range=True) 

855 

856 view = self.maybe_dotted(view) 

857 context = self.maybe_dotted(context) 

858 for_ = self.maybe_dotted(for_) 

859 containment = self.maybe_dotted(containment) 

860 mapper = self.maybe_dotted(mapper) 

861 

862 if is_nonstr_iter(decorator): 

863 decorator = combine_decorators(*map(self.maybe_dotted, decorator)) 

864 else: 

865 decorator = self.maybe_dotted(decorator) 

866 

867 if not view: 

868 if renderer: 

869 

870 def view(context, request): 

871 return {} 

872 

873 else: 

874 raise ConfigurationError( 

875 '"view" was not specified and ' 'no "renderer" specified' 

876 ) 

877 

878 if request_type is not None: 

879 request_type = self.maybe_dotted(request_type) 

880 if not IInterface.providedBy(request_type): 

881 raise ConfigurationError( 

882 'request_type must be an interface, not %s' % request_type 

883 ) 

884 

885 if context is None: 

886 context = for_ 

887 

888 isexc = isexception(context) 

889 if exception_only and not isexc: 

890 raise ConfigurationError( 

891 'view "context" must be an exception type when ' 

892 '"exception_only" is True' 

893 ) 

894 

895 r_context = context 

896 if r_context is None: 

897 r_context = Interface 

898 if not IInterface.providedBy(r_context): 

899 r_context = implementedBy(r_context) 

900 

901 if isinstance(renderer, string_types): 

902 renderer = renderers.RendererHelper( 

903 name=renderer, package=self.package, registry=self.registry 

904 ) 

905 

906 introspectables = [] 

907 ovals = view_options.copy() 

908 ovals.update( 

909 dict( 

910 xhr=xhr, 

911 request_method=request_method, 

912 path_info=path_info, 

913 request_param=request_param, 

914 header=header, 

915 accept=accept, 

916 containment=containment, 

917 request_type=request_type, 

918 match_param=match_param, 

919 check_csrf=check_csrf, 

920 custom=predvalseq(custom_predicates), 

921 ) 

922 ) 

923 

924 def discrim_func(): 

925 # We need to defer the discriminator until we know what the phash 

926 # is. It can't be computed any sooner because thirdparty 

927 # predicates/view derivers may not yet exist when add_view is 

928 # called. 

929 predlist = self.get_predlist('view') 

930 valid_predicates = predlist.names() 

931 pvals = {} 

932 dvals = {} 

933 

934 for (k, v) in ovals.items(): 

935 if k in valid_predicates: 

936 pvals[k] = v 

937 else: 

938 dvals[k] = v 

939 

940 self._check_view_options(**dvals) 

941 

942 order, preds, phash = predlist.make(self, **pvals) 

943 

944 view_intr.update( 

945 {'phash': phash, 'order': order, 'predicates': preds} 

946 ) 

947 return ('view', context, name, route_name, phash) 

948 

949 discriminator = Deferred(discrim_func) 

950 

951 if inspect.isclass(view) and attr: 

952 view_desc = 'method %r of %s' % ( 

953 attr, 

954 self.object_description(view), 

955 ) 

956 else: 

957 view_desc = self.object_description(view) 

958 

959 tmpl_intr = None 

960 

961 view_intr = self.introspectable( 

962 'views', discriminator, view_desc, 'view' 

963 ) 

964 view_intr.update( 

965 dict( 

966 name=name, 

967 context=context, 

968 exception_only=exception_only, 

969 containment=containment, 

970 request_param=request_param, 

971 request_methods=request_method, 

972 route_name=route_name, 

973 attr=attr, 

974 xhr=xhr, 

975 accept=accept, 

976 header=header, 

977 path_info=path_info, 

978 match_param=match_param, 

979 check_csrf=check_csrf, 

980 http_cache=http_cache, 

981 require_csrf=require_csrf, 

982 callable=view, 

983 mapper=mapper, 

984 decorator=decorator, 

985 ) 

986 ) 

987 view_intr.update(view_options) 

988 introspectables.append(view_intr) 

989 

990 def register(permission=permission, renderer=renderer): 

991 request_iface = IRequest 

992 if route_name is not None: 

993 request_iface = self.registry.queryUtility( 

994 IRouteRequest, name=route_name 

995 ) 

996 if request_iface is None: 

997 # route configuration should have already happened in 

998 # phase 2 

999 raise ConfigurationError( 

1000 'No route named %s found for view registration' 

1001 % route_name 

1002 ) 

1003 

1004 if renderer is None: 

1005 # use default renderer if one exists (reg'd in phase 1) 

1006 if self.registry.queryUtility(IRendererFactory) is not None: 

1007 renderer = renderers.RendererHelper( 

1008 name=None, package=self.package, registry=self.registry 

1009 ) 

1010 

1011 renderer_type = getattr(renderer, 'type', None) 

1012 intrspc = self.introspector 

1013 if ( 

1014 renderer_type is not None 

1015 and tmpl_intr is not None 

1016 and intrspc is not None 

1017 and intrspc.get('renderer factories', renderer_type) 

1018 is not None 

1019 ): 

1020 # allow failure of registered template factories to be deferred 

1021 # until view execution, like other bad renderer factories; if 

1022 # we tried to relate this to an existing renderer factory 

1023 # without checking if the factory actually existed, we'd end 

1024 # up with a KeyError at startup time, which is inconsistent 

1025 # with how other bad renderer registrations behave (they throw 

1026 # a ValueError at view execution time) 

1027 tmpl_intr.relate('renderer factories', renderer.type) 

1028 

1029 # make a new view separately for normal and exception paths 

1030 if not exception_only: 

1031 derived_view = derive_view(False, renderer) 

1032 register_view(IViewClassifier, request_iface, derived_view) 

1033 if isexc: 

1034 derived_exc_view = derive_view(True, renderer) 

1035 register_view( 

1036 IExceptionViewClassifier, request_iface, derived_exc_view 

1037 ) 

1038 

1039 if exception_only: 

1040 derived_view = derived_exc_view 

1041 

1042 # if there are two derived views, combine them into one for 

1043 # introspection purposes 

1044 if not exception_only and isexc: 

1045 derived_view = runtime_exc_view(derived_view, derived_exc_view) 

1046 

1047 derived_view.__discriminator__ = lambda *arg: discriminator 

1048 # __discriminator__ is used by superdynamic systems 

1049 # that require it for introspection after manual view lookup; 

1050 # see also MultiView.__discriminator__ 

1051 view_intr['derived_callable'] = derived_view 

1052 

1053 self.registry._clear_view_lookup_cache() 

1054 

1055 def derive_view(isexc_only, renderer): 

1056 # added by discrim_func above during conflict resolving 

1057 preds = view_intr['predicates'] 

1058 order = view_intr['order'] 

1059 phash = view_intr['phash'] 

1060 

1061 derived_view = self._derive_view( 

1062 view, 

1063 route_name=route_name, 

1064 permission=permission, 

1065 predicates=preds, 

1066 attr=attr, 

1067 context=context, 

1068 exception_only=isexc_only, 

1069 renderer=renderer, 

1070 wrapper_viewname=wrapper, 

1071 viewname=name, 

1072 accept=accept, 

1073 order=order, 

1074 phash=phash, 

1075 decorator=decorator, 

1076 mapper=mapper, 

1077 http_cache=http_cache, 

1078 require_csrf=require_csrf, 

1079 extra_options=ovals, 

1080 ) 

1081 return derived_view 

1082 

1083 def register_view(classifier, request_iface, derived_view): 

1084 # A multiviews is a set of views which are registered for 

1085 # exactly the same context type/request type/name triad. Each 

1086 # constituent view in a multiview differs only by the 

1087 # predicates which it possesses. 

1088 

1089 # To find a previously registered view for a context 

1090 # type/request type/name triad, we need to use the 

1091 # ``registered`` method of the adapter registry rather than 

1092 # ``lookup``. ``registered`` ignores interface inheritance 

1093 # for the required and provided arguments, returning only a 

1094 # view registered previously with the *exact* triad we pass 

1095 # in. 

1096 

1097 # We need to do this three times, because we use three 

1098 # different interfaces as the ``provided`` interface while 

1099 # doing registrations, and ``registered`` performs exact 

1100 # matches on all the arguments it receives. 

1101 

1102 old_view = None 

1103 order, phash = view_intr['order'], view_intr['phash'] 

1104 registered = self.registry.adapters.registered 

1105 

1106 for view_type in (IView, ISecuredView, IMultiView): 

1107 old_view = registered( 

1108 (classifier, request_iface, r_context), view_type, name 

1109 ) 

1110 if old_view is not None: 

1111 break 

1112 

1113 old_phash = getattr(old_view, '__phash__', DEFAULT_PHASH) 

1114 is_multiview = IMultiView.providedBy(old_view) 

1115 want_multiview = ( 

1116 is_multiview 

1117 # no component was yet registered for exactly this triad 

1118 # or only one was registered but with the same phash, meaning 

1119 # that this view is an override 

1120 or (old_view is not None and old_phash != phash) 

1121 ) 

1122 

1123 if not want_multiview: 

1124 if hasattr(derived_view, '__call_permissive__'): 

1125 view_iface = ISecuredView 

1126 else: 

1127 view_iface = IView 

1128 self.registry.registerAdapter( 

1129 derived_view, 

1130 (classifier, request_iface, context), 

1131 view_iface, 

1132 name, 

1133 ) 

1134 

1135 else: 

1136 # - A view or multiview was already registered for this 

1137 # triad, and the new view is not an override. 

1138 

1139 # XXX we could try to be more efficient here and register 

1140 # a non-secured view for a multiview if none of the 

1141 # multiview's constituent views have a permission 

1142 # associated with them, but this code is getting pretty 

1143 # rough already 

1144 if is_multiview: 

1145 multiview = old_view 

1146 else: 

1147 multiview = MultiView(name) 

1148 old_accept = getattr(old_view, '__accept__', None) 

1149 old_order = getattr(old_view, '__order__', MAX_ORDER) 

1150 # don't bother passing accept_order here as we know we're 

1151 # adding another one right after which will re-sort 

1152 multiview.add(old_view, old_order, old_phash, old_accept) 

1153 accept_order = self.registry.queryUtility(IAcceptOrder) 

1154 multiview.add(derived_view, order, phash, accept, accept_order) 

1155 for view_type in (IView, ISecuredView): 

1156 # unregister any existing views 

1157 self.registry.adapters.unregister( 

1158 (classifier, request_iface, r_context), 

1159 view_type, 

1160 name=name, 

1161 ) 

1162 self.registry.registerAdapter( 

1163 multiview, 

1164 (classifier, request_iface, context), 

1165 IMultiView, 

1166 name=name, 

1167 ) 

1168 

1169 if mapper: 

1170 mapper_intr = self.introspectable( 

1171 'view mappers', 

1172 discriminator, 

1173 'view mapper for %s' % view_desc, 

1174 'view mapper', 

1175 ) 

1176 mapper_intr['mapper'] = mapper 

1177 mapper_intr.relate('views', discriminator) 

1178 introspectables.append(mapper_intr) 

1179 if route_name: 

1180 view_intr.relate('routes', route_name) # see add_route 

1181 if renderer is not None and renderer.name and '.' in renderer.name: 

1182 # the renderer is a template 

1183 tmpl_intr = self.introspectable( 

1184 'templates', discriminator, renderer.name, 'template' 

1185 ) 

1186 tmpl_intr.relate('views', discriminator) 

1187 tmpl_intr['name'] = renderer.name 

1188 tmpl_intr['type'] = renderer.type 

1189 tmpl_intr['renderer'] = renderer 

1190 introspectables.append(tmpl_intr) 

1191 if permission is not None: 

1192 # if a permission exists, register a permission introspectable 

1193 perm_intr = self.introspectable( 

1194 'permissions', permission, permission, 'permission' 

1195 ) 

1196 perm_intr['value'] = permission 

1197 perm_intr.relate('views', discriminator) 

1198 introspectables.append(perm_intr) 

1199 self.action(discriminator, register, introspectables=introspectables) 

1200 

1201 def _check_view_options(self, **kw): 

1202 # we only need to validate deriver options because the predicates 

1203 # were checked by the predlist 

1204 derivers = self.registry.getUtility(IViewDerivers) 

1205 for deriver in derivers.values(): 

1206 for opt in getattr(deriver, 'options', []): 

1207 kw.pop(opt, None) 

1208 if kw: 

1209 raise ConfigurationError('Unknown view options: %s' % (kw,)) 

1210 

1211 def _apply_view_derivers(self, info): 

1212 # These derivers are not really derivers and so have fixed order 

1213 outer_derivers = [ 

1214 ('attr_wrapped_view', attr_wrapped_view), 

1215 ('predicated_view', predicated_view), 

1216 ] 

1217 

1218 view = info.original_view 

1219 derivers = self.registry.getUtility(IViewDerivers) 

1220 for name, deriver in reversed(outer_derivers + derivers.sorted()): 

1221 view = wraps_view(deriver)(view, info) 

1222 return view 

1223 

1224 @action_method 

1225 def add_view_predicate( 

1226 self, name, factory, weighs_more_than=None, weighs_less_than=None 

1227 ): 

1228 """ 

1229 .. versionadded:: 1.4 

1230 

1231 Adds a view predicate factory. The associated view predicate can 

1232 later be named as a keyword argument to 

1233 :meth:`pyramid.config.Configurator.add_view` in the 

1234 ``predicates`` anonyous keyword argument dictionary. 

1235 

1236 ``name`` should be the name of the predicate. It must be a valid 

1237 Python identifier (it will be used as a keyword argument to 

1238 ``add_view`` by others). 

1239 

1240 ``factory`` should be a :term:`predicate factory` or :term:`dotted 

1241 Python name` which refers to a predicate factory. 

1242 

1243 See :ref:`view_and_route_predicates` for more information. 

1244 """ 

1245 self._add_predicate( 

1246 'view', 

1247 name, 

1248 factory, 

1249 weighs_more_than=weighs_more_than, 

1250 weighs_less_than=weighs_less_than, 

1251 ) 

1252 

1253 def add_default_view_predicates(self): 

1254 p = pyramid.predicates 

1255 for (name, factory) in ( 

1256 ('xhr', p.XHRPredicate), 

1257 ('request_method', p.RequestMethodPredicate), 

1258 ('path_info', p.PathInfoPredicate), 

1259 ('request_param', p.RequestParamPredicate), 

1260 ('header', p.HeaderPredicate), 

1261 ('accept', p.AcceptPredicate), 

1262 ('containment', p.ContainmentPredicate), 

1263 ('request_type', p.RequestTypePredicate), 

1264 ('match_param', p.MatchParamPredicate), 

1265 ('check_csrf', p.CheckCSRFTokenPredicate), 

1266 ('physical_path', p.PhysicalPathPredicate), 

1267 ('effective_principals', p.EffectivePrincipalsPredicate), 

1268 ('custom', p.CustomPredicate), 

1269 ): 

1270 self.add_view_predicate(name, factory) 

1271 

1272 def add_default_accept_view_order(self): 

1273 for accept in ( 

1274 'text/html', 

1275 'application/xhtml+xml', 

1276 'application/xml', 

1277 'text/xml', 

1278 'text/plain', 

1279 'application/json', 

1280 ): 

1281 self.add_accept_view_order(accept) 

1282 

1283 @action_method 

1284 def add_accept_view_order( 

1285 self, value, weighs_more_than=None, weighs_less_than=None 

1286 ): 

1287 """ 

1288 Specify an ordering preference for the ``accept`` view option used 

1289 during :term:`view lookup`. 

1290 

1291 By default, if two views have different ``accept`` options and a 

1292 request specifies ``Accept: */*`` or omits the header entirely then 

1293 it is random which view will be selected. This method provides a way 

1294 to specify a server-side, relative ordering between accept media types. 

1295 

1296 ``value`` should be a :term:`media type` as specified by 

1297 :rfc:`7231#section-5.3.2`. For example, ``text/plain;charset=utf8``, 

1298 ``application/json`` or ``text/html``. 

1299 

1300 ``weighs_more_than`` and ``weighs_less_than`` control the ordering 

1301 of media types. Each value may be a string or a list of strings. If 

1302 all options for ``weighs_more_than`` (or ``weighs_less_than``) cannot 

1303 be found, it is an error. 

1304 

1305 Earlier calls to ``add_accept_view_order`` are given higher priority 

1306 over later calls, assuming similar constraints but standard conflict 

1307 resolution mechanisms can be used to override constraints. 

1308 

1309 See :ref:`accept_content_negotiation` for more information. 

1310 

1311 .. versionadded:: 1.10 

1312 

1313 """ 

1314 

1315 def check_type(than): 

1316 than_type, than_subtype, than_params = Accept.parse_offer(than) 

1317 # text/plain vs text/html;charset=utf8 

1318 if bool(offer_params) ^ bool(than_params): 

1319 raise ConfigurationError( 

1320 'cannot compare a media type with params to one without ' 

1321 'params' 

1322 ) 

1323 # text/plain;charset=utf8 vs text/html;charset=utf8 

1324 if offer_params and ( 

1325 offer_subtype != than_subtype or offer_type != than_type 

1326 ): 

1327 raise ConfigurationError( 

1328 'cannot compare params across different media types' 

1329 ) 

1330 

1331 def normalize_types(thans): 

1332 thans = [normalize_accept_offer(than) for than in thans] 

1333 for than in thans: 

1334 check_type(than) 

1335 return thans 

1336 

1337 value = normalize_accept_offer(value) 

1338 offer_type, offer_subtype, offer_params = Accept.parse_offer(value) 

1339 

1340 if weighs_more_than: 

1341 if not is_nonstr_iter(weighs_more_than): 

1342 weighs_more_than = [weighs_more_than] 

1343 weighs_more_than = normalize_types(weighs_more_than) 

1344 

1345 if weighs_less_than: 

1346 if not is_nonstr_iter(weighs_less_than): 

1347 weighs_less_than = [weighs_less_than] 

1348 weighs_less_than = normalize_types(weighs_less_than) 

1349 

1350 discriminator = ('accept view order', value) 

1351 intr = self.introspectable( 

1352 'accept view order', value, value, 'accept view order' 

1353 ) 

1354 intr['value'] = value 

1355 intr['weighs_more_than'] = weighs_more_than 

1356 intr['weighs_less_than'] = weighs_less_than 

1357 

1358 def register(): 

1359 sorter = self.registry.queryUtility(IAcceptOrder) 

1360 if sorter is None: 

1361 sorter = TopologicalSorter() 

1362 self.registry.registerUtility(sorter, IAcceptOrder) 

1363 sorter.add( 

1364 value, value, before=weighs_more_than, after=weighs_less_than 

1365 ) 

1366 

1367 self.action( 

1368 discriminator, 

1369 register, 

1370 introspectables=(intr,), 

1371 order=PHASE1_CONFIG, 

1372 ) # must be registered before add_view 

1373 

1374 @action_method 

1375 def add_view_deriver(self, deriver, name=None, under=None, over=None): 

1376 """ 

1377 .. versionadded:: 1.7 

1378 

1379 Add a :term:`view deriver` to the view pipeline. View derivers are 

1380 a feature used by extension authors to wrap views in custom code 

1381 controllable by view-specific options. 

1382 

1383 ``deriver`` should be a callable conforming to the 

1384 :class:`pyramid.interfaces.IViewDeriver` interface. 

1385 

1386 ``name`` should be the name of the view deriver. There are no 

1387 restrictions on the name of a view deriver. If left unspecified, the 

1388 name will be constructed from the name of the ``deriver``. 

1389 

1390 The ``under`` and ``over`` options can be used to control the ordering 

1391 of view derivers by providing hints about where in the view pipeline 

1392 the deriver is used. Each option may be a string or a list of strings. 

1393 At least one view deriver in each, the over and under directions, must 

1394 exist to fully satisfy the constraints. 

1395 

1396 ``under`` means closer to the user-defined :term:`view callable`, 

1397 and ``over`` means closer to view pipeline ingress. 

1398 

1399 The default value for ``over`` is ``rendered_view`` and ``under`` is 

1400 ``decorated_view``. This places the deriver somewhere between the two 

1401 in the view pipeline. If the deriver should be placed elsewhere in the 

1402 pipeline, such as above ``decorated_view``, then you MUST also specify 

1403 ``under`` to something earlier in the order, or a 

1404 ``CyclicDependencyError`` will be raised when trying to sort the 

1405 derivers. 

1406 

1407 See :ref:`view_derivers` for more information. 

1408 

1409 """ 

1410 deriver = self.maybe_dotted(deriver) 

1411 

1412 if name is None: 

1413 name = deriver.__name__ 

1414 

1415 if name in (INGRESS, VIEW): 

1416 raise ConfigurationError( 

1417 '%s is a reserved view deriver name' % name 

1418 ) 

1419 

1420 if under is None: 

1421 under = 'decorated_view' 

1422 

1423 if over is None: 

1424 over = 'rendered_view' 

1425 

1426 over = as_sorted_tuple(over) 

1427 under = as_sorted_tuple(under) 

1428 

1429 if INGRESS in over: 

1430 raise ConfigurationError('%s cannot be over INGRESS' % name) 

1431 

1432 # ensure everything is always over mapped_view 

1433 if VIEW in over and name != 'mapped_view': 

1434 over = as_sorted_tuple(over + ('mapped_view',)) 

1435 

1436 if VIEW in under: 

1437 raise ConfigurationError('%s cannot be under VIEW' % name) 

1438 if 'mapped_view' in under: 

1439 raise ConfigurationError('%s cannot be under "mapped_view"' % name) 

1440 

1441 discriminator = ('view deriver', name) 

1442 intr = self.introspectable('view derivers', name, name, 'view deriver') 

1443 intr['name'] = name 

1444 intr['deriver'] = deriver 

1445 intr['under'] = under 

1446 intr['over'] = over 

1447 

1448 def register(): 

1449 derivers = self.registry.queryUtility(IViewDerivers) 

1450 if derivers is None: 

1451 derivers = TopologicalSorter( 

1452 default_before=None, 

1453 default_after=INGRESS, 

1454 first=INGRESS, 

1455 last=VIEW, 

1456 ) 

1457 self.registry.registerUtility(derivers, IViewDerivers) 

1458 derivers.add(name, deriver, before=over, after=under) 

1459 

1460 self.action( 

1461 discriminator, 

1462 register, 

1463 introspectables=(intr,), 

1464 order=PHASE1_CONFIG, 

1465 ) # must be registered before add_view 

1466 

1467 def add_default_view_derivers(self): 

1468 d = pyramid.viewderivers 

1469 derivers = [ 

1470 ('secured_view', d.secured_view), 

1471 ('owrapped_view', d.owrapped_view), 

1472 ('http_cached_view', d.http_cached_view), 

1473 ('decorated_view', d.decorated_view), 

1474 ('rendered_view', d.rendered_view), 

1475 ('mapped_view', d.mapped_view), 

1476 ] 

1477 last = INGRESS 

1478 for name, deriver in derivers: 

1479 self.add_view_deriver(deriver, name=name, under=last, over=VIEW) 

1480 last = name 

1481 

1482 # leave the csrf_view loosely coupled to the rest of the pipeline 

1483 # by ensuring nothing in the default pipeline depends on the order 

1484 # of the csrf_view 

1485 self.add_view_deriver( 

1486 d.csrf_view, 

1487 'csrf_view', 

1488 under='secured_view', 

1489 over='owrapped_view', 

1490 ) 

1491 

1492 def derive_view(self, view, attr=None, renderer=None): 

1493 """ 

1494 Create a :term:`view callable` using the function, instance, 

1495 or class (or :term:`dotted Python name` referring to the same) 

1496 provided as ``view`` object. 

1497 

1498 .. warning:: 

1499 

1500 This method is typically only used by :app:`Pyramid` framework 

1501 extension authors, not by :app:`Pyramid` application developers. 

1502 

1503 This is API is useful to framework extenders who create 

1504 pluggable systems which need to register 'proxy' view 

1505 callables for functions, instances, or classes which meet the 

1506 requirements of being a :app:`Pyramid` view callable. For 

1507 example, a ``some_other_framework`` function in another 

1508 framework may want to allow a user to supply a view callable, 

1509 but he may want to wrap the view callable in his own before 

1510 registering the wrapper as a :app:`Pyramid` view callable. 

1511 Because a :app:`Pyramid` view callable can be any of a 

1512 number of valid objects, the framework extender will not know 

1513 how to call the user-supplied object. Running it through 

1514 ``derive_view`` normalizes it to a callable which accepts two 

1515 arguments: ``context`` and ``request``. 

1516 

1517 For example: 

1518 

1519 .. code-block:: python 

1520 

1521 def some_other_framework(user_supplied_view): 

1522 config = Configurator(reg) 

1523 proxy_view = config.derive_view(user_supplied_view) 

1524 def my_wrapper(context, request): 

1525 do_something_that_mutates(request) 

1526 return proxy_view(context, request) 

1527 config.add_view(my_wrapper) 

1528 

1529 The ``view`` object provided should be one of the following: 

1530 

1531 - A function or another non-class callable object that accepts 

1532 a :term:`request` as a single positional argument and which 

1533 returns a :term:`response` object. 

1534 

1535 - A function or other non-class callable object that accepts 

1536 two positional arguments, ``context, request`` and which 

1537 returns a :term:`response` object. 

1538 

1539 - A class which accepts a single positional argument in its 

1540 constructor named ``request``, and which has a ``__call__`` 

1541 method that accepts no arguments that returns a 

1542 :term:`response` object. 

1543 

1544 - A class which accepts two positional arguments named 

1545 ``context, request``, and which has a ``__call__`` method 

1546 that accepts no arguments that returns a :term:`response` 

1547 object. 

1548 

1549 - A :term:`dotted Python name` which refers to any of the 

1550 kinds of objects above. 

1551 

1552 This API returns a callable which accepts the arguments 

1553 ``context, request`` and which returns the result of calling 

1554 the provided ``view`` object. 

1555 

1556 The ``attr`` keyword argument is most useful when the view 

1557 object is a class. It names the method that should be used as 

1558 the callable. If ``attr`` is not provided, the attribute 

1559 effectively defaults to ``__call__``. See 

1560 :ref:`class_as_view` for more information. 

1561 

1562 The ``renderer`` keyword argument should be a renderer 

1563 name. If supplied, it will cause the returned callable to use 

1564 a :term:`renderer` to convert the user-supplied view result to 

1565 a :term:`response` object. If a ``renderer`` argument is not 

1566 supplied, the user-supplied view must itself return a 

1567 :term:`response` object. """ 

1568 return self._derive_view(view, attr=attr, renderer=renderer) 

1569 

1570 # b/w compat 

1571 def _derive_view( 

1572 self, 

1573 view, 

1574 permission=None, 

1575 predicates=(), 

1576 attr=None, 

1577 renderer=None, 

1578 wrapper_viewname=None, 

1579 viewname=None, 

1580 accept=None, 

1581 order=MAX_ORDER, 

1582 phash=DEFAULT_PHASH, 

1583 decorator=None, 

1584 route_name=None, 

1585 mapper=None, 

1586 http_cache=None, 

1587 context=None, 

1588 require_csrf=None, 

1589 exception_only=False, 

1590 extra_options=None, 

1591 ): 

1592 view = self.maybe_dotted(view) 

1593 mapper = self.maybe_dotted(mapper) 

1594 if isinstance(renderer, string_types): 

1595 renderer = renderers.RendererHelper( 

1596 name=renderer, package=self.package, registry=self.registry 

1597 ) 

1598 if renderer is None: 

1599 # use default renderer if one exists 

1600 if self.registry.queryUtility(IRendererFactory) is not None: 

1601 renderer = renderers.RendererHelper( 

1602 name=None, package=self.package, registry=self.registry 

1603 ) 

1604 

1605 options = dict( 

1606 view=view, 

1607 context=context, 

1608 permission=permission, 

1609 attr=attr, 

1610 renderer=renderer, 

1611 wrapper=wrapper_viewname, 

1612 name=viewname, 

1613 accept=accept, 

1614 mapper=mapper, 

1615 decorator=decorator, 

1616 http_cache=http_cache, 

1617 require_csrf=require_csrf, 

1618 route_name=route_name, 

1619 ) 

1620 if extra_options: 

1621 options.update(extra_options) 

1622 

1623 info = ViewDeriverInfo( 

1624 view=view, 

1625 registry=self.registry, 

1626 package=self.package, 

1627 predicates=predicates, 

1628 exception_only=exception_only, 

1629 options=options, 

1630 ) 

1631 

1632 # order and phash are only necessary for the predicated view and 

1633 # are not really view deriver options 

1634 info.order = order 

1635 info.phash = phash 

1636 

1637 return self._apply_view_derivers(info) 

1638 

1639 @viewdefaults 

1640 @action_method 

1641 def add_forbidden_view( 

1642 self, 

1643 view=None, 

1644 attr=None, 

1645 renderer=None, 

1646 wrapper=None, 

1647 route_name=None, 

1648 request_type=None, 

1649 request_method=None, 

1650 request_param=None, 

1651 containment=None, 

1652 xhr=None, 

1653 accept=None, 

1654 header=None, 

1655 path_info=None, 

1656 custom_predicates=(), 

1657 decorator=None, 

1658 mapper=None, 

1659 match_param=None, 

1660 **view_options 

1661 ): 

1662 """ Add a forbidden view to the current configuration state. The 

1663 view will be called when Pyramid or application code raises a 

1664 :exc:`pyramid.httpexceptions.HTTPForbidden` exception and the set of 

1665 circumstances implied by the predicates provided are matched. The 

1666 simplest example is: 

1667 

1668 .. code-block:: python 

1669 

1670 def forbidden(request): 

1671 return Response('Forbidden', status='403 Forbidden') 

1672 

1673 config.add_forbidden_view(forbidden) 

1674 

1675 If ``view`` argument is not provided, the view callable defaults to 

1676 :func:`~pyramid.httpexceptions.default_exceptionresponse_view`. 

1677 

1678 All arguments have the same meaning as 

1679 :meth:`pyramid.config.Configurator.add_view` and each predicate 

1680 argument restricts the set of circumstances under which this forbidden 

1681 view will be invoked. Unlike 

1682 :meth:`pyramid.config.Configurator.add_view`, this method will raise 

1683 an exception if passed ``name``, ``permission``, ``require_csrf``, 

1684 ``context``, ``for_``, or ``exception_only`` keyword arguments. These 

1685 argument values make no sense in the context of a forbidden 

1686 :term:`exception view`. 

1687 

1688 .. versionadded:: 1.3 

1689 

1690 .. versionchanged:: 1.8 

1691 

1692 The view is created using ``exception_only=True``. 

1693 """ 

1694 for arg in ( 

1695 'name', 

1696 'permission', 

1697 'context', 

1698 'for_', 

1699 'require_csrf', 

1700 'exception_only', 

1701 ): 

1702 if arg in view_options: 

1703 raise ConfigurationError( 

1704 '%s may not be used as an argument to add_forbidden_view' 

1705 % (arg,) 

1706 ) 

1707 

1708 if view is None: 

1709 view = default_exceptionresponse_view 

1710 

1711 settings = dict( 

1712 view=view, 

1713 context=HTTPForbidden, 

1714 exception_only=True, 

1715 wrapper=wrapper, 

1716 request_type=request_type, 

1717 request_method=request_method, 

1718 request_param=request_param, 

1719 containment=containment, 

1720 xhr=xhr, 

1721 accept=accept, 

1722 header=header, 

1723 path_info=path_info, 

1724 custom_predicates=custom_predicates, 

1725 decorator=decorator, 

1726 mapper=mapper, 

1727 match_param=match_param, 

1728 route_name=route_name, 

1729 permission=NO_PERMISSION_REQUIRED, 

1730 require_csrf=False, 

1731 attr=attr, 

1732 renderer=renderer, 

1733 ) 

1734 settings.update(view_options) 

1735 return self.add_view(**settings) 

1736 

1737 set_forbidden_view = add_forbidden_view # deprecated sorta-bw-compat alias 

1738 

1739 @viewdefaults 

1740 @action_method 

1741 def add_notfound_view( 

1742 self, 

1743 view=None, 

1744 attr=None, 

1745 renderer=None, 

1746 wrapper=None, 

1747 route_name=None, 

1748 request_type=None, 

1749 request_method=None, 

1750 request_param=None, 

1751 containment=None, 

1752 xhr=None, 

1753 accept=None, 

1754 header=None, 

1755 path_info=None, 

1756 custom_predicates=(), 

1757 decorator=None, 

1758 mapper=None, 

1759 match_param=None, 

1760 append_slash=False, 

1761 **view_options 

1762 ): 

1763 """ Add a default :term:`Not Found View` to the current configuration 

1764 state. The view will be called when Pyramid or application code raises 

1765 an :exc:`pyramid.httpexceptions.HTTPNotFound` exception (e.g., when a 

1766 view cannot be found for the request). The simplest example is: 

1767 

1768 .. code-block:: python 

1769 

1770 def notfound(request): 

1771 return Response('Not Found', status='404 Not Found') 

1772 

1773 config.add_notfound_view(notfound) 

1774 

1775 If ``view`` argument is not provided, the view callable defaults to 

1776 :func:`~pyramid.httpexceptions.default_exceptionresponse_view`. 

1777 

1778 All arguments except ``append_slash`` have the same meaning as 

1779 :meth:`pyramid.config.Configurator.add_view` and each predicate 

1780 argument restricts the set of circumstances under which this notfound 

1781 view will be invoked. Unlike 

1782 :meth:`pyramid.config.Configurator.add_view`, this method will raise 

1783 an exception if passed ``name``, ``permission``, ``require_csrf``, 

1784 ``context``, ``for_``, or ``exception_only`` keyword arguments. These 

1785 argument values make no sense in the context of a Not Found View. 

1786 

1787 If ``append_slash`` is ``True``, when this Not Found View is invoked, 

1788 and the current path info does not end in a slash, the notfound logic 

1789 will attempt to find a :term:`route` that matches the request's path 

1790 info suffixed with a slash. If such a route exists, Pyramid will 

1791 issue a redirect to the URL implied by the route; if it does not, 

1792 Pyramid will return the result of the view callable provided as 

1793 ``view``, as normal. 

1794 

1795 If the argument provided as ``append_slash`` is not a boolean but 

1796 instead implements :class:`~pyramid.interfaces.IResponse`, the 

1797 append_slash logic will behave as if ``append_slash=True`` was passed, 

1798 but the provided class will be used as the response class instead of 

1799 the default :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect` 

1800 response class when a redirect is performed. For example: 

1801 

1802 .. code-block:: python 

1803 

1804 from pyramid.httpexceptions import HTTPMovedPermanently 

1805 config.add_notfound_view(append_slash=HTTPMovedPermanently) 

1806 

1807 The above means that a redirect to a slash-appended route will be 

1808 attempted, but instead of 

1809 :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect` 

1810 being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will 

1811 be used` for the redirect response if a slash-appended route is found. 

1812 

1813 :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect` class is used 

1814 as default response, which is equivalent to 

1815 :class:`~pyramid.httpexceptions.HTTPFound` with addition of redirecting 

1816 with the same HTTP method (useful when doing POST requests). 

1817 

1818 .. versionadded:: 1.3 

1819 

1820 .. versionchanged:: 1.6 

1821 

1822 The ``append_slash`` argument was modified to allow any object that 

1823 implements the ``IResponse`` interface to specify the response class 

1824 used when a redirect is performed. 

1825 

1826 .. versionchanged:: 1.8 

1827 

1828 The view is created using ``exception_only=True``. 

1829 

1830 .. versionchanged: 1.10 

1831 

1832 Default response was changed from 

1833 :class:`~pyramid.httpexceptions.HTTPFound` 

1834 to :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect`. 

1835 

1836 """ 

1837 for arg in ( 

1838 'name', 

1839 'permission', 

1840 'context', 

1841 'for_', 

1842 'require_csrf', 

1843 'exception_only', 

1844 ): 

1845 if arg in view_options: 

1846 raise ConfigurationError( 

1847 '%s may not be used as an argument to add_notfound_view' 

1848 % (arg,) 

1849 ) 

1850 

1851 if view is None: 

1852 view = default_exceptionresponse_view 

1853 

1854 settings = dict( 

1855 view=view, 

1856 context=HTTPNotFound, 

1857 exception_only=True, 

1858 wrapper=wrapper, 

1859 request_type=request_type, 

1860 request_method=request_method, 

1861 request_param=request_param, 

1862 containment=containment, 

1863 xhr=xhr, 

1864 accept=accept, 

1865 header=header, 

1866 path_info=path_info, 

1867 custom_predicates=custom_predicates, 

1868 decorator=decorator, 

1869 mapper=mapper, 

1870 match_param=match_param, 

1871 route_name=route_name, 

1872 permission=NO_PERMISSION_REQUIRED, 

1873 require_csrf=False, 

1874 ) 

1875 settings.update(view_options) 

1876 if append_slash: 

1877 view = self._derive_view(view, attr=attr, renderer=renderer) 

1878 if IResponse.implementedBy(append_slash): 

1879 view = AppendSlashNotFoundViewFactory( 

1880 view, redirect_class=append_slash 

1881 ) 

1882 else: 

1883 view = AppendSlashNotFoundViewFactory(view) 

1884 settings['view'] = view 

1885 else: 

1886 settings['attr'] = attr 

1887 settings['renderer'] = renderer 

1888 return self.add_view(**settings) 

1889 

1890 set_notfound_view = add_notfound_view # deprecated sorta-bw-compat alias 

1891 

1892 @viewdefaults 

1893 @action_method 

1894 def add_exception_view( 

1895 self, 

1896 view=None, 

1897 context=None, 

1898 # force all other arguments to be specified as key=value 

1899 **view_options 

1900 ): 

1901 """ Add an :term:`exception view` for the specified ``exception`` to 

1902 the current configuration state. The view will be called when Pyramid 

1903 or application code raises the given exception. 

1904 

1905 This method accepts almost all of the same arguments as 

1906 :meth:`pyramid.config.Configurator.add_view` except for ``name``, 

1907 ``permission``, ``for_``, ``require_csrf``, and ``exception_only``. 

1908 

1909 By default, this method will set ``context=Exception``, thus 

1910 registering for most default Python exceptions. Any subclass of 

1911 ``Exception`` may be specified. 

1912 

1913 .. versionadded:: 1.8 

1914 """ 

1915 for arg in ( 

1916 'name', 

1917 'for_', 

1918 'exception_only', 

1919 'require_csrf', 

1920 'permission', 

1921 ): 

1922 if arg in view_options: 

1923 raise ConfigurationError( 

1924 '%s may not be used as an argument to add_exception_view' 

1925 % (arg,) 

1926 ) 

1927 if context is None: 

1928 context = Exception 

1929 view_options.update( 

1930 dict( 

1931 view=view, 

1932 context=context, 

1933 exception_only=True, 

1934 permission=NO_PERMISSION_REQUIRED, 

1935 require_csrf=False, 

1936 ) 

1937 ) 

1938 return self.add_view(**view_options) 

1939 

1940 @action_method 

1941 def set_view_mapper(self, mapper): 

1942 """ 

1943 Setting a :term:`view mapper` makes it possible to make use of 

1944 :term:`view callable` objects which implement different call 

1945 signatures than the ones supported by :app:`Pyramid` as described in 

1946 its narrative documentation. 

1947 

1948 The ``mapper`` argument should be an object implementing 

1949 :class:`pyramid.interfaces.IViewMapperFactory` or a :term:`dotted 

1950 Python name` to such an object. The provided ``mapper`` will become 

1951 the default view mapper to be used by all subsequent :term:`view 

1952 configuration` registrations. 

1953 

1954 .. seealso:: 

1955 

1956 See also :ref:`using_a_view_mapper`. 

1957 

1958 .. note:: 

1959 

1960 Using the ``default_view_mapper`` argument to the 

1961 :class:`pyramid.config.Configurator` constructor 

1962 can be used to achieve the same purpose. 

1963 """ 

1964 mapper = self.maybe_dotted(mapper) 

1965 

1966 def register(): 

1967 self.registry.registerUtility(mapper, IViewMapperFactory) 

1968 

1969 # IViewMapperFactory is looked up as the result of view config 

1970 # in phase 3 

1971 intr = self.introspectable( 

1972 'view mappers', 

1973 IViewMapperFactory, 

1974 self.object_description(mapper), 

1975 'default view mapper', 

1976 ) 

1977 intr['mapper'] = mapper 

1978 self.action( 

1979 IViewMapperFactory, 

1980 register, 

1981 order=PHASE1_CONFIG, 

1982 introspectables=(intr,), 

1983 ) 

1984 

1985 @action_method 

1986 def add_static_view(self, name, path, **kw): 

1987 """ Add a view used to render static assets such as images 

1988 and CSS files. 

1989 

1990 The ``name`` argument is a string representing an 

1991 application-relative local URL prefix. It may alternately be a full 

1992 URL. 

1993 

1994 The ``path`` argument is the path on disk where the static files 

1995 reside. This can be an absolute path, a package-relative path, or a 

1996 :term:`asset specification`. 

1997 

1998 The ``cache_max_age`` keyword argument is input to set the 

1999 ``Expires`` and ``Cache-Control`` headers for static assets served. 

2000 Note that this argument has no effect when the ``name`` is a *url 

2001 prefix*. By default, this argument is ``None``, meaning that no 

2002 particular Expires or Cache-Control headers are set in the response. 

2003 

2004 The ``permission`` keyword argument is used to specify the 

2005 :term:`permission` required by a user to execute the static view. By 

2006 default, it is the string 

2007 :data:`pyramid.security.NO_PERMISSION_REQUIRED`, a special sentinel 

2008 which indicates that, even if a :term:`default permission` exists for 

2009 the current application, the static view should be renderered to 

2010 completely anonymous users. This default value is permissive 

2011 because, in most web apps, static assets seldom need protection from 

2012 viewing. If ``permission`` is specified, the security checking will 

2013 be performed against the default root factory ACL. 

2014 

2015 Any other keyword arguments sent to ``add_static_view`` are passed on 

2016 to :meth:`pyramid.config.Configurator.add_route` (e.g. ``factory``, 

2017 perhaps to define a custom factory with a custom ACL for this static 

2018 view). 

2019 

2020 *Usage* 

2021 

2022 The ``add_static_view`` function is typically used in conjunction 

2023 with the :meth:`pyramid.request.Request.static_url` method. 

2024 ``add_static_view`` adds a view which renders a static asset when 

2025 some URL is visited; :meth:`pyramid.request.Request.static_url` 

2026 generates a URL to that asset. 

2027 

2028 The ``name`` argument to ``add_static_view`` is usually a simple URL 

2029 prefix (e.g. ``'images'``). When this is the case, the 

2030 :meth:`pyramid.request.Request.static_url` API will generate a URL 

2031 which points to a Pyramid view, which will serve up a set of assets 

2032 that live in the package itself. For example: 

2033 

2034 .. code-block:: python 

2035 

2036 add_static_view('images', 'mypackage:images/') 

2037 

2038 Code that registers such a view can generate URLs to the view via 

2039 :meth:`pyramid.request.Request.static_url`: 

2040 

2041 .. code-block:: python 

2042 

2043 request.static_url('mypackage:images/logo.png') 

2044 

2045 When ``add_static_view`` is called with a ``name`` argument that 

2046 represents a URL prefix, as it is above, subsequent calls to 

2047 :meth:`pyramid.request.Request.static_url` with paths that start with 

2048 the ``path`` argument passed to ``add_static_view`` will generate a 

2049 URL something like ``http://<Pyramid app URL>/images/logo.png``, 

2050 which will cause the ``logo.png`` file in the ``images`` subdirectory 

2051 of the ``mypackage`` package to be served. 

2052 

2053 ``add_static_view`` can alternately be used with a ``name`` argument 

2054 which is a *URL*, causing static assets to be served from an external 

2055 webserver. This happens when the ``name`` argument is a fully 

2056 qualified URL (e.g. starts with ``http://`` or similar). In this 

2057 mode, the ``name`` is used as the prefix of the full URL when 

2058 generating a URL using :meth:`pyramid.request.Request.static_url`. 

2059 Furthermore, if a protocol-relative URL (e.g. ``//example.com/images``) 

2060 is used as the ``name`` argument, the generated URL will use the 

2061 protocol of the request (http or https, respectively). 

2062 

2063 For example, if ``add_static_view`` is called like so: 

2064 

2065 .. code-block:: python 

2066 

2067 add_static_view('http://example.com/images', 'mypackage:images/') 

2068 

2069 Subsequently, the URLs generated by 

2070 :meth:`pyramid.request.Request.static_url` for that static view will 

2071 be prefixed with ``http://example.com/images`` (the external webserver 

2072 listening on ``example.com`` must be itself configured to respond 

2073 properly to such a request.): 

2074 

2075 .. code-block:: python 

2076 

2077 static_url('mypackage:images/logo.png', request) 

2078 

2079 See :ref:`static_assets_section` for more information. 

2080 """ 

2081 spec = self._make_spec(path) 

2082 info = self._get_static_info() 

2083 info.add(self, name, spec, **kw) 

2084 

2085 def add_cache_buster(self, path, cachebust, explicit=False): 

2086 """ 

2087 Add a cache buster to a set of files on disk. 

2088 

2089 The ``path`` should be the path on disk where the static files 

2090 reside. This can be an absolute path, a package-relative path, or a 

2091 :term:`asset specification`. 

2092 

2093 The ``cachebust`` argument may be set to cause 

2094 :meth:`~pyramid.request.Request.static_url` to use cache busting when 

2095 generating URLs. See :ref:`cache_busting` for general information 

2096 about cache busting. The value of the ``cachebust`` argument must 

2097 be an object which implements 

2098 :class:`~pyramid.interfaces.ICacheBuster`. 

2099 

2100 If ``explicit`` is set to ``True`` then the ``path`` for the cache 

2101 buster will be matched based on the ``rawspec`` instead of the 

2102 ``pathspec`` as defined in the 

2103 :class:`~pyramid.interfaces.ICacheBuster` interface. 

2104 Default: ``False``. 

2105 

2106 """ 

2107 spec = self._make_spec(path) 

2108 info = self._get_static_info() 

2109 info.add_cache_buster(self, spec, cachebust, explicit=explicit) 

2110 

2111 def _get_static_info(self): 

2112 info = self.registry.queryUtility(IStaticURLInfo) 

2113 if info is None: 

2114 info = StaticURLInfo() 

2115 self.registry.registerUtility(info, IStaticURLInfo) 

2116 return info 

2117 

2118 

2119def isexception(o): 

2120 if IInterface.providedBy(o): 

2121 if IException.isEqualOrExtendedBy(o): 

2122 return True 

2123 return isinstance(o, Exception) or ( 

2124 inspect.isclass(o) and (issubclass(o, Exception)) 

2125 ) 

2126 

2127 

2128def runtime_exc_view(view, excview): 

2129 # create a view callable which can pretend to be both a normal view 

2130 # and an exception view, dispatching to the appropriate one based 

2131 # on the state of request.exception 

2132 def wrapper_view(context, request): 

2133 if getattr(request, 'exception', None): 

2134 return excview(context, request) 

2135 return view(context, request) 

2136 

2137 # these constants are the same between the two views 

2138 wrapper_view.__wraps__ = wrapper_view 

2139 wrapper_view.__original_view__ = getattr(view, '__original_view__', view) 

2140 wrapper_view.__module__ = view.__module__ 

2141 wrapper_view.__doc__ = view.__doc__ 

2142 wrapper_view.__name__ = view.__name__ 

2143 

2144 wrapper_view.__accept__ = getattr(view, '__accept__', None) 

2145 wrapper_view.__order__ = getattr(view, '__order__', MAX_ORDER) 

2146 wrapper_view.__phash__ = getattr(view, '__phash__', DEFAULT_PHASH) 

2147 wrapper_view.__view_attr__ = getattr(view, '__view_attr__', None) 

2148 wrapper_view.__permission__ = getattr(view, '__permission__', None) 

2149 

2150 def wrap_fn(attr): 

2151 def wrapper(context, request): 

2152 if getattr(request, 'exception', None): 

2153 selected_view = excview 

2154 else: 

2155 selected_view = view 

2156 fn = getattr(selected_view, attr, None) 

2157 if fn is not None: 

2158 return fn(context, request) 

2159 

2160 return wrapper 

2161 

2162 # these methods are dynamic per-request and should dispatch to their 

2163 # respective views based on whether it's an exception or not 

2164 wrapper_view.__call_permissive__ = wrap_fn('__call_permissive__') 

2165 wrapper_view.__permitted__ = wrap_fn('__permitted__') 

2166 wrapper_view.__predicated__ = wrap_fn('__predicated__') 

2167 wrapper_view.__predicates__ = wrap_fn('__predicates__') 

2168 return wrapper_view 

2169 

2170 

2171@implementer(IViewDeriverInfo) 

2172class ViewDeriverInfo(object): 

2173 def __init__( 

2174 self, view, registry, package, predicates, exception_only, options 

2175 ): 

2176 self.original_view = view 

2177 self.registry = registry 

2178 self.package = package 

2179 self.predicates = predicates or [] 

2180 self.options = options or {} 

2181 self.exception_only = exception_only 

2182 

2183 @reify 

2184 def settings(self): 

2185 return self.registry.settings 

2186 

2187 

2188@implementer(IStaticURLInfo) 

2189class StaticURLInfo(object): 

2190 def __init__(self): 

2191 self.registrations = [] 

2192 self.cache_busters = [] 

2193 

2194 def generate(self, path, request, **kw): 

2195 for (url, spec, route_name) in self.registrations: 

2196 if path.startswith(spec): 

2197 subpath = path[len(spec) :] 

2198 if WIN: # pragma: no cover 

2199 subpath = subpath.replace('\\', '/') # windows 

2200 if self.cache_busters: 

2201 subpath, kw = self._bust_asset_path( 

2202 request, spec, subpath, kw 

2203 ) 

2204 if url is None: 

2205 kw['subpath'] = subpath 

2206 return request.route_url(route_name, **kw) 

2207 else: 

2208 app_url, qs, anchor = parse_url_overrides(request, kw) 

2209 parsed = url_parse(url) 

2210 if not parsed.scheme: 

2211 url = urlparse.urlunparse( 

2212 parsed._replace( 

2213 scheme=request.environ['wsgi.url_scheme'] 

2214 ) 

2215 ) 

2216 subpath = url_quote(subpath) 

2217 result = urljoin(url, subpath) 

2218 return result + qs + anchor 

2219 

2220 raise ValueError('No static URL definition matching %s' % path) 

2221 

2222 def add(self, config, name, spec, **extra): 

2223 # This feature only allows for the serving of a directory and 

2224 # the files contained within, not of a single asset; 

2225 # appending a slash here if the spec doesn't have one is 

2226 # required for proper prefix matching done in ``generate`` 

2227 # (``subpath = path[len(spec):]``). 

2228 if os.path.isabs(spec): # FBO windows 

2229 sep = os.sep 

2230 else: 

2231 sep = '/' 

2232 if not spec.endswith(sep) and not spec.endswith(':'): 

2233 spec = spec + sep 

2234 

2235 # we also make sure the name ends with a slash, purely as a 

2236 # convenience: a name that is a url is required to end in a 

2237 # slash, so that ``urljoin(name, subpath))`` will work above 

2238 # when the name is a URL, and it doesn't hurt things for it to 

2239 # have a name that ends in a slash if it's used as a route 

2240 # name instead of a URL. 

2241 if not name.endswith('/'): 

2242 # make sure it ends with a slash 

2243 name = name + '/' 

2244 

2245 if url_parse(name).netloc: 

2246 # it's a URL 

2247 # url, spec, route_name 

2248 url = name 

2249 route_name = None 

2250 else: 

2251 # it's a view name 

2252 url = None 

2253 cache_max_age = extra.pop('cache_max_age', None) 

2254 

2255 # create a view 

2256 view = static_view( 

2257 spec, cache_max_age=cache_max_age, use_subpath=True 

2258 ) 

2259 

2260 # Mutate extra to allow factory, etc to be passed through here. 

2261 # Treat permission specially because we'd like to default to 

2262 # permissiveness (see docs of config.add_static_view). 

2263 permission = extra.pop('permission', None) 

2264 if permission is None: 

2265 permission = NO_PERMISSION_REQUIRED 

2266 

2267 context = extra.pop('context', None) 

2268 if context is None: 

2269 context = extra.pop('for_', None) 

2270 

2271 renderer = extra.pop('renderer', None) 

2272 

2273 # register a route using the computed view, permission, and 

2274 # pattern, plus any extras passed to us via add_static_view 

2275 pattern = "%s*subpath" % name # name already ends with slash 

2276 if config.route_prefix: 

2277 route_name = '__%s/%s' % (config.route_prefix, name) 

2278 else: 

2279 route_name = '__%s' % name 

2280 config.add_route(route_name, pattern, **extra) 

2281 config.add_view( 

2282 route_name=route_name, 

2283 view=view, 

2284 permission=permission, 

2285 context=context, 

2286 renderer=renderer, 

2287 ) 

2288 

2289 def register(): 

2290 registrations = self.registrations 

2291 

2292 names = [t[0] for t in registrations] 

2293 

2294 if name in names: 

2295 idx = names.index(name) 

2296 registrations.pop(idx) 

2297 

2298 # url, spec, route_name 

2299 registrations.append((url, spec, route_name)) 

2300 

2301 intr = config.introspectable( 

2302 'static views', name, 'static view for %r' % name, 'static view' 

2303 ) 

2304 intr['name'] = name 

2305 intr['spec'] = spec 

2306 

2307 config.action(None, callable=register, introspectables=(intr,)) 

2308 

2309 def add_cache_buster(self, config, spec, cachebust, explicit=False): 

2310 # ensure the spec always has a trailing slash as we only support 

2311 # adding cache busters to folders, not files 

2312 if os.path.isabs(spec): # FBO windows 

2313 sep = os.sep 

2314 else: 

2315 sep = '/' 

2316 if not spec.endswith(sep) and not spec.endswith(':'): 

2317 spec = spec + sep 

2318 

2319 def register(): 

2320 if config.registry.settings.get('pyramid.prevent_cachebust'): 

2321 return 

2322 

2323 cache_busters = self.cache_busters 

2324 

2325 # find duplicate cache buster (old_idx) 

2326 # and insertion location (new_idx) 

2327 new_idx, old_idx = len(cache_busters), None 

2328 for idx, (spec_, cb_, explicit_) in enumerate(cache_busters): 

2329 # if we find an identical (spec, explicit) then use it 

2330 if spec == spec_ and explicit == explicit_: 

2331 old_idx = new_idx = idx 

2332 break 

2333 

2334 # past all explicit==False specs then add to the end 

2335 elif not explicit and explicit_: 

2336 new_idx = idx 

2337 break 

2338 

2339 # explicit matches and spec is shorter 

2340 elif explicit == explicit_ and len(spec) < len(spec_): 

2341 new_idx = idx 

2342 break 

2343 

2344 if old_idx is not None: 

2345 cache_busters.pop(old_idx) 

2346 

2347 cache_busters.insert(new_idx, (spec, cachebust, explicit)) 

2348 

2349 intr = config.introspectable( 

2350 'cache busters', spec, 'cache buster for %r' % spec, 'cache buster' 

2351 ) 

2352 intr['cachebust'] = cachebust 

2353 intr['path'] = spec 

2354 intr['explicit'] = explicit 

2355 

2356 config.action(None, callable=register, introspectables=(intr,)) 

2357 

2358 def _bust_asset_path(self, request, spec, subpath, kw): 

2359 registry = request.registry 

2360 pkg_name, pkg_subpath = resolve_asset_spec(spec) 

2361 rawspec = None 

2362 

2363 if pkg_name is not None: 

2364 pathspec = '{0}:{1}{2}'.format(pkg_name, pkg_subpath, subpath) 

2365 overrides = registry.queryUtility(IPackageOverrides, name=pkg_name) 

2366 if overrides is not None: 

2367 resource_name = posixpath.join(pkg_subpath, subpath) 

2368 sources = overrides.filtered_sources(resource_name) 

2369 for source, filtered_path in sources: 

2370 rawspec = source.get_path(filtered_path) 

2371 if hasattr(source, 'pkg_name'): 

2372 rawspec = '{0}:{1}'.format(source.pkg_name, rawspec) 

2373 break 

2374 

2375 else: 

2376 pathspec = pkg_subpath + subpath 

2377 

2378 if rawspec is None: 

2379 rawspec = pathspec 

2380 

2381 kw['pathspec'] = pathspec 

2382 kw['rawspec'] = rawspec 

2383 for spec_, cachebust, explicit in reversed(self.cache_busters): 

2384 if (explicit and rawspec.startswith(spec_)) or ( 

2385 not explicit and pathspec.startswith(spec_) 

2386 ): 

2387 subpath, kw = cachebust(request, subpath, kw) 

2388 break 

2389 return subpath, kw