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