204 lines
6.7 KiB
Python
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())
|