Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/webob/exc.py : 69%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2This module processes Python exceptions that relate to HTTP exceptions
3by defining a set of exceptions, all subclasses of HTTPException.
4Each exception, in addition to being a Python exception that can be
5raised and caught, is also a WSGI application and ``webob.Response``
6object.
8This module defines exceptions according to RFC 2068 [1]_ : codes with
9100-300 are not really errors; 400's are client errors, and 500's are
10server errors. According to the WSGI specification [2]_ , the application
11can call ``start_response`` more then once only under two conditions:
12(a) the response has not yet been sent, or (b) if the second and
13subsequent invocations of ``start_response`` have a valid ``exc_info``
14argument obtained from ``sys.exc_info()``. The WSGI specification then
15requires the server or gateway to handle the case where content has been
16sent and then an exception was encountered.
18Exception
19 HTTPException
20 HTTPOk
21 * 200 - :class:`HTTPOk`
22 * 201 - :class:`HTTPCreated`
23 * 202 - :class:`HTTPAccepted`
24 * 203 - :class:`HTTPNonAuthoritativeInformation`
25 * 204 - :class:`HTTPNoContent`
26 * 205 - :class:`HTTPResetContent`
27 * 206 - :class:`HTTPPartialContent`
28 HTTPRedirection
29 * 300 - :class:`HTTPMultipleChoices`
30 * 301 - :class:`HTTPMovedPermanently`
31 * 302 - :class:`HTTPFound`
32 * 303 - :class:`HTTPSeeOther`
33 * 304 - :class:`HTTPNotModified`
34 * 305 - :class:`HTTPUseProxy`
35 * 307 - :class:`HTTPTemporaryRedirect`
36 * 308 - :class:`HTTPPermanentRedirect`
37 HTTPError
38 HTTPClientError
39 * 400 - :class:`HTTPBadRequest`
40 * 401 - :class:`HTTPUnauthorized`
41 * 402 - :class:`HTTPPaymentRequired`
42 * 403 - :class:`HTTPForbidden`
43 * 404 - :class:`HTTPNotFound`
44 * 405 - :class:`HTTPMethodNotAllowed`
45 * 406 - :class:`HTTPNotAcceptable`
46 * 407 - :class:`HTTPProxyAuthenticationRequired`
47 * 408 - :class:`HTTPRequestTimeout`
48 * 409 - :class:`HTTPConflict`
49 * 410 - :class:`HTTPGone`
50 * 411 - :class:`HTTPLengthRequired`
51 * 412 - :class:`HTTPPreconditionFailed`
52 * 413 - :class:`HTTPRequestEntityTooLarge`
53 * 414 - :class:`HTTPRequestURITooLong`
54 * 415 - :class:`HTTPUnsupportedMediaType`
55 * 416 - :class:`HTTPRequestRangeNotSatisfiable`
56 * 417 - :class:`HTTPExpectationFailed`
57 * 422 - :class:`HTTPUnprocessableEntity`
58 * 423 - :class:`HTTPLocked`
59 * 424 - :class:`HTTPFailedDependency`
60 * 428 - :class:`HTTPPreconditionRequired`
61 * 429 - :class:`HTTPTooManyRequests`
62 * 431 - :class:`HTTPRequestHeaderFieldsTooLarge`
63 * 451 - :class:`HTTPUnavailableForLegalReasons`
64 HTTPServerError
65 * 500 - :class:`HTTPInternalServerError`
66 * 501 - :class:`HTTPNotImplemented`
67 * 502 - :class:`HTTPBadGateway`
68 * 503 - :class:`HTTPServiceUnavailable`
69 * 504 - :class:`HTTPGatewayTimeout`
70 * 505 - :class:`HTTPVersionNotSupported`
71 * 511 - :class:`HTTPNetworkAuthenticationRequired`
73Usage notes
74-----------
76The HTTPException class is complicated by 4 factors:
78 1. The content given to the exception may either be plain-text or
79 as html-text.
81 2. The template may want to have string-substitutions taken from
82 the current ``environ`` or values from incoming headers. This
83 is especially troublesome due to case sensitivity.
85 3. The final output may either be text/plain or text/html
86 mime-type as requested by the client application.
88 4. Each exception has a default explanation, but those who
89 raise exceptions may want to provide additional detail.
91Subclass attributes and call parameters are designed to provide an easier path
92through the complications.
94Attributes:
96 ``code``
97 the HTTP status code for the exception
99 ``title``
100 remainder of the status line (stuff after the code)
102 ``explanation``
103 a plain-text explanation of the error message that is
104 not subject to environment or header substitutions;
105 it is accessible in the template via %(explanation)s
107 ``detail``
108 a plain-text message customization that is not subject
109 to environment or header substitutions; accessible in
110 the template via %(detail)s
112 ``body_template``
113 a content fragment (in HTML) used for environment and
114 header substitution; the default template includes both
115 the explanation and further detail provided in the
116 message
118Parameters:
120 ``detail``
121 a plain-text override of the default ``detail``
123 ``headers``
124 a list of (k,v) header pairs
126 ``comment``
127 a plain-text additional information which is
128 usually stripped/hidden for end-users
130 ``body_template``
131 a string.Template object containing a content fragment in HTML
132 that frames the explanation and further detail
134To override the template (which is HTML content) or the plain-text
135explanation, one must subclass the given exception; or customize it
136after it has been created. This particular breakdown of a message
137into explanation, detail and template allows both the creation of
138plain-text and html messages for various clients as well as
139error-free substitution of environment variables and headers.
142The subclasses of :class:`~_HTTPMove`
143(:class:`~HTTPMultipleChoices`, :class:`~HTTPMovedPermanently`,
144:class:`~HTTPFound`, :class:`~HTTPSeeOther`, :class:`~HTTPUseProxy` and
145:class:`~HTTPTemporaryRedirect`) are redirections that require a ``Location``
146field. Reflecting this, these subclasses have two additional keyword arguments:
147``location`` and ``add_slash``.
149Parameters:
151 ``location``
152 to set the location immediately
154 ``add_slash``
155 set to True to redirect to the same URL as the request, except with a
156 ``/`` appended
158Relative URLs in the location will be resolved to absolute.
160References:
162.. [1] https://www.python.org/dev/peps/pep-0333/#error-handling
163.. [2] https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5
166"""
168import json
169from string import Template
170import re
171import sys
173from webob.acceptparse import create_accept_header
174from webob.compat import (
175 class_types,
176 text_,
177 text_type,
178 urlparse,
179 )
180from webob.request import Request
181from webob.response import Response
182from webob.util import html_escape
184tag_re = re.compile(r'<.*?>', re.S)
185br_re = re.compile(r'<br.*?>', re.I | re.S)
186comment_re = re.compile(r'<!--|-->')
188class _lazified(object):
189 def __init__(self, func, value):
190 self.func = func
191 self.value = value
193 def __str__(self):
194 return self.func(self.value)
196def lazify(func):
197 def wrapper(value):
198 return _lazified(func, value)
199 return wrapper
201def no_escape(value):
202 if value is None:
203 return ''
204 if not isinstance(value, text_type):
205 if hasattr(value, '__unicode__'):
206 value = value.__unicode__()
207 if isinstance(value, bytes):
208 value = text_(value, 'utf-8')
209 else:
210 value = text_type(value)
211 return value
213def strip_tags(value):
214 value = value.replace('\n', ' ')
215 value = value.replace('\r', '')
216 value = br_re.sub('\n', value)
217 value = comment_re.sub('', value)
218 value = tag_re.sub('', value)
219 return value
221class HTTPException(Exception):
222 def __init__(self, message, wsgi_response):
223 Exception.__init__(self, message)
224 self.wsgi_response = wsgi_response
226 def __call__(self, environ, start_response):
227 return self.wsgi_response(environ, start_response)
229class WSGIHTTPException(Response, HTTPException):
231 ## You should set in subclasses:
232 # code = 200
233 # title = 'OK'
234 # explanation = 'why this happens'
235 # body_template_obj = Template('response template')
236 code = 500
237 title = 'Internal Server Error'
238 explanation = ''
239 body_template_obj = Template('''\
240${explanation}<br /><br />
241${detail}
242${html_comment}
243''')
245 plain_template_obj = Template('''\
246${status}
248${body}''')
250 html_template_obj = Template('''\
251<html>
252 <head>
253 <title>${status}</title>
254 </head>
255 <body>
256 <h1>${status}</h1>
257 ${body}
258 </body>
259</html>''')
261 ## Set this to True for responses that should have no request body
262 empty_body = False
264 def __init__(self, detail=None, headers=None, comment=None,
265 body_template=None, json_formatter=None, **kw):
266 Response.__init__(self,
267 status='%s %s' % (self.code, self.title),
268 **kw)
269 Exception.__init__(self, detail)
270 if headers:
271 self.headers.extend(headers)
272 self.detail = detail
273 self.comment = comment
274 if body_template is not None:
275 self.body_template = body_template
276 self.body_template_obj = Template(body_template)
277 if self.empty_body:
278 del self.content_type
279 del self.content_length
280 if json_formatter is not None:
281 self.json_formatter = json_formatter
283 def __str__(self):
284 return self.detail or self.explanation
286 def _make_body(self, environ, escape):
287 escape = lazify(escape)
288 args = {
289 'explanation': escape(self.explanation),
290 'detail': escape(self.detail or ''),
291 'comment': escape(self.comment or ''),
292 }
293 if self.comment:
294 args['html_comment'] = '<!-- %s -->' % escape(self.comment)
295 else:
296 args['html_comment'] = ''
297 if WSGIHTTPException.body_template_obj is not self.body_template_obj:
298 # Custom template; add headers to args
299 for k, v in environ.items():
300 args[k] = escape(v)
301 for k, v in self.headers.items():
302 args[k.lower()] = escape(v)
303 t_obj = self.body_template_obj
304 return t_obj.safe_substitute(args)
306 def plain_body(self, environ):
307 body = self._make_body(environ, no_escape)
308 body = strip_tags(body)
309 return self.plain_template_obj.substitute(status=self.status,
310 title=self.title,
311 body=body)
313 def html_body(self, environ):
314 body = self._make_body(environ, html_escape)
315 return self.html_template_obj.substitute(status=self.status,
316 body=body)
318 def json_formatter(self, body, status, title, environ):
319 return {'message': body,
320 'code': status,
321 'title': title}
323 def json_body(self, environ):
324 body = self._make_body(environ, no_escape)
325 jsonbody = self.json_formatter(body=body, status=self.status,
326 title=self.title, environ=environ)
327 return json.dumps(jsonbody)
329 def generate_response(self, environ, start_response):
330 if self.content_length is not None:
331 del self.content_length
332 headerlist = list(self.headerlist)
333 accept_value = environ.get('HTTP_ACCEPT', '')
334 accept_header = create_accept_header(header_value=accept_value)
335 acceptable_offers = accept_header.acceptable_offers(
336 offers=['text/html', 'application/json'],
337 )
338 match = acceptable_offers[0][0] if acceptable_offers else None
340 if match == 'text/html':
341 content_type = 'text/html'
342 body = self.html_body(environ)
343 elif match == 'application/json':
344 content_type = 'application/json'
345 body = self.json_body(environ)
346 else:
347 content_type = 'text/plain'
348 body = self.plain_body(environ)
349 resp = Response(body,
350 status=self.status,
351 headerlist=headerlist,
352 content_type=content_type,
353 )
354 resp.content_type = content_type
355 return resp(environ, start_response)
357 def __call__(self, environ, start_response):
358 is_head = environ['REQUEST_METHOD'] == 'HEAD'
359 if self.has_body or self.empty_body or is_head:
360 app_iter = Response.__call__(self, environ, start_response)
361 else:
362 app_iter = self.generate_response(environ, start_response)
363 if is_head:
364 app_iter = []
365 return app_iter
367 @property
368 def wsgi_response(self):
369 return self
373class HTTPError(WSGIHTTPException):
374 """
375 base class for status codes in the 400's and 500's
377 This is an exception which indicates that an error has occurred,
378 and that any work in progress should not be committed. These are
379 typically results in the 400's and 500's.
380 """
382class HTTPRedirection(WSGIHTTPException):
383 """
384 base class for 300's status code (redirections)
386 This is an abstract base class for 3xx redirection. It indicates
387 that further action needs to be taken by the user agent in order
388 to fulfill the request. It does not necessarly signal an error
389 condition.
390 """
392class HTTPOk(WSGIHTTPException):
393 """
394 Base class for the 200's status code (successful responses)
396 code: 200, title: OK
397 """
398 code = 200
399 title = 'OK'
401############################################################
402## 2xx success
403############################################################
405class HTTPCreated(HTTPOk):
406 """
407 subclass of :class:`~HTTPOk`
409 This indicates that request has been fulfilled and resulted in a new
410 resource being created.
412 code: 201, title: Created
413 """
414 code = 201
415 title = 'Created'
417class HTTPAccepted(HTTPOk):
418 """
419 subclass of :class:`~HTTPOk`
421 This indicates that the request has been accepted for processing, but the
422 processing has not been completed.
424 code: 202, title: Accepted
425 """
426 code = 202
427 title = 'Accepted'
428 explanation = 'The request is accepted for processing.'
430class HTTPNonAuthoritativeInformation(HTTPOk):
431 """
432 subclass of :class:`~HTTPOk`
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.
438 code: 203, title: Non-Authoritative Information
439 """
440 code = 203
441 title = 'Non-Authoritative Information'
443class HTTPNoContent(HTTPOk):
444 """
445 subclass of :class:`~HTTPOk`
447 This indicates that the server has fulfilled the request but does
448 not need to return an entity-body, and might want to return updated
449 metainformation.
451 code: 204, title: No Content
452 """
453 code = 204
454 title = 'No Content'
455 empty_body = True
457class HTTPResetContent(HTTPOk):
458 """
459 subclass of :class:`~HTTPOk`
461 This indicates that the the server has fulfilled the request and
462 the user agent SHOULD reset the document view which caused the
463 request to be sent.
465 code: 205, title: Reset Content
466 """
467 code = 205
468 title = 'Reset Content'
469 empty_body = True
471class HTTPPartialContent(HTTPOk):
472 """
473 subclass of :class:`~HTTPOk`
475 This indicates that the server has fulfilled the partial GET
476 request for the resource.
478 code: 206, title: Partial Content
479 """
480 code = 206
481 title = 'Partial Content'
483############################################################
484## 3xx redirection
485############################################################
487class _HTTPMove(HTTPRedirection):
488 """
489 redirections which require a Location field
491 Since a 'Location' header is a required attribute of 301, 302, 303,
492 305, 307 and 308 (but not 304), this base class provides the mechanics to
493 make this easy.
495 You can provide a location keyword argument to set the location
496 immediately. You may also give ``add_slash=True`` if you want to
497 redirect to the same URL as the request, except with a ``/`` added
498 to the end.
500 Relative URLs in the location will be resolved to absolute.
501 """
502 explanation = 'The resource has been moved to'
503 body_template_obj = Template('''\
504${explanation} <a href="${location}">${location}</a>;
505you should be redirected automatically.
506${detail}
507${html_comment}''')
509 def __init__(self, detail=None, headers=None, comment=None,
510 body_template=None, location=None, add_slash=False):
511 super(_HTTPMove, self).__init__(
512 detail=detail, headers=headers, comment=comment,
513 body_template=body_template)
514 if location is not None:
515 if '\n' in location or '\r' in location:
516 raise ValueError('Control characters are not allowed in location')
518 self.location = location
519 if add_slash:
520 raise TypeError(
521 "You can only provide one of the arguments location "
522 "and add_slash")
523 self.add_slash = add_slash
525 def __call__(self, environ, start_response):
526 req = Request(environ)
527 if self.add_slash:
528 url = req.path_url
529 url += '/'
530 if req.environ.get('QUERY_STRING'):
531 url += '?' + req.environ['QUERY_STRING']
532 self.location = url
533 self.location = urlparse.urljoin(req.path_url, self.location)
534 return super(_HTTPMove, self).__call__(
535 environ, start_response)
537class HTTPMultipleChoices(_HTTPMove):
538 """
539 subclass of :class:`~_HTTPMove`
541 This indicates that the requested resource corresponds to any one
542 of a set of representations, each with its own specific location,
543 and agent-driven negotiation information is being provided so that
544 the user can select a preferred representation and redirect its
545 request to that location.
547 code: 300, title: Multiple Choices
548 """
549 code = 300
550 title = 'Multiple Choices'
552class HTTPMovedPermanently(_HTTPMove):
553 """
554 subclass of :class:`~_HTTPMove`
556 This indicates that the requested resource has been assigned a new
557 permanent URI and any future references to this resource SHOULD use
558 one of the returned URIs.
560 code: 301, title: Moved Permanently
561 """
562 code = 301
563 title = 'Moved Permanently'
565class HTTPFound(_HTTPMove):
566 """
567 subclass of :class:`~_HTTPMove`
569 This indicates that the requested resource resides temporarily under
570 a different URI.
572 code: 302, title: Found
573 """
574 code = 302
575 title = 'Found'
576 explanation = 'The resource was found at'
578# This one is safe after a POST (the redirected location will be
579# retrieved with GET):
580class HTTPSeeOther(_HTTPMove):
581 """
582 subclass of :class:`~_HTTPMove`
584 This indicates that the response to the request can be found under
585 a different URI and SHOULD be retrieved using a GET method on that
586 resource.
588 code: 303, title: See Other
589 """
590 code = 303
591 title = 'See Other'
593class HTTPNotModified(HTTPRedirection):
594 """
595 subclass of :class:`~HTTPRedirection`
597 This indicates that if the client has performed a conditional GET
598 request and access is allowed, but the document has not been
599 modified, the server SHOULD respond with this status code.
601 code: 304, title: Not Modified
602 """
603 # TODO: this should include a date or etag header
604 code = 304
605 title = 'Not Modified'
606 empty_body = True
608class HTTPUseProxy(_HTTPMove):
609 """
610 subclass of :class:`~_HTTPMove`
612 This indicates that the requested resource MUST be accessed through
613 the proxy given by the Location field.
615 code: 305, title: Use Proxy
616 """
617 # Not a move, but looks a little like one
618 code = 305
619 title = 'Use Proxy'
620 explanation = (
621 'The resource must be accessed through a proxy located at')
623class HTTPTemporaryRedirect(_HTTPMove):
624 """
625 subclass of :class:`~_HTTPMove`
627 This indicates that the requested resource resides temporarily
628 under a different URI.
630 code: 307, title: Temporary Redirect
631 """
632 code = 307
633 title = 'Temporary Redirect'
635class HTTPPermanentRedirect(_HTTPMove):
636 """
637 subclass of :class:`~_HTTPMove`
639 This indicates that the requested resource resides permanently
640 under a different URI.
642 code: 308, title: Permanent Redirect
643 """
644 code = 308
645 title = 'Permanent Redirect'
648############################################################
649## 4xx client error
650############################################################
652class HTTPClientError(HTTPError):
653 """
654 base class for the 400's, where the client is in error
656 This is an error condition in which the client is presumed to be
657 in-error. This is an expected problem, and thus is not considered
658 a bug. A server-side traceback is not warranted. Unless specialized,
659 this is a '400 Bad Request'
661 code: 400, title: Bad Request
662 """
663 code = 400
664 title = 'Bad Request'
665 explanation = ('The server could not comply with the request since\r\n'
666 'it is either malformed or otherwise incorrect.\r\n')
668class HTTPBadRequest(HTTPClientError):
669 pass
671class HTTPUnauthorized(HTTPClientError):
672 """
673 subclass of :class:`~HTTPClientError`
675 This indicates that the request requires user authentication.
677 code: 401, title: Unauthorized
678 """
679 code = 401
680 title = 'Unauthorized'
681 explanation = (
682 'This server could not verify that you are authorized to\r\n'
683 'access the document you requested. Either you supplied the\r\n'
684 'wrong credentials (e.g., bad password), or your browser\r\n'
685 'does not understand how to supply the credentials required.\r\n')
687class HTTPPaymentRequired(HTTPClientError):
688 """
689 subclass of :class:`~HTTPClientError`
691 code: 402, title: Payment Required
692 """
693 code = 402
694 title = 'Payment Required'
695 explanation = ('Access was denied for financial reasons.')
697class HTTPForbidden(HTTPClientError):
698 """
699 subclass of :class:`~HTTPClientError`
701 This indicates that the server understood the request, but is
702 refusing to fulfill it.
704 code: 403, title: Forbidden
705 """
706 code = 403
707 title = 'Forbidden'
708 explanation = ('Access was denied to this resource.')
710class HTTPNotFound(HTTPClientError):
711 """
712 subclass of :class:`~HTTPClientError`
714 This indicates that the server did not find anything matching the
715 Request-URI.
717 code: 404, title: Not Found
718 """
719 code = 404
720 title = 'Not Found'
721 explanation = ('The resource could not be found.')
723class HTTPMethodNotAllowed(HTTPClientError):
724 """
725 subclass of :class:`~HTTPClientError`
727 This indicates that the method specified in the Request-Line is
728 not allowed for the resource identified by the Request-URI.
730 code: 405, title: Method Not Allowed
731 """
732 code = 405
733 title = 'Method Not Allowed'
734 # override template since we need an environment variable
735 body_template_obj = Template('''\
736The method ${REQUEST_METHOD} is not allowed for this resource. <br /><br />
737${detail}''')
739class HTTPNotAcceptable(HTTPClientError):
740 """
741 subclass of :class:`~HTTPClientError`
743 This indicates the resource identified by the request is only
744 capable of generating response entities which have content
745 characteristics not acceptable according to the accept headers
746 sent in the request.
748 code: 406, title: Not Acceptable
749 """
750 code = 406
751 title = 'Not Acceptable'
752 # override template since we need an environment variable
753 body_template_obj = Template('''\
754The resource could not be generated that was acceptable to your browser
755(content of type ${HTTP_ACCEPT}. <br /><br />
756${detail}''')
758class HTTPProxyAuthenticationRequired(HTTPClientError):
759 """
760 subclass of :class:`~HTTPClientError`
762 This is similar to 401, but indicates that the client must first
763 authenticate itself with the proxy.
765 code: 407, title: Proxy Authentication Required
766 """
767 code = 407
768 title = 'Proxy Authentication Required'
769 explanation = ('Authentication with a local proxy is needed.')
771class HTTPRequestTimeout(HTTPClientError):
772 """
773 subclass of :class:`~HTTPClientError`
775 This indicates that the client did not produce a request within
776 the time that the server was prepared to wait.
778 code: 408, title: Request Timeout
779 """
780 code = 408
781 title = 'Request Timeout'
782 explanation = ('The server has waited too long for the request to '
783 'be sent by the client.')
785class HTTPConflict(HTTPClientError):
786 """
787 subclass of :class:`~HTTPClientError`
789 This indicates that the request could not be completed due to a
790 conflict with the current state of the resource.
792 code: 409, title: Conflict
793 """
794 code = 409
795 title = 'Conflict'
796 explanation = ('There was a conflict when trying to complete '
797 'your request.')
799class HTTPGone(HTTPClientError):
800 """
801 subclass of :class:`~HTTPClientError`
803 This indicates that the requested resource is no longer available
804 at the server and no forwarding address is known.
806 code: 410, title: Gone
807 """
808 code = 410
809 title = 'Gone'
810 explanation = ('This resource is no longer available. No forwarding '
811 'address is given.')
813class HTTPLengthRequired(HTTPClientError):
814 """
815 subclass of :class:`~HTTPClientError`
817 This indicates that the the server refuses to accept the request
818 without a defined Content-Length.
820 code: 411, title: Length Required
821 """
822 code = 411
823 title = 'Length Required'
824 explanation = ('Content-Length header required.')
826class HTTPPreconditionFailed(HTTPClientError):
827 """
828 subclass of :class:`~HTTPClientError`
830 This indicates that the precondition given in one or more of the
831 request-header fields evaluated to false when it was tested on the
832 server.
834 code: 412, title: Precondition Failed
835 """
836 code = 412
837 title = 'Precondition Failed'
838 explanation = ('Request precondition failed.')
840class HTTPRequestEntityTooLarge(HTTPClientError):
841 """
842 subclass of :class:`~HTTPClientError`
844 This indicates that the server is refusing to process a request
845 because the request entity is larger than the server is willing or
846 able to process.
848 code: 413, title: Request Entity Too Large
849 """
850 code = 413
851 title = 'Request Entity Too Large'
852 explanation = ('The body of your request was too large for this server.')
854class HTTPRequestURITooLong(HTTPClientError):
855 """
856 subclass of :class:`~HTTPClientError`
858 This indicates that the server is refusing to service the request
859 because the Request-URI is longer than the server is willing to
860 interpret.
862 code: 414, title: Request-URI Too Long
863 """
864 code = 414
865 title = 'Request-URI Too Long'
866 explanation = ('The request URI was too long for this server.')
868class HTTPUnsupportedMediaType(HTTPClientError):
869 """
870 subclass of :class:`~HTTPClientError`
872 This indicates that the server is refusing to service the request
873 because the entity of the request is in a format not supported by
874 the requested resource for the requested method.
876 code: 415, title: Unsupported Media Type
877 """
878 code = 415
879 title = 'Unsupported Media Type'
880 # override template since we need an environment variable
881 body_template_obj = Template('''\
882The request media type ${CONTENT_TYPE} is not supported by this server.
883<br /><br />
884${detail}''')
886class HTTPRequestRangeNotSatisfiable(HTTPClientError):
887 """
888 subclass of :class:`~HTTPClientError`
890 The server SHOULD return a response with this status code if a
891 request included a Range request-header field, and none of the
892 range-specifier values in this field overlap the current extent
893 of the selected resource, and the request did not include an
894 If-Range request-header field.
896 code: 416, title: Request Range Not Satisfiable
897 """
898 code = 416
899 title = 'Request Range Not Satisfiable'
900 explanation = ('The Range requested is not available.')
902class HTTPExpectationFailed(HTTPClientError):
903 """
904 subclass of :class:`~HTTPClientError`
906 This indidcates that the expectation given in an Expect
907 request-header field could not be met by this server.
909 code: 417, title: Expectation Failed
910 """
911 code = 417
912 title = 'Expectation Failed'
913 explanation = ('Expectation failed.')
915class HTTPUnprocessableEntity(HTTPClientError):
916 """
917 subclass of :class:`~HTTPClientError`
919 This indicates that the server is unable to process the contained
920 instructions.
922 code: 422, title: Unprocessable Entity
923 """
924 ## Note: from WebDAV
925 code = 422
926 title = 'Unprocessable Entity'
927 explanation = 'Unable to process the contained instructions'
929class HTTPLocked(HTTPClientError):
930 """
931 subclass of :class:`~HTTPClientError`
933 This indicates that the resource is locked.
935 code: 423, title: Locked
936 """
937 ## Note: from WebDAV
938 code = 423
939 title = 'Locked'
940 explanation = ('The resource is locked')
942class HTTPFailedDependency(HTTPClientError):
943 """
944 subclass of :class:`~HTTPClientError`
946 This indicates that the method could not be performed because the
947 requested action depended on another action and that action failed.
949 code: 424, title: Failed Dependency
950 """
951 ## Note: from WebDAV
952 code = 424
953 title = 'Failed Dependency'
954 explanation = (
955 'The method could not be performed because the requested '
956 'action dependended on another action and that action failed')
958class HTTPPreconditionRequired(HTTPClientError):
959 """
960 subclass of :class:`~HTTPClientError`
962 This indicates that the origin server requires the request to be
963 conditional. From RFC 6585, "Additional HTTP Status Codes".
965 code: 428, title: Precondition Required
966 """
967 code = 428
968 title = 'Precondition Required'
969 explanation = ('This request is required to be conditional')
971class HTTPTooManyRequests(HTTPClientError):
972 """
973 subclass of :class:`~HTTPClientError`
975 This indicates that the client has sent too many requests in a
976 given amount of time. Useful for rate limiting.
978 From RFC 6585, "Additional HTTP Status Codes".
980 code: 429, title: Too Many Requests
981 """
982 code = 429
983 title = 'Too Many Requests'
984 explanation = (
985 'The client has sent too many requests in a given amount of time')
987class HTTPRequestHeaderFieldsTooLarge(HTTPClientError):
988 """
989 subclass of :class:`~HTTPClientError`
991 This indicates that the server is unwilling to process the request
992 because its header fields are too large. The request may be resubmitted
993 after reducing the size of the request header fields.
995 From RFC 6585, "Additional HTTP Status Codes".
997 code: 431, title: Request Header Fields Too Large
998 """
999 code = 431
1000 title = 'Request Header Fields Too Large'
1001 explanation = (
1002 'The request header fields were too large')
1004class HTTPUnavailableForLegalReasons(HTTPClientError):
1005 """
1006 subclass of :class:`~HTTPClientError`
1008 This indicates that the server is unable to process the request
1009 because of legal reasons, e.g. censorship or government-mandated
1010 blocked access.
1012 From the draft "A New HTTP Status Code for Legally-restricted Resources"
1013 by Tim Bray:
1015 https://tools.ietf.org/html/draft-tbray-http-legally-restricted-status-00
1017 code: 451, title: Unavailable For Legal Reasons
1018 """
1019 code = 451
1020 title = 'Unavailable For Legal Reasons'
1021 explanation = ('The resource is not available due to legal reasons.')
1023############################################################
1024## 5xx Server Error
1025############################################################
1026# Response status codes beginning with the digit "5" indicate cases in
1027# which the server is aware that it has erred or is incapable of
1028# performing the request. Except when responding to a HEAD request, the
1029# server SHOULD include an entity containing an explanation of the error
1030# situation, and whether it is a temporary or permanent condition. User
1031# agents SHOULD display any included entity to the user. These response
1032# codes are applicable to any request method.
1034class HTTPServerError(HTTPError):
1035 """
1036 base class for the 500's, where the server is in-error
1038 This is an error condition in which the server is presumed to be
1039 in-error. This is usually unexpected, and thus requires a traceback;
1040 ideally, opening a support ticket for the customer. Unless specialized,
1041 this is a '500 Internal Server Error'
1042 """
1043 code = 500
1044 title = 'Internal Server Error'
1045 explanation = (
1046 'The server has either erred or is incapable of performing\r\n'
1047 'the requested operation.\r\n')
1049class HTTPInternalServerError(HTTPServerError):
1050 pass
1052class HTTPNotImplemented(HTTPServerError):
1053 """
1054 subclass of :class:`~HTTPServerError`
1056 This indicates that the server does not support the functionality
1057 required to fulfill the request.
1059 code: 501, title: Not Implemented
1060 """
1061 code = 501
1062 title = 'Not Implemented'
1063 body_template_obj = Template('''
1064The request method ${REQUEST_METHOD} is not implemented for this server. <br /><br />
1065${detail}''')
1067class HTTPBadGateway(HTTPServerError):
1068 """
1069 subclass of :class:`~HTTPServerError`
1071 This indicates that the server, while acting as a gateway or proxy,
1072 received an invalid response from the upstream server it accessed
1073 in attempting to fulfill the request.
1075 code: 502, title: Bad Gateway
1076 """
1077 code = 502
1078 title = 'Bad Gateway'
1079 explanation = ('Bad gateway.')
1081class HTTPServiceUnavailable(HTTPServerError):
1082 """
1083 subclass of :class:`~HTTPServerError`
1085 This indicates that the server is currently unable to handle the
1086 request due to a temporary overloading or maintenance of the server.
1088 code: 503, title: Service Unavailable
1089 """
1090 code = 503
1091 title = 'Service Unavailable'
1092 explanation = ('The server is currently unavailable. '
1093 'Please try again at a later time.')
1095class HTTPGatewayTimeout(HTTPServerError):
1096 """
1097 subclass of :class:`~HTTPServerError`
1099 This indicates that the server, while acting as a gateway or proxy,
1100 did not receive a timely response from the upstream server specified
1101 by the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server
1102 (e.g. DNS) it needed to access in attempting to complete the request.
1104 code: 504, title: Gateway Timeout
1105 """
1106 code = 504
1107 title = 'Gateway Timeout'
1108 explanation = ('The gateway has timed out.')
1110class HTTPVersionNotSupported(HTTPServerError):
1111 """
1112 subclass of :class:`~HTTPServerError`
1114 This indicates that the server does not support, or refuses to
1115 support, the HTTP protocol version that was used in the request
1116 message.
1118 code: 505, title: HTTP Version Not Supported
1119 """
1120 code = 505
1121 title = 'HTTP Version Not Supported'
1122 explanation = ('The HTTP version is not supported.')
1124class HTTPInsufficientStorage(HTTPServerError):
1125 """
1126 subclass of :class:`~HTTPServerError`
1128 This indicates that the server does not have enough space to save
1129 the resource.
1131 code: 507, title: Insufficient Storage
1132 """
1133 code = 507
1134 title = 'Insufficient Storage'
1135 explanation = ('There was not enough space to save the resource')
1137class HTTPNetworkAuthenticationRequired(HTTPServerError):
1138 """
1139 subclass of :class:`~HTTPServerError`
1141 This indicates that the client needs to authenticate to gain
1142 network access. From RFC 6585, "Additional HTTP Status Codes".
1144 code: 511, title: Network Authentication Required
1145 """
1146 code = 511
1147 title = 'Network Authentication Required'
1148 explanation = ('Network authentication is required')
1150class HTTPExceptionMiddleware(object):
1151 """
1152 Middleware that catches exceptions in the sub-application. This
1153 does not catch exceptions in the app_iter; only during the initial
1154 calling of the application.
1156 This should be put *very close* to applications that might raise
1157 these exceptions. This should not be applied globally; letting
1158 *expected* exceptions raise through the WSGI stack is dangerous.
1159 """
1161 def __init__(self, application):
1162 self.application = application
1163 def __call__(self, environ, start_response):
1164 try:
1165 return self.application(environ, start_response)
1166 except HTTPException:
1167 parent_exc_info = sys.exc_info()
1168 def repl_start_response(status, headers, exc_info=None):
1169 if exc_info is None:
1170 exc_info = parent_exc_info
1171 return start_response(status, headers, exc_info)
1172 return parent_exc_info[1](environ, repl_start_response)
1174try:
1175 from paste import httpexceptions
1176except ImportError: # pragma: no cover
1177 # Without Paste we don't need to do this fixup
1178 pass
1179else: # pragma: no cover
1180 for name in dir(httpexceptions):
1181 obj = globals().get(name)
1182 if (obj and isinstance(obj, type) and issubclass(obj, HTTPException)
1183 and obj is not HTTPException
1184 and obj is not WSGIHTTPException):
1185 obj.__bases__ = obj.__bases__ + (getattr(httpexceptions, name),)
1186 del name, obj, httpexceptions
1188__all__ = ['HTTPExceptionMiddleware', 'status_map']
1189status_map={}
1190for name, value in list(globals().items()):
1191 if (isinstance(value, (type, class_types)) and
1192 issubclass(value, HTTPException)
1193 and not name.startswith('_')):
1194 __all__.append(name)
1195 if all((
1196 getattr(value, 'code', None),
1197 value not in (HTTPRedirection, HTTPClientError, HTTPServerError),
1198 issubclass(
1199 value,
1200 (HTTPOk, HTTPRedirection, HTTPClientError, HTTPServerError)
1201 )
1202 )):
1203 status_map[value.code]=value
1204 if hasattr(value, 'explanation'):
1205 value.explanation = ' '.join(value.explanation.strip().split())
1206del name, value