Robert Brewer committed db8d394

Tutorial fixes, plus a bug in _cputil.getErrorPage.

  • Participants
  • Parent commits 0a188d8

Comments (0)

Files changed (12)


     errorPageFile = cherrypy.config.get('errorPage.%s' % code, '')
     if errorPageFile:
-            template = file(errorPageFile, 'rb')
+            template = file(errorPageFile, 'rb').read()
             m = kwargs['message']
             if m:


         self.assertHeader("Content-Type", "application/x-download")
         self.assertHeader("Content-Disposition", "attachment; filename=pdf_file.pdf")
         self.assertEqual(len(self.body), 85698)
+    def test10HTTPErrors(self):
+        self.load_tut_module("tut10_http_errors")
+        self.getPage("/")
+        self.assertInBody("""<a href="toggleTracebacks">""")
+        self.assertInBody("""<a href="/doesNotExist">""")
+        self.assertInBody("""<a href="/error?code=403">""")
+        self.assertInBody("""<a href="/error?code=500">""")
+        self.assertInBody("""<a href="/messageArg">""")
+        tracebacks = cherrypy.config.get('server.showTracebacks')
+        self.getPage("/toggleTracebacks")
+        self.assertEqual(cherrypy.config.get('server.showTracebacks'), not tracebacks)
+        self.assertStatus("302 Found")
+        self.getPage("/error?code=500")
+        self.assertStatus("500 Internal error")
+        self.assertInBody("Server got itself in trouble")
+        self.getPage("/error?code=403")
+        self.assertStatus("403 Forbidden")
+        self.assertInBody("<h2>You can't do that!</h2>")
+        self.getPage("/messageArg")
+        self.assertStatus("500 Internal error")
+        self.assertInBody("If you construct an HTTPError with a 'message'")
 if __name__ == "__main__":


     <title>403 Unauthorized</title>
-        <h2>You can't do that!</h2>
-        <p>This is a custom error page that is read from a file.<p>
+        <h2>You can't do that!</h2>
+        <p>%(message)s</p>
+        <p>This is a custom error page that is read from a file.<p>
+        <p>%(traceback)s</p>
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-  "">
-    <title>403 Unauthorized</title>
-    <body>
-        <h2>You can't do that!</h2>
-        <p>This is a custom error page that is read from a file.<p>
-    </body>


-Tutorial 01 - Hello World
+Tutorial - Hello World
 The most basic (working) CherryPy application possible.


-Tutorial 02 - Multiple methods
+Tutorial - Multiple methods
 This tutorial shows you how to link to other methods of your request


-Tutorial 03 - Passing variables
+Tutorial - Passing variables
 This tutorial shows you how to pass GET/POST variables to methods.


-Tutorial 04 - Multiple objects
+Tutorial - Multiple objects
 This tutorial shows you how to create a site structure through multiple
 possibly nested request handler objects.


-Tutorial 05 - Object inheritance
+Tutorial - Object inheritance
 You are free to derive your request handler classes from any base
 class you wish. In most real-world applications, you will probably


-Tutorial 07 - The default method
+Tutorial - The default method
 Request handler objects can implement a method called "default" that
 is called when no other suitable method/object could be found.


-Tutorial 08 - Sessions
+Tutorial - Sessions
 Storing session data in CherryPy applications is very easy: cherrypy.request
 provides a dictionary called sessionMap that represents the session


-"""Tutorial: File upload"""
+Tutorial: File upload and download
+When a client uploads a file to a CherryPy application, it's placed
+on disk immediately. CherryPy will pass it to your exposed method
+as an argument (see "myFile" below); that arg will have a "file"
+attribute, which is a handle to the temporary uploaded file.
+If you wish to permanently save the file, you need to read()
+from myFile.file and write() somewhere else.
+Note the use of 'enctype="multipart/form-data"' and 'input type="file"'
+in the HTML which the client uses to upload the file.
+If you wish to send a file to the client, you have two options:
+First, you can simply return a file-like object from your page handler.
+CherryPy will read the file and serve it as the content (HTTP body)
+of the response. However, that doesn't tell the client that
+the response is a file to be saved, rather than displayed.
+Use cherrypy.lib.cptools.serveFile for that; it takes four
+serveFile(path, contentType=None, disposition=None, name=None)
+Set "name" to the filename that you expect clients to use when they save
+your file. Note that the "name" argument is ignored if you don't also
+provide a "disposition" ("application/x-download" works in most cases).
 import os
 localDir = os.path.dirname(__file__)


-"""Tutorial: http errors """
+Tutorial: HTTP errors
+HTTPError is used to return an error response to the client.
+CherryPy has lots of options regarding how such errors are
+logged, displayed, and formatted.
 import cherrypy
-# we want to customize 403 errors
-customErrors = {
-                 'errorPage.403' : "custom_error.html"
-               }
-cherrypy.config.update({'/' : customErrors})
 class HTTPErrorDemo(object):
         return """
-            <h2><a href="toggleTracebacks">Toggle tracebacks %s</a><br/><br/></h2>
-            <a href="/doesNotExist">Click me i'm a broken link!</a>
-            <br/><br/>
-            <a href="/error?code=403">Use a custom an error page from a file.</a>
-            <br/><br/>
-            These errors are explicitly raised by the application.
-            <a href="/error?code=400">400</a>
-            <a href="/error?code=401">401</a>
-            <a href="/error?code=402">402</a>
-            <a href="/error?code=500">500</a>
-            <br/><br/>
-            <a href="/bodyArg">You can also set the response body when you raise an error</a>
+            <h2><a href="toggleTracebacks">Toggle tracebacks %s</a></h2>
+            <p><a href="/doesNotExist">Click me; I'm a broken link!</a></p>
+            <p><a href="/error?code=403">Use a custom an error page from a file.</a></p>
+            <p>These errors are explicitly raised by the application:</p>
+            <ul>
+                <li><a href="/error?code=400">400</a></li>
+                <li><a href="/error?code=401">401</a></li>
+                <li><a href="/error?code=402">402</a></li>
+                <li><a href="/error?code=500">500</a></li>
+            </ul>
+            <p><a href="/messageArg">You can also set the response body
+            when you raise an error.</a></p>
         """ % trace = True
     def toggleTracebacks(self):
         # simple function to toggle tracebacks on and off 
         tracebacks = cherrypy.config.get('server.showTracebacks')
         cherrypy.config.update({'server.showTracebacks': not tracebacks})
         # redirect back to the index
-        raise cherrypy._cperror.HTTPRedirect('/')
+        raise cherrypy.HTTPRedirect('/')
+ = True
     def error(self, code):
         # raise an error based on the get query
-        code = int(code)
         raise cherrypy.HTTPError(status = code) = True
+    def messageArg(self):
+        message = ("If you construct an HTTPError with a 'message' "
+                   "argument, it wil be placed on the error page "
+                   "(underneath the status line by default).")
+        raise cherrypy.HTTPError(500, message=message)
+ = True
-    def bodyArg(self):
-        message = """ If you construct a HTTPError wiht body argument, the body argument
-                      will overide any default or custom error page.
-                  """
-        raise cherrypy.HTTPError(403, body = message)
- = True
 cherrypy.root = HTTPErrorDemo()
+# Set a custom response for 403 errors.
+import os
+localDir = os.path.dirname(__file__)
+curpath = os.path.normpath(os.path.join(os.getcwd(), localDir))
+cherrypy.config.update({'errorPage.403' : os.path.join(curpath, "custom_error.html")})
 if __name__ == '__main__':
     # Use the configuration file tutorial.conf.
     cherrypy.config.update(file = 'tutorial.conf')