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