Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/werkzeug/exceptions.py
diff options
context:
space:
mode:
Diffstat (limited to 'werkzeug/exceptions.py')
-rw-r--r--werkzeug/exceptions.py459
1 files changed, 459 insertions, 0 deletions
diff --git a/werkzeug/exceptions.py b/werkzeug/exceptions.py
new file mode 100644
index 0000000..cce1230
--- /dev/null
+++ b/werkzeug/exceptions.py
@@ -0,0 +1,459 @@
+# -*- 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) 2010 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, _get_environ
+
+
+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
+
+ @classmethod
+ 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
+
+ @property
+ def name(self):
+ """The status name."""
+ return HTTP_STATUS_CODES[self.code]
+
+ def get_description(self, environ):
+ """Get the description."""
+ environ = _get_environ(environ)
+ 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.
+ """
+ # lazily imported for various reasons. For one, we 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 circular dependencies when bootstrapping the module.
+ environ = _get_environ(environ)
+ 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)
+
+ def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
+ if 'description' in self.__dict__:
+ txt = self.description
+ else:
+ txt = self.name
+ return '%d: %s' % (self.code, txt)
+
+ def __repr__(self):
+ return '<%s \'%s\'>' % (self.__class__.__name__, self)
+
+
+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: # pragma: no cover
+ 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 an 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