debian-quixote3/quixote/errors.py

204 lines
6.7 KiB
Python

"""quixote.errors
Exception classes used by Quixote
"""
from quixote.html import htmltext, htmlescape
class PublishError(Exception):
"""PublishError exceptions are raised due to some problem with the
data provided by the client and are raised during the publishing
process. Quixote will abort the current request and return an error
page to the client.
public_msg should be a user-readable message that reveals no
inner workings of your application; it will always be shown.
private_msg will only be shown if the config option DISPLAY_EXCEPTIONS is
true; Quixote uses this to give you more detail about why the error
occurred. You might want to use it for similar, application-specific
information. (DISPLAY_EXCEPTIONS should always be false in a production
environment, since these details about the inner workings of your
application could conceivably be useful to attackers.)
The formatting done by the Quixote versions of these exceptions is
very simple. Applications will probably wish to raise application
specific subclasses which do more sophisticated formatting or provide
a _q_except handler to format the exception.
"""
status_code = 400 # bad request
title = "Publishing error"
description = ("An error occured while generating the requested page. "
"For help, please contact the site administrator.")
def __init__(self, public_msg=None, private_msg=None):
self.public_msg = public_msg
self.private_msg = private_msg # cleared if DISPLAY_EXCEPTIONS is false
def __str__(self):
return self.private_msg or self.public_msg or "???"
def format(self):
msg = htmlescape(self.title)
if self.public_msg:
msg = msg + ": " + self.public_msg
if self.private_msg:
msg = msg + ": " + self.private_msg
return msg
class TraversalError(PublishError):
"""
Raised when a client attempts to access a resource that does not
exist or is otherwise unavailable to them (eg. a Python function
not listed in its module's _q_exports list).
path should be the path to the requested resource; if not
supplied, the current request object will be fetched and its
get_path() method called.
"""
status_code = 404 # not found
title = "Page not found"
description = ("The requested link does not exist on this site. If "
"you arrived here by following a link from an external "
"page, please inform that page's maintainer.")
def __init__(self, public_msg=None, private_msg=None, path=None):
PublishError.__init__(self, public_msg, private_msg)
if path is None:
import quixote
path = quixote.get_request().get_path()
self.path = path
def format(self):
msg = htmlescape(self.title) + ": " + self.path
if self.public_msg:
msg = msg + ": " + self.public_msg
if self.private_msg:
msg = msg + ": " + self.private_msg
return msg
class RequestError(PublishError):
"""
Raised when Quixote is unable to parse an HTTP request (or its CGI
representation). This is a lower-level error than QueryError -- it
either means that Quixote is not smart enough to handle the request
being passed to it, or the user-agent is broken and/or malicious.
"""
status_code = 400
title = "Invalid request"
description = "Unable to parse HTTP request."
class QueryError(PublishError):
"""Should be raised if bad data was provided in the query part of a
URL or in the content of a POST request. What constitutes bad data is
solely application dependent (eg: letters in a form field when the
application expects a number).
"""
status_code = 400
title = "Invalid query"
description = ("An error occurred while handling your request. The "
"query data provided as part of the request is invalid.")
class AccessError(PublishError):
"""Should be raised if the client does not have access to the
requested resource. Usually applications will raise this error from
an _q_access method.
"""
status_code = 403
title = "Access denied"
description = ("An error occurred while handling your request. "
"Access to the requested resource was not permitted.")
class MethodNotAllowedError(PublishError):
status_code = 405
title = "Method not allowed"
description = ("The method specified in the request is not allowed for "
"the resource identified by the URI.")
def __init__(self, allowed_methods):
self.allowed_methods = allowed_methods
self.public_msg = self.private_msg = None
def format(self):
import quixote
allowed_methods = ', '.join(self.allowed_methods)
quixote.get_response().set_header('Allow', allowed_methods)
return 'Allowed methods are: %s' % allowed_methods
PAGE_TEMPLATE = htmltext('''\
<!DOCTYPE html>
<html>
<head>
<title>%(title)s</title>
<style type="text/css">
body {
font: 13px arial,helvetica,clean,sans-serif;
}
h1 {
font-size: 24px;
}
p {
width: 40em;
}
</style>
</head>
<body>
<h1>%(title)s</h1>
%(body)s
</body>
</html>
''')
def format_page(title, body):
"""Used for Quixote generated HTML pages. This function can be replaced to
ensure you application has a consistent look to web pages. Be aware
that this function should do a minimal amount of processing since it
can be called when the server encounters an error.
"""
return PAGE_TEMPLATE % dict(title=title, body=body)
ERROR_BODY = htmltext('''
<p>%(description)s</p>
<p>%(details)s</p>
''')
def format_error_page(title, description, details):
body = ERROR_BODY % dict(description=description, details=details)
return format_page(title=title, body=body)
# Error message to dispay for non-PublishError exception when
# DISPLAY_EXCEPTIONS is set to None. If DISPLAY_EXCEPTIONS is not
# None than a traceback will displayed.
INTERNAL_ERROR_MESSAGE = format_error_page(
title='Internal Server Error',
description=('An internal error occured while handling your page '
'request.'),
details=('The error has been logged but you may wish to contact the '
'server administrator and inform them of the time the error '
'occurred, and anything you might have done to trigger the '
'error.'))
def format_publish_error(exc):
"""(exc : PublishError) -> string
Format a PublishError exception as a web page.
"""
return format_error_page(title='Error: %s' % exc.title,
description=exc.description,
details=exc.format())