Michael Merickel
2018-10-15 2b024920847481592b1a13d4006d2a9fa8881d72
commit | author | age
a7e625 1 """
CM 2 HTTP Exceptions
3 ---------------
4
6b0889 5 This module contains Pyramid HTTP exception classes.  Each class relates to a
MM 6 single HTTP status code.  Each class is a subclass of the
7 :class:`~HTTPException`.  Each exception class is also a :term:`response`
8 object.
a7e625 9
56cbb5 10 Each exception class has a status code according to :rfc:`2068` or :rfc:`7538`:
55241c 11 codes with 100-300 are not really errors; 400s are client errors,
TL 12 and 500s are server errors.
a7e625 13
CM 14 Exception
15   HTTPException
c5ed54 16     HTTPSuccessful
a7e625 17       * 200 - HTTPOk
CM 18       * 201 - HTTPCreated
19       * 202 - HTTPAccepted
20       * 203 - HTTPNonAuthoritativeInformation
21       * 204 - HTTPNoContent
22       * 205 - HTTPResetContent
23       * 206 - HTTPPartialContent
24     HTTPRedirection
25       * 300 - HTTPMultipleChoices
26       * 301 - HTTPMovedPermanently
27       * 302 - HTTPFound
28       * 303 - HTTPSeeOther
29       * 304 - HTTPNotModified
30       * 305 - HTTPUseProxy
31       * 307 - HTTPTemporaryRedirect
56cbb5 32       * 308 - HTTPPermanentRedirect
a7e625 33     HTTPError
CM 34       HTTPClientError
35         * 400 - HTTPBadRequest
36         * 401 - HTTPUnauthorized
37         * 402 - HTTPPaymentRequired
38         * 403 - HTTPForbidden
39         * 404 - HTTPNotFound
40         * 405 - HTTPMethodNotAllowed
41         * 406 - HTTPNotAcceptable
42         * 407 - HTTPProxyAuthenticationRequired
43         * 408 - HTTPRequestTimeout
44         * 409 - HTTPConflict
45         * 410 - HTTPGone
46         * 411 - HTTPLengthRequired
47         * 412 - HTTPPreconditionFailed
48         * 413 - HTTPRequestEntityTooLarge
49         * 414 - HTTPRequestURITooLong
50         * 415 - HTTPUnsupportedMediaType
51         * 416 - HTTPRequestRangeNotSatisfiable
52         * 417 - HTTPExpectationFailed
596860 53         * 422 - HTTPUnprocessableEntity
MM 54         * 423 - HTTPLocked
55         * 424 - HTTPFailedDependency
36d5a4 56         * 428 - HTTPPreconditionRequired
H 57         * 429 - HTTPTooManyRequests
58         * 431 - HTTPRequestHeaderFieldsTooLarge
a7e625 59       HTTPServerError
CM 60         * 500 - HTTPInternalServerError
61         * 501 - HTTPNotImplemented
62         * 502 - HTTPBadGateway
63         * 503 - HTTPServiceUnavailable
64         * 504 - HTTPGatewayTimeout
65         * 505 - HTTPVersionNotSupported
596860 66         * 507 - HTTPInsufficientStorage
a7e625 67
5bcab6 68 HTTP exceptions are also :term:`response` objects, thus they accept most of
MM 69 the same parameters that can be passed to a regular
70 :class:`~pyramid.response.Response`. Each HTTP exception also has the
71 following attributes:
a7e625 72
CM 73    ``code``
74        the HTTP status code for the exception
75
76    ``title``
77        remainder of the status line (stuff after the code)
78
79    ``explanation``
80        a plain-text explanation of the error message that is
81        not subject to environment or header substitutions;
82        it is accessible in the template via ${explanation}
83
84    ``detail``
85        a plain-text message customization that is not subject
86        to environment or header substitutions; accessible in
87        the template via ${detail}
88
89    ``body_template``
90        a ``String.template``-format content fragment used for environment
91        and header substitution; the default template includes both
92        the explanation and further detail provided in the
93        message.
94
5bcab6 95 Each HTTP exception accepts the following parameters, any others will
MM 96 be forwarded to its :class:`~pyramid.response.Response` superclass:
a7e625 97
CM 98    ``detail``
99      a plain-text override of the default ``detail``
100
101    ``headers``
24e1b3 102      a list of (k,v) header pairs, or a dict, to be added to the
MM 103      response; use the content_type='application/json' kwarg and other
104      similar kwargs to to change properties of the response supported by the
105      :class:`pyramid.response.Response` superclass
a7e625 106
CM 107    ``comment``
108      a plain-text additional information which is
109      usually stripped/hidden for end-users
110
111    ``body_template``
112      a ``string.Template`` object containing a content fragment in HTML
113      that frames the explanation and further detail
114
5bcab6 115    ``body``
MM 116      a string that will override the ``body_template`` and be used as the
117      body of the response.
118
a7e625 119 Substitution of response headers into template values is always performed.
CM 120 Substitution of WSGI environment values is performed if a ``request`` is
121 passed to the exception's constructor.
122
25c64c 123 The subclasses of :class:`~_HTTPMove`
a7e625 124 (:class:`~HTTPMultipleChoices`, :class:`~HTTPMovedPermanently`,
56cbb5 125 :class:`~HTTPFound`, :class:`~HTTPSeeOther`, :class:`~HTTPUseProxy`,
JW 126 :class:`~HTTPTemporaryRedirect`, and :class: `~HTTPPermanentRedirect) are
127 redirections that require a ``Location`` field. Reflecting this, these
128 subclasses have one additional keyword argument: ``location``,
129 which indicates the location to which to redirect.
a7e625 130 """
cb67e0 131 import json
a7e625 132
99edc5 133 from string import Template
CM 134
3b7334 135 from zope.interface import implementer
99edc5 136
CM 137 from webob import html_escape as _html_escape
12a58d 138 from webob.acceptparse import create_accept_header
99edc5 139
0c1c39 140 from pyramid.compat import (
CM 141     class_types,
142     text_type,
143     binary_type,
144     text_,
145     )
146
99edc5 147 from pyramid.interfaces import IExceptionResponse
CM 148 from pyramid.response import Response
149
150 def _no_escape(value):
151     if value is None:
152         return ''
8e606d 153     if not isinstance(value, text_type):
99edc5 154         if hasattr(value, '__unicode__'):
8e606d 155             value = value.__unicode__()
CM 156         if isinstance(value, binary_type):
157             value = text_(value, 'utf-8')
99edc5 158         else:
8e606d 159             value = text_type(value)
99edc5 160     return value
CM 161
3b7334 162 @implementer(IExceptionResponse)
97ed56 163 class HTTPException(Response, Exception):
99edc5 164
CM 165     ## You should set in subclasses:
166     # code = 200
167     # title = 'OK'
168     # explanation = 'why this happens'
169     # body_template_obj = Template('response template')
88fcdc 170     #
BJR 171     # This class itself uses the error code "520" with the error message/title
172     # of "Unknown Error". This is not an RFC standard, however it is
173     # implemented in practice. Sub-classes should be overriding the default
174     # values and 520 should not be seen in the wild from Pyramid applications.
175     # Due to changes in WebOb, a code of "None" is not valid, and WebOb due to
176     # more strict error checking rejects it now.
99edc5 177
CM 178     # differences from webob.exc.WSGIHTTPException:
179     #
180     # - doesn't use "strip_tags" (${br} placeholder for <br/>, no other html
181     #   in default body template)
182     #
d0a5f0 183     # - __call__ never generates a new Response, it always mutates self
99edc5 184     #
CM 185     # - explicitly sets self.message = detail to prevent whining by Python
186     #   2.6.5+ access of Exception.message
187     #
188     # - its base class of HTTPException is no longer a Python 2.4 compatibility
189     #   shim; it's purely a base class that inherits from Exception.  This
190     #   implies that this class' ``exception`` property always returns
d0a5f0 191     #   ``self`` (it exists only for bw compat at this point).
99edc5 192     #
CM 193     # - documentation improvements (Pyramid-specific docstrings where necessary)
194     #
88fcdc 195     code = 520
BJR 196     title = 'Unknown Error'
99edc5 197     explanation = ''
CM 198     body_template_obj = Template('''\
199 ${explanation}${br}${br}
200 ${detail}
201 ${html_comment}
202 ''')
203
204     plain_template_obj = Template('''\
205 ${status}
206
207 ${body}''')
208
209     html_template_obj = Template('''\
210 <html>
211  <head>
212   <title>${status}</title>
213  </head>
214  <body>
215   <h1>${status}</h1>
216   ${body}
217  </body>
218 </html>''')
219
220     ## Set this to True for responses that should have no request body
221     empty_body = False
222
223     def __init__(self, detail=None, headers=None, comment=None,
cb67e0 224                  body_template=None, json_formatter=None, **kw):
99edc5 225         status = '%s %s' % (self.code, self.title)
CM 226         Response.__init__(self, status=status, **kw)
227         Exception.__init__(self, detail)
228         self.detail = self.message = detail
229         if headers:
230             self.headers.extend(headers)
231         self.comment = comment
232         if body_template is not None:
233             self.body_template = body_template
234             self.body_template_obj = Template(body_template)
cb67e0 235         if json_formatter is not None:
BJR 236             self._json_formatter = json_formatter
99edc5 237
CM 238         if self.empty_body:
239             del self.content_type
240             del self.content_length
241
242     def __str__(self):
309e6b 243         return str(self.detail) if self.detail else self.explanation
99edc5 244
cb67e0 245     def _json_formatter(self, status, body, title, environ):
BJR 246         return {'message': body,
247                 'code': status,
248                 'title': self.title}
249
85093d 250     def prepare(self, environ):
80865c 251         if not self.has_body and not self.empty_body:
85093d 252             html_comment = ''
CM 253             comment = self.comment or ''
cb67e0 254             accept_value = environ.get('HTTP_ACCEPT', '')
12a58d 255             accept = create_accept_header(accept_value)
80f982 256             # Attempt to match text/html or application/json, if those don't
bee098 257             # match, we will fall through to defaulting to text/plain
12a58d 258             acceptable = accept.acceptable_offers(['text/html', 'application/json'])
BJR 259             acceptable = [offer[0] for offer in acceptable] + ['text/plain']
260             match = acceptable[0]
cb67e0 261
BJR 262             if match == 'text/html':
85093d 263                 self.content_type = 'text/html'
CM 264                 escape = _html_escape
265                 page_template = self.html_template_obj
266                 br = '<br/>'
267                 if comment:
268                     html_comment = '<!-- %s -->' % escape(comment)
cb67e0 269             elif match == 'application/json':
BJR 270                 self.content_type = 'application/json'
f16e6e 271                 self.charset = None
cb67e0 272                 escape = _no_escape
BJR 273                 br = '\n'
274                 if comment:
275                     html_comment = escape(comment)
276
277                 class JsonPageTemplate(object):
278                     def __init__(self, excobj):
279                         self.excobj = excobj
280
281                     def substitute(self, status, body):
282                         jsonbody = self.excobj._json_formatter(
283                             status=status,
284                             body=body, title=self.excobj.title,
285                             environ=environ)
286                         return json.dumps(jsonbody)
287
288                 page_template = JsonPageTemplate(self)
85093d 289             else:
CM 290                 self.content_type = 'text/plain'
291                 escape = _no_escape
292                 page_template = self.plain_template_obj
293                 br = '\n'
294                 if comment:
295                     html_comment = escape(comment)
296             args = {
cb67e0 297                 'br': br,
85093d 298                 'explanation': escape(self.explanation),
CM 299                 'detail': escape(self.detail or ''),
300                 'comment': escape(comment),
cb67e0 301                 'html_comment': html_comment,
85093d 302                 }
CM 303             body_tmpl = self.body_template_obj
97ed56 304             if HTTPException.body_template_obj is not body_tmpl:
85093d 305                 # Custom template; add headers to args
CM 306                 for k, v in environ.items():
307                     if (not k.startswith('wsgi.')) and ('.' in k):
308                         # omit custom environ variables, stringifying them may
309                         # trigger code that should not be executed here; see
310                         # https://github.com/Pylons/pyramid/issues/239
311                         continue
312                     args[k] = escape(v)
313                 for k, v in self.headers.items():
314                     args[k.lower()] = escape(v)
315             body = body_tmpl.substitute(args)
316             page = page_template.substitute(status=self.status, body=body)
8e606d 317             if isinstance(page, text_type):
f16e6e 318                 page = page.encode(self.charset if self.charset else 'UTF-8')
85093d 319             self.app_iter = [page]
CM 320             self.body = page
99edc5 321
CM 322     @property
53d11e 323     def wsgi_response(self):
99edc5 324         # bw compat only
CM 325         return self
53d11e 326
CM 327     exception = wsgi_response # bw compat only
328
329     def __call__(self, environ, start_response):
d0a5f0 330         # differences from webob.exc.WSGIHTTPException
CM 331         #
332         # - does not try to deal with HEAD requests
333         #
334         # - does not manufacture a new response object when generating
335         #   the default response
336         #
85093d 337         self.prepare(environ)
53d11e 338         return Response.__call__(self, environ, start_response)
99edc5 339
97ed56 340 WSGIHTTPException = HTTPException # b/c post 1.5
CM 341
342 class HTTPError(HTTPException):
99edc5 343     """
8b1057 344     base class for exceptions with status codes in the 400s and 500s
99edc5 345
CM 346     This is an exception which indicates that an error has occurred,
25c64c 347     and that any work in progress should not be committed.
99edc5 348     """
CM 349
97ed56 350 class HTTPRedirection(HTTPException):
99edc5 351     """
8b1057 352     base class for exceptions with status codes in the 300s (redirections)
99edc5 353
CM 354     This is an abstract base class for 3xx redirection.  It indicates
355     that further action needs to be taken by the user agent in order
356     to fulfill the request.  It does not necessarly signal an error
357     condition.
358     """
359
c5ed54 360 class HTTPSuccessful(HTTPException):
CM 361     """
362     Base class for exceptions with status codes in the 200s (successful
363     responses)
364     """
365
366 ############################################################
367 ## 2xx success
368 ############################################################
369
370 class HTTPOk(HTTPSuccessful):
99edc5 371     """
0c93e5 372     subclass of :class:`~HTTPSuccessful`
CM 373
23f1ff 374     Indicates that the request has succeeded.
25c64c 375
99edc5 376     code: 200, title: OK
CM 377     """
378     code = 200
379     title = 'OK'
380
c5ed54 381 class HTTPCreated(HTTPSuccessful):
99edc5 382     """
6926fa 383     subclass of :class:`~HTTPSuccessful`
99edc5 384
CM 385     This indicates that request has been fulfilled and resulted in a new
386     resource being created.
25c64c 387
99edc5 388     code: 201, title: Created
CM 389     """
390     code = 201
391     title = 'Created'
392
c5ed54 393 class HTTPAccepted(HTTPSuccessful):
99edc5 394     """
6926fa 395     subclass of :class:`~HTTPSuccessful`
99edc5 396
CM 397     This indicates that the request has been accepted for processing, but the
398     processing has not been completed.
399
400     code: 202, title: Accepted
401     """
402     code = 202
403     title = 'Accepted'
404     explanation = 'The request is accepted for processing.'
405
c5ed54 406 class HTTPNonAuthoritativeInformation(HTTPSuccessful):
99edc5 407     """
6926fa 408     subclass of :class:`~HTTPSuccessful`
99edc5 409
CM 410     This indicates that the returned metainformation in the entity-header is
411     not the definitive set as available from the origin server, but is
412     gathered from a local or a third-party copy.
413
414     code: 203, title: Non-Authoritative Information
415     """
416     code = 203
417     title = 'Non-Authoritative Information'
418
c5ed54 419 class HTTPNoContent(HTTPSuccessful):
99edc5 420     """
6926fa 421     subclass of :class:`~HTTPSuccessful`
99edc5 422
CM 423     This indicates that the server has fulfilled the request but does
424     not need to return an entity-body, and might want to return updated
425     metainformation.
25c64c 426
99edc5 427     code: 204, title: No Content
CM 428     """
429     code = 204
430     title = 'No Content'
431     empty_body = True
432
c5ed54 433 class HTTPResetContent(HTTPSuccessful):
99edc5 434     """
6926fa 435     subclass of :class:`~HTTPSuccessful`
99edc5 436
08c221 437     This indicates that the server has fulfilled the request and
99edc5 438     the user agent SHOULD reset the document view which caused the
CM 439     request to be sent.
25c64c 440
99edc5 441     code: 205, title: Reset Content
CM 442     """
443     code = 205
444     title = 'Reset Content'
445     empty_body = True
446
c5ed54 447 class HTTPPartialContent(HTTPSuccessful):
99edc5 448     """
6926fa 449     subclass of :class:`~HTTPSuccessful`
99edc5 450
CM 451     This indicates that the server has fulfilled the partial GET
452     request for the resource.
25c64c 453
99edc5 454     code: 206, title: Partial Content
CM 455     """
456     code = 206
457     title = 'Partial Content'
458
459 ## FIXME: add 207 Multi-Status (but it's complicated)
460
461 ############################################################
462 ## 3xx redirection
463 ############################################################
464
465 class _HTTPMove(HTTPRedirection):
466     """
467     redirections which require a Location field
468
469     Since a 'Location' header is a required attribute of 301, 302, 303,
470     305 and 307 (but not 304), this base class provides the mechanics to
471     make this easy.
472
473     You must provide a ``location`` keyword argument.
474     """
475     # differences from webob.exc._HTTPMove:
476     #
477     # - ${location} isn't wrapped in an <a> tag in body
478     #
479     # - location keyword arg defaults to ''
d0a5f0 480     #
CM 481     # - location isn't prepended with req.path_url when adding it as
482     #   a header
483     #
484     # - ``location`` is first keyword (and positional) argument
99edc5 485     #
CM 486     # - ``add_slash`` argument is no longer accepted:  code that passes
487     #   add_slash argument to the constructor will receive an exception.
488     explanation = 'The resource has been moved to'
489     body_template_obj = Template('''\
d0a5f0 490 ${explanation} ${location}; you should be redirected automatically.
99edc5 491 ${detail}
CM 492 ${html_comment}''')
493
d0a5f0 494     def __init__(self, location='', detail=None, headers=None, comment=None,
CM 495                  body_template=None, **kw):
45b15a 496         if location is None:
RB 497             raise ValueError("HTTP redirects need a location to redirect to.")
99edc5 498         super(_HTTPMove, self).__init__(
CM 499             detail=detail, headers=headers, comment=comment,
500             body_template=body_template, location=location, **kw)
501
502 class HTTPMultipleChoices(_HTTPMove):
503     """
504     subclass of :class:`~_HTTPMove`
505
506     This indicates that the requested resource corresponds to any one
507     of a set of representations, each with its own specific location,
508     and agent-driven negotiation information is being provided so that
509     the user can select a preferred representation and redirect its
510     request to that location.
25c64c 511
99edc5 512     code: 300, title: Multiple Choices
CM 513     """
514     code = 300
515     title = 'Multiple Choices'
516
517 class HTTPMovedPermanently(_HTTPMove):
518     """
519     subclass of :class:`~_HTTPMove`
520
521     This indicates that the requested resource has been assigned a new
522     permanent URI and any future references to this resource SHOULD use
523     one of the returned URIs.
524
525     code: 301, title: Moved Permanently
526     """
527     code = 301
528     title = 'Moved Permanently'
529
530 class HTTPFound(_HTTPMove):
531     """
532     subclass of :class:`~_HTTPMove`
533
534     This indicates that the requested resource resides temporarily under
535     a different URI.
25c64c 536
99edc5 537     code: 302, title: Found
CM 538     """
539     code = 302
540     title = 'Found'
541     explanation = 'The resource was found at'
542
543 # This one is safe after a POST (the redirected location will be
544 # retrieved with GET):
545 class HTTPSeeOther(_HTTPMove):
546     """
547     subclass of :class:`~_HTTPMove`
548
549     This indicates that the response to the request can be found under
550     a different URI and SHOULD be retrieved using a GET method on that
551     resource.
25c64c 552
99edc5 553     code: 303, title: See Other
CM 554     """
555     code = 303
556     title = 'See Other'
557
558 class HTTPNotModified(HTTPRedirection):
559     """
560     subclass of :class:`~HTTPRedirection`
561
562     This indicates that if the client has performed a conditional GET
563     request and access is allowed, but the document has not been
564     modified, the server SHOULD respond with this status code.
565
566     code: 304, title: Not Modified
567     """
568     # FIXME: this should include a date or etag header
569     code = 304
570     title = 'Not Modified'
571     empty_body = True
572
573 class HTTPUseProxy(_HTTPMove):
574     """
575     subclass of :class:`~_HTTPMove`
576
577     This indicates that the requested resource MUST be accessed through
578     the proxy given by the Location field.
25c64c 579
99edc5 580     code: 305, title: Use Proxy
CM 581     """
582     # Not a move, but looks a little like one
583     code = 305
584     title = 'Use Proxy'
585     explanation = (
586         'The resource must be accessed through a proxy located at')
587
588 class HTTPTemporaryRedirect(_HTTPMove):
589     """
590     subclass of :class:`~_HTTPMove`
591
592     This indicates that the requested resource resides temporarily
593     under a different URI.
25c64c 594
99edc5 595     code: 307, title: Temporary Redirect
CM 596     """
597     code = 307
598     title = 'Temporary Redirect'
599
56cbb5 600 class HTTPPermanentRedirect(_HTTPMove):
JW 601     """
602     subclass of :class:`~_HTTPMove`
603
604     This indicates that the requested resource resides permanently
605     under a different URI and that the request method must not be
606     changed.
607
608     code: 308, title: Permanent Redirect
609     """
610     code = 308
611     title = 'Permanent Redirect'
612
99edc5 613 ############################################################
CM 614 ## 4xx client error
615 ############################################################
616
617 class HTTPClientError(HTTPError):
618     """
8b1057 619     base class for the 400s, where the client is in error
99edc5 620
CM 621     This is an error condition in which the client is presumed to be
622     in-error.  This is an expected problem, and thus is not considered
623     a bug.  A server-side traceback is not warranted.  Unless specialized,
624     this is a '400 Bad Request'
625     """
391275 626     code = 400
CE 627     title = 'Bad Request'
99edc5 628
CM 629 class HTTPBadRequest(HTTPClientError):
0905d2 630     """
KOP 631     subclass of :class:`~HTTPClientError`
632
6b0889 633     This indicates that the body or headers failed validity checks,
MM 634     preventing the server from being able to continue processing.
0905d2 635
6b0889 636     code: 400, title: Bad Request
0905d2 637     """
7740b6 638     explanation = ('The server could not comply with the request since '
TS 639                    'it is either malformed or otherwise incorrect.')
0905d2 640
99edc5 641 class HTTPUnauthorized(HTTPClientError):
CM 642     """
643     subclass of :class:`~HTTPClientError`
644
645     This indicates that the request requires user authentication.
25c64c 646
99edc5 647     code: 401, title: Unauthorized
CM 648     """
649     code = 401
650     title = 'Unauthorized'
651     explanation = (
652         'This server could not verify that you are authorized to '
653         'access the document you requested.  Either you supplied the '
654         'wrong credentials (e.g., bad password), or your browser '
655         'does not understand how to supply the credentials required.')
656
657 class HTTPPaymentRequired(HTTPClientError):
658     """
659     subclass of :class:`~HTTPClientError`
25c64c 660
99edc5 661     code: 402, title: Payment Required
CM 662     """
663     code = 402
664     title = 'Payment Required'
665     explanation = ('Access was denied for financial reasons.')
666
667 class HTTPForbidden(HTTPClientError):
668     """
669     subclass of :class:`~HTTPClientError`
670
671     This indicates that the server understood the request, but is
672     refusing to fulfill it.
673
674     code: 403, title: Forbidden
675
676     Raise this exception within :term:`view` code to immediately return the
677     :term:`forbidden view` to the invoking user.  Usually this is a basic
678     ``403`` page, but the forbidden view can be customized as necessary.  See
679     :ref:`changing_the_forbidden_view`.  A ``Forbidden`` exception will be
680     the ``context`` of a :term:`Forbidden View`.
681
682     This exception's constructor treats two arguments specially.  The first
683     argument, ``detail``, should be a string.  The value of this string will
684     be used as the ``message`` attribute of the exception object.  The second
685     special keyword argument, ``result`` is usually an instance of
686     :class:`pyramid.security.Denied` or :class:`pyramid.security.ACLDenied`
687     each of which indicates a reason for the forbidden error.  However,
688     ``result`` is also permitted to be just a plain boolean ``False`` object
689     or ``None``.  The ``result`` value will be used as the ``result``
690     attribute of the exception object.  It defaults to ``None``.
691
692     The :term:`Forbidden View` can use the attributes of a Forbidden
693     exception as necessary to provide extended information in an error
694     report shown to a user.
695     """
696     # differences from webob.exc.HTTPForbidden:
697     #
698     # - accepts a ``result`` keyword argument
25c64c 699     #
99edc5 700     # - overrides constructor to set ``self.result``
CM 701     #
702     # differences from older ``pyramid.exceptions.Forbidden``:
703     #
704     # - ``result`` must be passed as a keyword argument.
705     #
706     code = 403
707     title = 'Forbidden'
708     explanation = ('Access was denied to this resource.')
709     def __init__(self, detail=None, headers=None, comment=None,
710                  body_template=None, result=None, **kw):
711         HTTPClientError.__init__(self, detail=detail, headers=headers,
712                                  comment=comment, body_template=body_template,
713                                  **kw)
714         self.result = result
715
716 class HTTPNotFound(HTTPClientError):
717     """
718     subclass of :class:`~HTTPClientError`
719
720     This indicates that the server did not find anything matching the
721     Request-URI.
25c64c 722
99edc5 723     code: 404, title: Not Found
CM 724
725     Raise this exception within :term:`view` code to immediately
2f4bde 726     return the :term:`Not Found View` to the invoking user.  Usually
TL 727     this is a basic ``404`` page, but the Not Found View can be
99edc5 728     customized as necessary.  See :ref:`changing_the_notfound_view`.
CM 729
730     This exception's constructor accepts a ``detail`` argument
731     (the first argument), which should be a string.  The value of this
732     string will be available as the ``message`` attribute of this exception,
733     for availability to the :term:`Not Found View`.
734     """
735     code = 404
736     title = 'Not Found'
737     explanation = ('The resource could not be found.')
738
739 class HTTPMethodNotAllowed(HTTPClientError):
740     """
741     subclass of :class:`~HTTPClientError`
742
743     This indicates that the method specified in the Request-Line is
744     not allowed for the resource identified by the Request-URI.
745
746     code: 405, title: Method Not Allowed
747     """
748     # differences from webob.exc.HTTPMethodNotAllowed:
749     #
d0a5f0 750     # - body_template_obj uses ${br} instead of <br />
99edc5 751     code = 405
CM 752     title = 'Method Not Allowed'
d0a5f0 753     body_template_obj = Template('''\
CM 754 The method ${REQUEST_METHOD} is not allowed for this resource. ${br}${br}
755 ${detail}''')
99edc5 756
CM 757 class HTTPNotAcceptable(HTTPClientError):
758     """
759     subclass of :class:`~HTTPClientError`
760
761     This indicates the resource identified by the request is only
762     capable of generating response entities which have content
763     characteristics not acceptable according to the accept headers
764     sent in the request.
25c64c 765
99edc5 766     code: 406, title: Not Acceptable
CM 767     """
768     # differences from webob.exc.HTTPNotAcceptable:
769     #
d0a5f0 770     # - "template" attribute left off (useless, bug in webob?)
99edc5 771     code = 406
CM 772     title = 'Not Acceptable'
773
774 class HTTPProxyAuthenticationRequired(HTTPClientError):
775     """
776     subclass of :class:`~HTTPClientError`
777
778     This is similar to 401, but indicates that the client must first
779     authenticate itself with the proxy.
25c64c 780
99edc5 781     code: 407, title: Proxy Authentication Required
CM 782     """
783     code = 407
784     title = 'Proxy Authentication Required'
785     explanation = ('Authentication with a local proxy is needed.')
786
787 class HTTPRequestTimeout(HTTPClientError):
788     """
789     subclass of :class:`~HTTPClientError`
790
791     This indicates that the client did not produce a request within
792     the time that the server was prepared to wait.
25c64c 793
99edc5 794     code: 408, title: Request Timeout
CM 795     """
796     code = 408
797     title = 'Request Timeout'
798     explanation = ('The server has waited too long for the request to '
799                    'be sent by the client.')
800
801 class HTTPConflict(HTTPClientError):
802     """
803     subclass of :class:`~HTTPClientError`
804
805     This indicates that the request could not be completed due to a
806     conflict with the current state of the resource.
25c64c 807
99edc5 808     code: 409, title: Conflict
CM 809     """
810     code = 409
811     title = 'Conflict'
812     explanation = ('There was a conflict when trying to complete '
813                    'your request.')
814
815 class HTTPGone(HTTPClientError):
816     """
817     subclass of :class:`~HTTPClientError`
818
819     This indicates that the requested resource is no longer available
820     at the server and no forwarding address is known.
25c64c 821
99edc5 822     code: 410, title: Gone
CM 823     """
824     code = 410
825     title = 'Gone'
826     explanation = ('This resource is no longer available.  No forwarding '
827                    'address is given.')
828
829 class HTTPLengthRequired(HTTPClientError):
830     """
831     subclass of :class:`~HTTPClientError`
832
08c221 833     This indicates that the server refuses to accept the request
99edc5 834     without a defined Content-Length.
25c64c 835
99edc5 836     code: 411, title: Length Required
CM 837     """
838     code = 411
839     title = 'Length Required'
840     explanation = ('Content-Length header required.')
841
842 class HTTPPreconditionFailed(HTTPClientError):
843     """
844     subclass of :class:`~HTTPClientError`
845
846     This indicates that the precondition given in one or more of the
847     request-header fields evaluated to false when it was tested on the
848     server.
25c64c 849
99edc5 850     code: 412, title: Precondition Failed
CM 851     """
852     code = 412
853     title = 'Precondition Failed'
854     explanation = ('Request precondition failed.')
855
856 class HTTPRequestEntityTooLarge(HTTPClientError):
857     """
858     subclass of :class:`~HTTPClientError`
859
860     This indicates that the server is refusing to process a request
861     because the request entity is larger than the server is willing or
862     able to process.
863
864     code: 413, title: Request Entity Too Large
865     """
866     code = 413
867     title = 'Request Entity Too Large'
868     explanation = ('The body of your request was too large for this server.')
869
870 class HTTPRequestURITooLong(HTTPClientError):
871     """
872     subclass of :class:`~HTTPClientError`
873
874     This indicates that the server is refusing to service the request
875     because the Request-URI is longer than the server is willing to
876     interpret.
25c64c 877
99edc5 878     code: 414, title: Request-URI Too Long
CM 879     """
880     code = 414
881     title = 'Request-URI Too Long'
882     explanation = ('The request URI was too long for this server.')
883
884 class HTTPUnsupportedMediaType(HTTPClientError):
885     """
886     subclass of :class:`~HTTPClientError`
887
888     This indicates that the server is refusing to service the request
889     because the entity of the request is in a format not supported by
890     the requested resource for the requested method.
25c64c 891
99edc5 892     code: 415, title: Unsupported Media Type
CM 893     """
894     # differences from webob.exc.HTTPUnsupportedMediaType:
895     #
d0a5f0 896     # - "template_obj" attribute left off (useless, bug in webob?)
99edc5 897     code = 415
CM 898     title = 'Unsupported Media Type'
899
900 class HTTPRequestRangeNotSatisfiable(HTTPClientError):
901     """
902     subclass of :class:`~HTTPClientError`
903
904     The server SHOULD return a response with this status code if a
905     request included a Range request-header field, and none of the
906     range-specifier values in this field overlap the current extent
907     of the selected resource, and the request did not include an
908     If-Range request-header field.
25c64c 909
99edc5 910     code: 416, title: Request Range Not Satisfiable
CM 911     """
912     code = 416
913     title = 'Request Range Not Satisfiable'
914     explanation = ('The Range requested is not available.')
915
916 class HTTPExpectationFailed(HTTPClientError):
917     """
918     subclass of :class:`~HTTPClientError`
919
920     This indidcates that the expectation given in an Expect
921     request-header field could not be met by this server.
25c64c 922
99edc5 923     code: 417, title: Expectation Failed
CM 924     """
925     code = 417
926     title = 'Expectation Failed'
927     explanation = ('Expectation failed.')
928
929 class HTTPUnprocessableEntity(HTTPClientError):
930     """
931     subclass of :class:`~HTTPClientError`
932
933     This indicates that the server is unable to process the contained
25c64c 934     instructions.
a3033c 935
BJR 936     May be used to notify the client that their JSON/XML is well formed, but
937     not correct for the current request.
938
939     See RFC4918 section 11 for more information.
25c64c 940
99edc5 941     code: 422, title: Unprocessable Entity
CM 942     """
943     ## Note: from WebDAV
944     code = 422
945     title = 'Unprocessable Entity'
946     explanation = 'Unable to process the contained instructions'
947
948 class HTTPLocked(HTTPClientError):
949     """
950     subclass of :class:`~HTTPClientError`
951
a3033c 952     This indicates that the resource is locked.
25c64c 953
99edc5 954     code: 423, title: Locked
CM 955     """
956     ## Note: from WebDAV
957     code = 423
958     title = 'Locked'
959     explanation = ('The resource is locked')
960
961 class HTTPFailedDependency(HTTPClientError):
962     """
963     subclass of :class:`~HTTPClientError`
964
965     This indicates that the method could not be performed because the
966     requested action depended on another action and that action failed.
25c64c 967
99edc5 968     code: 424, title: Failed Dependency
CM 969     """
970     ## Note: from WebDAV
971     code = 424
972     title = 'Failed Dependency'
973     explanation = (
974         'The method could not be performed because the requested '
975         'action dependended on another action and that action failed')
976
36d5a4 977 class HTTPPreconditionRequired(HTTPClientError):
H 978     """
979     subclass of :class:`~HTTPClientError`
980
981     This indicates that the origin server requires the
982     request to be conditional.
983
984     Its typical use is to avoid the "lost update" problem, where a client
985     GETs a resource's state, modifies it, and PUTs it back to the server,
986     when meanwhile a third party has modified the state on the server,
987     leading to a conflict.  By requiring requests to be conditional, the
988     server can assure that clients are working with the correct copies.
989
990     RFC 6585.3
991
992     code: 428, title: Precondition Required
993     """
994     code = 428
995     title = 'Precondition Required'
996     explanation = (
997         'The origin server requires the request to be conditional.')
998
999 class HTTPTooManyRequests(HTTPClientError):
1000     """
1001     subclass of :class:`~HTTPClientError`
1002
1003     This indicates that the user has sent too many
1004     requests in a given amount of time ("rate limiting").
1005
1006     RFC 6585.4
1007
1008     code: 429, title: Too Many Requests
1009     """
1010     code = 429
1011     title = 'Too Many Requests'
1012     explanation = (
1013         'The action could not be performed because there were too '
1014         'many requests by the client.')
1015
1016 class HTTPRequestHeaderFieldsTooLarge(HTTPClientError):
1017     """
1018     subclass of :class:`~HTTPClientError`
1019
1020     This indicates that the server is unwilling to process
1021     the request because its header fields are too large.  The request MAY
1022     be resubmitted after reducing the size of the request header fields.
1023
1024     RFC 6585.5
1025
1026     code: 431, title: Request Header Fields Too Large
1027     """
1028     code = 431
1029     title = 'Request Header Fields Too Large'
1030     explanation = (
1031         'The requests header fields were too large.')
1032
99edc5 1033 ############################################################
CM 1034 ## 5xx Server Error
1035 ############################################################
1036 #  Response status codes beginning with the digit "5" indicate cases in
1037 #  which the server is aware that it has erred or is incapable of
1038 #  performing the request. Except when responding to a HEAD request, the
1039 #  server SHOULD include an entity containing an explanation of the error
1040 #  situation, and whether it is a temporary or permanent condition. User
1041 #  agents SHOULD display any included entity to the user. These response
1042 #  codes are applicable to any request method.
1043
1044 class HTTPServerError(HTTPError):
1045     """
8b1057 1046     base class for the 500s, where the server is in-error
99edc5 1047
CM 1048     This is an error condition in which the server is presumed to be
8b1057 1049     in-error.  Unless specialized, this is a '500 Internal Server Error'.
99edc5 1050     """
CM 1051     code = 500
1052     title = 'Internal Server Error'
391275 1053
CE 1054 class HTTPInternalServerError(HTTPServerError):
1055     """
1056     subclass of :class:`~HTTPServerError`
1057
1058     This indicates that the server encountered an unexpected condition
1059     which prevented it from fulfilling the request.
1060
1061     code: 500, title: Internal Server Error
1062     """
99edc5 1063     explanation = (
cb67e0 1064         'The server has either erred or is incapable of performing '
BJR 1065         'the requested operation.')
99edc5 1066
CM 1067 class HTTPNotImplemented(HTTPServerError):
1068     """
1069     subclass of :class:`~HTTPServerError`
1070
1071     This indicates that the server does not support the functionality
1072     required to fulfill the request.
25c64c 1073
99edc5 1074     code: 501, title: Not Implemented
CM 1075     """
1076     # differences from webob.exc.HTTPNotAcceptable:
1077     #
d0a5f0 1078     # - "template" attr left off (useless, bug in webob?)
99edc5 1079     code = 501
CM 1080     title = 'Not Implemented'
1081
1082 class HTTPBadGateway(HTTPServerError):
1083     """
1084     subclass of :class:`~HTTPServerError`
1085
1086     This indicates that the server, while acting as a gateway or proxy,
1087     received an invalid response from the upstream server it accessed
1088     in attempting to fulfill the request.
25c64c 1089
99edc5 1090     code: 502, title: Bad Gateway
CM 1091     """
1092     code = 502
1093     title = 'Bad Gateway'
1094     explanation = ('Bad gateway.')
1095
1096 class HTTPServiceUnavailable(HTTPServerError):
1097     """
1098     subclass of :class:`~HTTPServerError`
1099
1100     This indicates that the server is currently unable to handle the
1101     request due to a temporary overloading or maintenance of the server.
25c64c 1102
99edc5 1103     code: 503, title: Service Unavailable
CM 1104     """
1105     code = 503
1106     title = 'Service Unavailable'
1107     explanation = ('The server is currently unavailable. '
1108                    'Please try again at a later time.')
1109
1110 class HTTPGatewayTimeout(HTTPServerError):
1111     """
1112     subclass of :class:`~HTTPServerError`
1113
1114     This indicates that the server, while acting as a gateway or proxy,
1115     did not receive a timely response from the upstream server specified
1116     by the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server
1117     (e.g. DNS) it needed to access in attempting to complete the request.
1118
1119     code: 504, title: Gateway Timeout
1120     """
1121     code = 504
1122     title = 'Gateway Timeout'
1123     explanation = ('The gateway has timed out.')
1124
1125 class HTTPVersionNotSupported(HTTPServerError):
1126     """
1127     subclass of :class:`~HTTPServerError`
1128
1129     This indicates that the server does not support, or refuses to
1130     support, the HTTP protocol version that was used in the request
1131     message.
1132
1133     code: 505, title: HTTP Version Not Supported
1134     """
1135     code = 505
1136     title = 'HTTP Version Not Supported'
1137     explanation = ('The HTTP version is not supported.')
1138
1139 class HTTPInsufficientStorage(HTTPServerError):
1140     """
1141     subclass of :class:`~HTTPServerError`
1142
1143     This indicates that the server does not have enough space to save
1144     the resource.
25c64c 1145
99edc5 1146     code: 507, title: Insufficient Storage
CM 1147     """
1148     code = 507
1149     title = 'Insufficient Storage'
1150     explanation = ('There was not enough space to save the resource')
1151
f8f08b 1152 def exception_response(status_code, **kw):
99edc5 1153     """Creates an HTTP exception based on a status code. Example::
CM 1154
a6035f 1155         raise exception_response(404) # raises an HTTPNotFound exception.
99edc5 1156
CM 1157     The values passed as ``kw`` are provided to the exception's constructor.
1158     """
1159     exc = status_map[status_code](**kw)
1160     return exc
1161
1162 def default_exceptionresponse_view(context, request):
1163     if not isinstance(context, Exception):
1164         # backwards compat for an exception response view registered via
1165         # config.set_notfound_view or config.set_forbidden_view
1166         # instead of as a proper exception view
1167         context = request.exception or context
d69ae6 1168     return context # assumed to be an IResponse
99edc5 1169
25c64c 1170 status_map = {}
d0a5f0 1171 code = None
e6c2d2 1172 for name, value in list(globals().items()):
50a8a0 1173     if (
JA 1174             isinstance(value, class_types) and
1175             issubclass(value, HTTPException) and
19beea 1176             value not in {HTTPClientError, HTTPServerError} and
50a8a0 1177             not name.startswith('_')
JA 1178     ):
99edc5 1179         code = getattr(value, 'code', None)
CM 1180         if code:
1181             status_map[code] = value
d0a5f0 1182 del name, value, code