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