Back to index

moin  1.9.0~rc2
exceptions.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 """
00003     werkzeug.exceptions
00004     ~~~~~~~~~~~~~~~~~~~
00005 
00006     This module implements a number of Python exceptions you can raise from
00007     within your views to trigger a standard non-200 response.
00008 
00009 
00010     Usage Example
00011     -------------
00012 
00013     ::
00014 
00015         from werkzeug import BaseRequest, responder
00016         from werkzeug.exceptions import HTTPException, NotFound
00017 
00018         def view(request):
00019             raise NotFound()
00020 
00021         @responder
00022         def application(environ, start_response):
00023             request = BaseRequest(environ)
00024             try:
00025                 return view(request)
00026             except HTTPException, e:
00027                 return e
00028 
00029 
00030     As you can see from this example those exceptions are callable WSGI
00031     applications.  Because of Python 2.4 compatibility those do not extend
00032     from the response objects but only from the python exception class.
00033 
00034     As a matter of fact they are not Werkzeug response objects.  However you
00035     can get a response object by calling ``get_response()`` on a HTTP
00036     exception.
00037 
00038     Keep in mind that you have to pass an environment to ``get_response()``
00039     because some errors fetch additional information from the WSGI
00040     environment.
00041 
00042     If you want to hook in a different exception page to say, a 404 status
00043     code, you can add a second except for a specific subclass of an error::
00044 
00045         @responder
00046         def application(environ, start_response):
00047             request = BaseRequest(environ)
00048             try:
00049                 return view(request)
00050             except NotFound, e:
00051                 return not_found(request)
00052             except HTTPException, e:
00053                 return e
00054 
00055 
00056     :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
00057     :license: BSD, see LICENSE for more details.
00058 """
00059 import sys
00060 from werkzeug._internal import HTTP_STATUS_CODES
00061 
00062 
00063 class HTTPException(Exception):
00064     """
00065     Baseclass for all HTTP exceptions.  This exception can be called as WSGI
00066     application to render a default error page or you can catch the subclasses
00067     of it independently and render nicer error messages.
00068     """
00069 
00070     code = None
00071     description = None
00072 
00073     def __init__(self, description=None):
00074         Exception.__init__(self, '%d %s' % (self.code, self.name))
00075         if description is not None:
00076             self.description = description
00077 
00078     def wrap(cls, exception, name=None):
00079         """
00080         This method returns a new subclass of the exception provided that
00081         also is a subclass of `BadRequest`.
00082         """
00083         class newcls(cls, exception):
00084             def __init__(self, arg=None, description=None):
00085                 cls.__init__(self, description)
00086                 exception.__init__(self, arg)
00087         newcls.__module__ = sys._getframe(1).f_globals.get('__name__')
00088         newcls.__name__ = name or cls.__name__ + exception.__name__
00089         return newcls
00090     wrap = classmethod(wrap)
00091 
00092     def name(self):
00093         """The status name."""
00094         return HTTP_STATUS_CODES[self.code]
00095     name = property(name, doc=name.__doc__)
00096 
00097     def get_description(self, environ):
00098         """Get the description."""
00099         return self.description
00100 
00101     def get_body(self, environ):
00102         """Get the HTML body."""
00103         return (
00104             '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
00105             '<title>%(code)s %(name)s</title>\n'
00106             '<h1>%(name)s</h1>\n'
00107             '%(description)s\n'
00108         ) % {
00109             'code':         self.code,
00110             'name':         escape(self.name),
00111             'description':  self.get_description(environ)
00112         }
00113 
00114     def get_headers(self, environ):
00115         """Get a list of headers."""
00116         return [('Content-Type', 'text/html')]
00117 
00118     def get_response(self, environ):
00119         """Get a response object.
00120 
00121         :param environ: the environ for the request.
00122         :return: a :class:`BaseResponse` object or a subclass thereof.
00123         """
00124         # lazyly imported for various reasons.  For one can use the exceptions
00125         # with custom responses (testing exception instances against types) and
00126         # so we don't ever have to import the wrappers, but also because there
00127         # are ciruclar dependencies when bootstrapping the module.
00128         from werkzeug.wrappers import BaseResponse
00129         headers = self.get_headers(environ)
00130         return BaseResponse(self.get_body(environ), self.code, headers)
00131 
00132     def __call__(self, environ, start_response):
00133         """Call the exception as WSGI application.
00134 
00135         :param environ: the WSGI environment.
00136         :param start_response: the response callable provided by the WSGI
00137                                server.
00138         """
00139         response = self.get_response(environ)
00140         return response(environ, start_response)
00141 
00142 
00143 class _ProxyException(HTTPException):
00144     """An HTTP exception that expands renders a WSGI application on error."""
00145 
00146     def __init__(self, response):
00147         Exception.__init__(self, 'proxy exception for %r' % response)
00148         self.response = response
00149 
00150     def get_response(self, environ):
00151         return self.response
00152 
00153 
00154 class BadRequest(HTTPException):
00155     """*400* `Bad Request`
00156 
00157     Raise if the browser sends something to the application the application
00158     or server cannot handle.
00159     """
00160     code = 400
00161     description = (
00162         '<p>The browser (or proxy) sent a request that this server could '
00163         'not understand.</p>'
00164     )
00165 
00166 
00167 class Unauthorized(HTTPException):
00168     """*401* `Unauthorized`
00169 
00170     Raise if the user is not authorized.  Also used if you want to use HTTP
00171     basic auth.
00172     """
00173     code = 401
00174     description = (
00175         '<p>The server could not verify that you are authorized to access '
00176         'the URL requested.  You either supplied the wrong credentials (e.g. '
00177         'a bad password), or your browser doesn\'t understand how to supply '
00178         'the credentials required.</p><p>In case you are allowed to request '
00179         'the document, please check your user-id and password and try '
00180         'again.</p>'
00181     )
00182 
00183 
00184 class Forbidden(HTTPException):
00185     """*403* `Forbidden`
00186 
00187     Raise if the user doesn't have the permission for the requested resource
00188     but was authenticated.
00189     """
00190     code = 403
00191     description = (
00192         '<p>You don\'t have the permission to access the requested resource. '
00193         'It is either read-protected or not readable by the server.</p>'
00194     )
00195 
00196 
00197 class NotFound(HTTPException):
00198     """*404* `Not Found`
00199 
00200     Raise if a resource does not exist and never existed.
00201     """
00202     code = 404
00203     description = (
00204         '<p>The requested URL was not found on the server.</p>'
00205         '<p>If you entered the URL manually please check your spelling and '
00206         'try again.</p>'
00207     )
00208 
00209 
00210 class MethodNotAllowed(HTTPException):
00211     """*405* `Method Not Allowed`
00212 
00213     Raise if the server used a method the resource does not handle.  For
00214     example `POST` if the resource is view only.  Especially useful for REST.
00215 
00216     The first argument for this exception should be a list of allowed methods.
00217     Strictly speaking the response would be invalid if you don't provide valid
00218     methods in the header which you can do with that list.
00219     """
00220     code = 405
00221 
00222     def __init__(self, valid_methods=None, description=None):
00223         """Takes an optional list of valid http methods
00224         starting with werkzeug 0.3 the list will be mandatory."""
00225         HTTPException.__init__(self, description)
00226         self.valid_methods = valid_methods
00227 
00228     def get_headers(self, environ):
00229         headers = HTTPException.get_headers(self, environ)
00230         if self.valid_methods:
00231             headers.append(('Allow', ', '.join(self.valid_methods)))
00232         return headers
00233 
00234     def get_description(self, environ):
00235         m = escape(environ.get('REQUEST_METHOD', 'GET'))
00236         return '<p>The method %s is not allowed for the requested URL.</p>' % m
00237 
00238 
00239 class NotAcceptable(HTTPException):
00240     """*406* `Not Acceptable`
00241 
00242     Raise if the server can't return any content conforming to the
00243     `Accept` headers of the client.
00244     """
00245     code = 406
00246 
00247     description = (
00248         '<p>The resource identified by the request is only capable of '
00249         'generating response entities which have content characteristics '
00250         'not acceptable according to the accept headers sent in the '
00251         'request.</p>'
00252         )
00253 
00254 
00255 class RequestTimeout(HTTPException):
00256     """*408* `Request Timeout`
00257 
00258     Raise to signalize a timeout.
00259     """
00260     code = 408
00261     description = (
00262         '<p>The server closed the network connection because the browser '
00263         'didn\'t finish the request within the specified time.</p>'
00264     )
00265 
00266 
00267 class Gone(HTTPException):
00268     """*410* `Gone`
00269 
00270     Raise if a resource existed previously and went away without new location.
00271     """
00272     code = 410
00273     description = (
00274         '<p>The requested URL is no longer available on this server and '
00275         'there is no forwarding address.</p><p>If you followed a link '
00276         'from a foreign page, please contact the author of this page.'
00277     )
00278 
00279 
00280 class LengthRequired(HTTPException):
00281     """*411* `Length Required`
00282 
00283     Raise if the browser submitted data but no ``Content-Length`` header which
00284     is required for the kind of processing the server does.
00285     """
00286     code = 411
00287     description = (
00288         '<p>A request with this method requires a valid <code>Content-'
00289         'Length</code> header.</p>'
00290     )
00291 
00292 
00293 class PreconditionFailed(HTTPException):
00294     """*412* `Precondition Failed`
00295 
00296     Status code used in combination with ``If-Match``, ``If-None-Match``, or
00297     ``If-Unmodified-Since``.
00298     """
00299     code = 412
00300     description = (
00301         '<p>The precondition on the request for the URL failed positive '
00302         'evaluation.</p>'
00303     )
00304 
00305 
00306 class RequestEntityTooLarge(HTTPException):
00307     """*413* `Request Entity Too Large`
00308 
00309     The status code one should return if the data submitted exceeded a given
00310     limit.
00311     """
00312     code = 413
00313     description = (
00314         '<p>The data value transmitted exceeds the capacity limit.</p>'
00315     )
00316 
00317 
00318 class RequestURITooLarge(HTTPException):
00319     """*414* `Request URI Too Large`
00320 
00321     Like *413* but for too long URLs.
00322     """
00323     code = 414
00324     description = (
00325         '<p>The length of the requested URL exceeds the capacity limit '
00326         'for this server.  The request cannot be processed.</p>'
00327     )
00328 
00329 
00330 class UnsupportedMediaType(HTTPException):
00331     """*415* `Unsupported Media Type`
00332 
00333     The status code returned if the server is unable to handle the media type
00334     the client transmitted.
00335     """
00336     code = 415
00337     description = (
00338         '<p>The server does not support the media type transmitted in '
00339         'the request.</p>'
00340     )
00341 
00342 
00343 class InternalServerError(HTTPException):
00344     """*500* `Internal Server Error`
00345 
00346     Raise if an internal server error occurred.  This is a good fallback if an
00347     unknown error occurred in the dispatcher.
00348     """
00349     code = 500
00350     description = (
00351         '<p>The server encountered an internal error and was unable to '
00352         'complete your request.  Either the server is overloaded or there '
00353         'is an error in the application.</p>'
00354     )
00355 
00356 
00357 class NotImplemented(HTTPException):
00358     """*501* `Not Implemented`
00359 
00360     Raise if the application does not support the action requested by the
00361     browser.
00362     """
00363     code = 501
00364     description = (
00365         '<p>The server does not support the action requested by the '
00366         'browser.</p>'
00367     )
00368 
00369 
00370 class BadGateway(HTTPException):
00371     """*502* `Bad Gateway`
00372 
00373     If you do proxying in your application you should return this status code
00374     if you received an invalid response from the upstream server it accessed
00375     in attempting to fulfill the request.
00376     """
00377     code = 502
00378     description = (
00379         '<p>The proxy server received an invalid response from an upstream '
00380         'server.</p>'
00381     )
00382 
00383 
00384 class ServiceUnavailable(HTTPException):
00385     """*503* `Service Unavailable`
00386 
00387     Status code you should return if a service is temporarily unavailable.
00388     """
00389     code = 503
00390     description = (
00391         '<p>The server is temporarily unable to service your request due to '
00392         'maintenance downtime or capacity problems.  Please try again '
00393         'later.</p>'
00394     )
00395 
00396 
00397 default_exceptions = {}
00398 __all__ = ['HTTPException']
00399 
00400 def _find_exceptions():
00401     for name, obj in globals().iteritems():
00402         try:
00403             if getattr(obj, 'code', None) is not None:
00404                 default_exceptions[obj.code] = obj
00405                 __all__.append(obj.__name__)
00406         except TypeError:
00407             continue
00408 _find_exceptions()
00409 del _find_exceptions
00410 
00411 
00412 #: raised by the request functions if they were unable to decode the
00413 #: incoming data properly.
00414 HTTPUnicodeError = BadRequest.wrap(UnicodeError, 'HTTPUnicodeError')
00415 
00416 
00417 class Aborter(object):
00418     """
00419     When passed a dict of code -> exception items it can be used as
00420     callable that raises exceptions.  If the first argument to the
00421     callable is a integer it will be looked up in the mapping, if it's
00422     a WSGI application it will be raised in a proxy exception.
00423 
00424     The rest of the arguments are forwarded to the exception constructor.
00425     """
00426 
00427     def __init__(self, mapping=None, extra=None):
00428         if mapping is None:
00429             mapping = default_exceptions
00430         self.mapping = dict(mapping)
00431         if extra is not None:
00432             self.mapping.update(extra)
00433 
00434     def __call__(self, code, *args, **kwargs):
00435         if not args and not kwargs and not isinstance(code, (int, long)):
00436             raise _ProxyException(code)
00437         if code not in self.mapping:
00438             raise LookupError('no exception for %r' % code)
00439         raise self.mapping[code](*args, **kwargs)
00440 
00441 abort = Aborter()
00442 
00443 
00444 # imported here because of circular dependencies of werkzeug.utils
00445 from werkzeug.utils import escape