Anonymous avatar Anonymous committed 7398262

implimented changes needed to re-close ticket:288

Comments (0)

Files changed (9)

cherrypy/_cperror.py

 """
 
 import urllib
+import os
 
 
 class Error(Exception):
         self.status = status = int(status)
         if status < 400 or status > 599:
             raise ValueError("status must be between 400 and 599.")
+         
+        self.body = body
+    
+    def set_response(self):
+        import cherrypy
         
-        # these 4 lines might dissapear
-        import cherrypy
-        self.statusString = cherrypy._cputil.getErrorStatusAndPage(status)[0]
-        cherrypy.response.status = self.statusString
+        # we now now have access to the traceback 
+        statusString, defaultBody = cherrypy._cputil.getErrorStatusAndPage(self.status)
+        
+        if self.body is _missing:
+            self.body = defaultBody
+            # try to look up a custom error page in the config map
+            # if there is no error page then use the pageGenerator
+            
+            # The page generator is used because the init method is called 
+            # before the exception is raised.  It is impossible to embed the
+            # traceback in the error page at this piont so we use the generator
+            # to render the error page at a later point
+            
+            import cherrypy
+            # try and read the page from a file
+            # we use the default if the page can't be read
+            try:
+                errorPageFile = cherrypy.config.get('errorPage.%s' % status, '')
+                self.body = file(errorPageFile, 'r')
+            except:
+                # we have alread set the body
+                pass
 
-        if body is _missing:
-            # because the init method is called before the exception
-            # is raised it is impossible to embed the traceback in the
-            # error page at this point. We use a generator so that the
-            # error page is generated at a later point (after the
-            # exception is raised).
-            body = self.pageGenerator()
-        
-        cherrypy.response.body = body
+        cherrypy.response.status = statusString
+        cherrypy.response.body   = self.body
     
     def __str__(self):
-        return self.statusString
-
-    def pageGenerator(self):
         import cherrypy
-        yield cherrypy._cputil.getErrorStatusAndPage(self.status)[1]
+        return cherrypy._cputil.getErrorStatusAndPage(self.status)[0]
 
 class NotFound(HTTPError):
     """ Happens when a URL couldn't be mapped to any class.method """
     def __init__(self, path):
         self.args = (path,)
         HTTPError.__init__(self, 404)
+
+    def __str__(self):
+        return self.args[0]

cherrypy/_cphttptools.py

                     inst.set_response()
                     applyFilters('beforeFinalize')
                     finalize()
+                except cherrypy.HTTPError, inst:
+                    # This includes NotFound
+                    inst.set_response()
+                    applyFilters('beforeFinalize')
+                    finalize()
+
             finally:
                 applyFilters('onEndResource')
         except:
     try:
         applyFilters('beforeErrorResponse')
        
-        # status, body may already be set by HTTPError constructor
-        if not isinstance(exc, cherrypy.HTTPError):
-            # _cpOnError will probably change cherrypy.response.body.
-            # It may also change the headerMap, etc.
-            _cputil.getSpecialAttribute('_cpOnError')()
+        # _cpOnError will probably change cherrypy.response.body.
+        # It may also change the headerMap, etc.
+        _cputil.getSpecialAttribute('_cpOnError')()
         
         finalize()
         

cherrypy/tutorial/custom_error.html

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+    <title>403 Unauthorized</title>
+</head>
+    <body>
+
+        <h2>You can't do that!</h2>
+        <p>This is a custom error page that is read from a file.<p>
+    </body>
+
+</html>
+
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+    <title>403 Unauthorized</title>
+</head>
+    <body>
+
+        <h2>You can't do that!</h2>
+        <p>This is a custom error page that is read from a file.<p>
+    </body>
+
+</html>
+
+

cherrypy/tutorial/tut10_http_errors.py

 
 import cherrypy
 
+# we want to customize 403 errors
+customErrors = {
+                 'errorPage.403' : "custom_error.html"
+               }
+
+cherrypy.config.update({'/' : customErrors})
 
 class HTTPErrorDemo(object):
     
             
         return """
         <html><body>
-            <a href="toggleTracebacks">Toggle tracebacks %s</a><br/><br/>
+            <h2><a href="toggleTracebacks">Toggle tracebacks %s</a><br/><br/></h2>
             <a href="/doesNotExist">Click me i'm a broken link!</a>
-            <br/>
-            <a href="/customMessage">Use a custom error message</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>
         </body></html>
         """ % trace
     index.exposed = True
         raise cherrypy.HTTPError(status = code)
     error.exposed = True
 
-    def customMessage(self):
-        raise cherrypy.HTTPError(500, "Plain text message")
-    customMessage.exposed = 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)
+    bodyArg.exposed = True
 
 cherrypy.root = HTTPErrorDemo()
 

cherrypy/tutorial/tutorial.conf

-[global]
-server.socketPort = 8080
-server.threadPool = 10
-server.environment = "production"
-server.showTracebacks = True
-# server.logToScreen = False
+[global]
+server.socketPort = 8080
+server.threadPool = 10
+server.environment = "production"
+# server.showTracebacks = True
+# server.logToScreen = False

docs/book/xml/apireference.xml

         
       <listitem>
         <section>
-            <title>cherrypy.HTTPStatusError</title>
+            <title>cherrypy.HTTPError</title>
             <para>
                 This exception can be used to automatically send a response 
                 using a http status code, with an appropriate error page.

docs/book/xml/appdeveloperreference.xml

     <xi:include href="templateindependant.xml" />
     <xi:include href="staticcontenthandling.xml" />
     <xi:include href="fileuploadbehavior.xml" />
-</section>
+    <xi:include href="errorhandling.xml" />
+</section>

docs/book/xml/errorhandling.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<section xmlns:db="http://docbook.org/docbook-ng"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+         xml:id="globaloverviewcherrypy">
+    <title>Exceptions and Error Handling</title>
+    <para>
+        Exceptions raised inside CherryPy applications result in a call
+        to the _cpOnError function.  HTTPError and HTTPRedirect exceptions do not result in calls to
+        _cpOnError.  HTTPError exceptions force the server to set 
+        response status and return an error page.
+    </para>
+</section>
         [
             'cherrypy/tutorial/tutorial.conf',
             'cherrypy/tutorial/README.txt',
+            'cherrypy/tutorial/ReturnVsYield.txt',
+            'cherrypy/tutorial/custom_error.html',
         ]
     ),
     ('cherrypy', ['cherrypy/favicon.ico',]),
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.