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