/packages/Werkzeug/werkzeug/exceptions.py
Python | 445 lines | 318 code | 21 blank | 106 comment | 6 complexity | a680096d3736846e0261d76468b1cef2 MD5 | raw file
- # -*- coding: utf-8 -*-
- """
- werkzeug.exceptions
- ~~~~~~~~~~~~~~~~~~~
- This module implements a number of Python exceptions you can raise from
- within your views to trigger a standard non-200 response.
- Usage Example
- -------------
- ::
- from werkzeug import BaseRequest, responder
- from werkzeug.exceptions import HTTPException, NotFound
- def view(request):
- raise NotFound()
- @responder
- def application(environ, start_response):
- request = BaseRequest(environ)
- try:
- return view(request)
- except HTTPException, e:
- return e
- As you can see from this example those exceptions are callable WSGI
- applications. Because of Python 2.4 compatibility those do not extend
- from the response objects but only from the python exception class.
- As a matter of fact they are not Werkzeug response objects. However you
- can get a response object by calling ``get_response()`` on a HTTP
- exception.
- Keep in mind that you have to pass an environment to ``get_response()``
- because some errors fetch additional information from the WSGI
- environment.
- If you want to hook in a different exception page to say, a 404 status
- code, you can add a second except for a specific subclass of an error::
- @responder
- def application(environ, start_response):
- request = BaseRequest(environ)
- try:
- return view(request)
- except NotFound, e:
- return not_found(request)
- except HTTPException, e:
- return e
- :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
- :license: BSD, see LICENSE for more details.
- """
- import sys
- from werkzeug._internal import HTTP_STATUS_CODES
- class HTTPException(Exception):
- """
- Baseclass for all HTTP exceptions. This exception can be called as WSGI
- application to render a default error page or you can catch the subclasses
- of it independently and render nicer error messages.
- """
- code = None
- description = None
- def __init__(self, description=None):
- Exception.__init__(self, '%d %s' % (self.code, self.name))
- if description is not None:
- self.description = description
- def wrap(cls, exception, name=None):
- """
- This method returns a new subclass of the exception provided that
- also is a subclass of `BadRequest`.
- """
- class newcls(cls, exception):
- def __init__(self, arg=None, description=None):
- cls.__init__(self, description)
- exception.__init__(self, arg)
- newcls.__module__ = sys._getframe(1).f_globals.get('__name__')
- newcls.__name__ = name or cls.__name__ + exception.__name__
- return newcls
- wrap = classmethod(wrap)
- def name(self):
- """The status name."""
- return HTTP_STATUS_CODES[self.code]
- name = property(name, doc=name.__doc__)
- def get_description(self, environ):
- """Get the description."""
- return self.description
- def get_body(self, environ):
- """Get the HTML body."""
- return (
- '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
- '<title>%(code)s %(name)s</title>\n'
- '<h1>%(name)s</h1>\n'
- '%(description)s\n'
- ) % {
- 'code': self.code,
- 'name': escape(self.name),
- 'description': self.get_description(environ)
- }
- def get_headers(self, environ):
- """Get a list of headers."""
- return [('Content-Type', 'text/html')]
- def get_response(self, environ):
- """Get a response object.
- :param environ: the environ for the request.
- :return: a :class:`BaseResponse` object or a subclass thereof.
- """
- # lazyly imported for various reasons. For one can use the exceptions
- # with custom responses (testing exception instances against types) and
- # so we don't ever have to import the wrappers, but also because there
- # are ciruclar dependencies when bootstrapping the module.
- from werkzeug.wrappers import BaseResponse
- headers = self.get_headers(environ)
- return BaseResponse(self.get_body(environ), self.code, headers)
- def __call__(self, environ, start_response):
- """Call the exception as WSGI application.
- :param environ: the WSGI environment.
- :param start_response: the response callable provided by the WSGI
- server.
- """
- response = self.get_response(environ)
- return response(environ, start_response)
- class _ProxyException(HTTPException):
- """An HTTP exception that expands renders a WSGI application on error."""
- def __init__(self, response):
- Exception.__init__(self, 'proxy exception for %r' % response)
- self.response = response
- def get_response(self, environ):
- return self.response
- class BadRequest(HTTPException):
- """*400* `Bad Request`
- Raise if the browser sends something to the application the application
- or server cannot handle.
- """
- code = 400
- description = (
- '<p>The browser (or proxy) sent a request that this server could '
- 'not understand.</p>'
- )
- class Unauthorized(HTTPException):
- """*401* `Unauthorized`
- Raise if the user is not authorized. Also used if you want to use HTTP
- basic auth.
- """
- code = 401
- description = (
- '<p>The server could not verify that you are authorized to access '
- 'the URL requested. You either supplied the wrong credentials (e.g. '
- 'a bad password), or your browser doesn\'t understand how to supply '
- 'the credentials required.</p><p>In case you are allowed to request '
- 'the document, please check your user-id and password and try '
- 'again.</p>'
- )
- class Forbidden(HTTPException):
- """*403* `Forbidden`
- Raise if the user doesn't have the permission for the requested resource
- but was authenticated.
- """
- code = 403
- description = (
- '<p>You don\'t have the permission to access the requested resource. '
- 'It is either read-protected or not readable by the server.</p>'
- )
- class NotFound(HTTPException):
- """*404* `Not Found`
- Raise if a resource does not exist and never existed.
- """
- code = 404
- description = (
- '<p>The requested URL was not found on the server.</p>'
- '<p>If you entered the URL manually please check your spelling and '
- 'try again.</p>'
- )
- class MethodNotAllowed(HTTPException):
- """*405* `Method Not Allowed`
- Raise if the server used a method the resource does not handle. For
- example `POST` if the resource is view only. Especially useful for REST.
- The first argument for this exception should be a list of allowed methods.
- Strictly speaking the response would be invalid if you don't provide valid
- methods in the header which you can do with that list.
- """
- code = 405
- def __init__(self, valid_methods=None, description=None):
- """Takes an optional list of valid http methods
- starting with werkzeug 0.3 the list will be mandatory."""
- HTTPException.__init__(self, description)
- self.valid_methods = valid_methods
- def get_headers(self, environ):
- headers = HTTPException.get_headers(self, environ)
- if self.valid_methods:
- headers.append(('Allow', ', '.join(self.valid_methods)))
- return headers
- def get_description(self, environ):
- m = escape(environ.get('REQUEST_METHOD', 'GET'))
- return '<p>The method %s is not allowed for the requested URL.</p>' % m
- class NotAcceptable(HTTPException):
- """*406* `Not Acceptable`
- Raise if the server can't return any content conforming to the
- `Accept` headers of the client.
- """
- code = 406
- description = (
- '<p>The resource identified by the request is only capable of '
- 'generating response entities which have content characteristics '
- 'not acceptable according to the accept headers sent in the '
- 'request.</p>'
- )
- class RequestTimeout(HTTPException):
- """*408* `Request Timeout`
- Raise to signalize a timeout.
- """
- code = 408
- description = (
- '<p>The server closed the network connection because the browser '
- 'didn\'t finish the request within the specified time.</p>'
- )
- class Gone(HTTPException):
- """*410* `Gone`
- Raise if a resource existed previously and went away without new location.
- """
- code = 410
- description = (
- '<p>The requested URL is no longer available on this server and '
- 'there is no forwarding address.</p><p>If you followed a link '
- 'from a foreign page, please contact the author of this page.'
- )
- class LengthRequired(HTTPException):
- """*411* `Length Required`
- Raise if the browser submitted data but no ``Content-Length`` header which
- is required for the kind of processing the server does.
- """
- code = 411
- description = (
- '<p>A request with this method requires a valid <code>Content-'
- 'Length</code> header.</p>'
- )
- class PreconditionFailed(HTTPException):
- """*412* `Precondition Failed`
- Status code used in combination with ``If-Match``, ``If-None-Match``, or
- ``If-Unmodified-Since``.
- """
- code = 412
- description = (
- '<p>The precondition on the request for the URL failed positive '
- 'evaluation.</p>'
- )
- class RequestEntityTooLarge(HTTPException):
- """*413* `Request Entity Too Large`
- The status code one should return if the data submitted exceeded a given
- limit.
- """
- code = 413
- description = (
- '<p>The data value transmitted exceeds the capacity limit.</p>'
- )
- class RequestURITooLarge(HTTPException):
- """*414* `Request URI Too Large`
- Like *413* but for too long URLs.
- """
- code = 414
- description = (
- '<p>The length of the requested URL exceeds the capacity limit '
- 'for this server. The request cannot be processed.</p>'
- )
- class UnsupportedMediaType(HTTPException):
- """*415* `Unsupported Media Type`
- The status code returned if the server is unable to handle the media type
- the client transmitted.
- """
- code = 415
- description = (
- '<p>The server does not support the media type transmitted in '
- 'the request.</p>'
- )
- class InternalServerError(HTTPException):
- """*500* `Internal Server Error`
- Raise if an internal server error occurred. This is a good fallback if an
- unknown error occurred in the dispatcher.
- """
- code = 500
- description = (
- '<p>The server encountered an internal error and was unable to '
- 'complete your request. Either the server is overloaded or there '
- 'is an error in the application.</p>'
- )
- class NotImplemented(HTTPException):
- """*501* `Not Implemented`
- Raise if the application does not support the action requested by the
- browser.
- """
- code = 501
- description = (
- '<p>The server does not support the action requested by the '
- 'browser.</p>'
- )
- class BadGateway(HTTPException):
- """*502* `Bad Gateway`
- If you do proxying in your application you should return this status code
- if you received an invalid response from the upstream server it accessed
- in attempting to fulfill the request.
- """
- code = 502
- description = (
- '<p>The proxy server received an invalid response from an upstream '
- 'server.</p>'
- )
- class ServiceUnavailable(HTTPException):
- """*503* `Service Unavailable`
- Status code you should return if a service is temporarily unavailable.
- """
- code = 503
- description = (
- '<p>The server is temporarily unable to service your request due to '
- 'maintenance downtime or capacity problems. Please try again '
- 'later.</p>'
- )
- default_exceptions = {}
- __all__ = ['HTTPException']
- def _find_exceptions():
- for name, obj in globals().iteritems():
- try:
- if getattr(obj, 'code', None) is not None:
- default_exceptions[obj.code] = obj
- __all__.append(obj.__name__)
- except TypeError:
- continue
- _find_exceptions()
- del _find_exceptions
- #: raised by the request functions if they were unable to decode the
- #: incoming data properly.
- HTTPUnicodeError = BadRequest.wrap(UnicodeError, 'HTTPUnicodeError')
- class Aborter(object):
- """
- When passed a dict of code -> exception items it can be used as
- callable that raises exceptions. If the first argument to the
- callable is a integer it will be looked up in the mapping, if it's
- a WSGI application it will be raised in a proxy exception.
- The rest of the arguments are forwarded to the exception constructor.
- """
- def __init__(self, mapping=None, extra=None):
- if mapping is None:
- mapping = default_exceptions
- self.mapping = dict(mapping)
- if extra is not None:
- self.mapping.update(extra)
- def __call__(self, code, *args, **kwargs):
- if not args and not kwargs and not isinstance(code, (int, long)):
- raise _ProxyException(code)
- if code not in self.mapping:
- raise LookupError('no exception for %r' % code)
- raise self.mapping[code](*args, **kwargs)
- abort = Aborter()
- # imported here because of circular dependencies of werkzeug.utils
- from werkzeug.utils import escape