Index: cherrypy/test/test_core.py
===================================================================
--- cherrypy/test/test_core.py	(révision 1946)
+++ cherrypy/test/test_core.py	(copie de travail)
@@ -241,10 +241,8 @@
                 yield chunk
     
     
-    def page401(e):
-        cherrypy.response.status = e.status
-        cherrypy._cperror.clean_headers(e.status)
-        cherrypy.response.body = "Well, I'm very sorry but you haven't paid!"
+    def callable_error_page(status, **kwargs):
+        return "Error %s - Well, I'm very sorry but you haven't paid!" % status
     
     
     class Error(Test):
@@ -255,9 +253,13 @@
         def custom(self, err='404'):
             raise cherrypy.HTTPError(int(err), "No, <b>really</b>, not found!")
         custom._cp_config = {'error_page.404': os.path.join(localDir, "static/index.html"),
-                             'error_page.401': page401,
+                             'error_page.401': callable_error_page,
                              }
         
+        def custom_default(self):
+            return 1 + 'a' # raise an unexpected error
+        custom_default._cp_config = {'error_page.default': callable_error_page}
+        
         def noexist(self):
             raise cherrypy.HTTPError(404, "No, <b>really</b>, not found!")
         noexist._cp_config = {'error_page.404': "nonexistent.html"}
@@ -728,23 +730,28 @@
         finally:
             ignore.pop()
         
-        # Test custom error page.
+        # Test custom error page for a specific error.
         self.getPage("/error/custom")
         self.assertStatus(404)
         self.assertBody("Hello, world\r\n" + (" " * 499))
         
-        # Test custom error page.
+        # Test custom error page for a specific error.
         self.getPage("/error/custom?err=401")
         self.assertStatus(401)
-        self.assertBody("Well, I'm very sorry but you haven't paid!")
+        self.assertBody("Error 401 Unauthorized - Well, I'm very sorry but you haven't paid!")
         
+        # Test default custom error page.
+        self.getPage("/error/custom_default")
+        self.assertStatus(500)
+        self.assertBody("Error 500 Internal Server Error - Well, I'm very sorry but you haven't paid!".ljust(513))
+        
         # Test error in custom error page (ticket #305).
         # Note that the message is escaped for HTML (ticket #310).
         self.getPage("/error/noexist")
         self.assertStatus(404)
         msg = ("No, &lt;b&gt;really&lt;/b&gt;, not found!<br />"
                "In addition, the custom error page failed:\n<br />"
-               "[Errno 2] No such file or directory: 'nonexistent.html'")
+               "IOError: [Errno 2] No such file or directory: 'nonexistent.html'")
         self.assertInBody(msg)
         
         if (hasattr(self, 'harness') and
Index: cherrypy/_cprequest.py
===================================================================
--- cherrypy/_cprequest.py	(révision 1946)
+++ cherrypy/_cprequest.py	(copie de travail)
@@ -145,7 +145,9 @@
 
 def error_page_namespace(k, v):
     """Attach error pages declared in config."""
-    cherrypy.request.error_page[int(k)] = v
+    if k != 'default':
+        k = int(k)
+    cherrypy.request.error_page[k] = v
 
 
 hookpoints = ['on_start_resource', 'before_request_body',
@@ -393,15 +395,21 @@
     error_page = {}
     error_page__doc = """
     A dict of {error code: response filename or callable} pairs.
-    The named response files should be Python string-formatting templates,
-    and can expect by default to receive format values with the mapping
-    keys 'status', 'message', 'traceback', and 'version'. The set of
-    format mappings can be extended by overriding HTTPError.set_response.
     
-    If a callable is provided, it will be called with the HTTPError
-    instance as the only argument. The callable is expected to set
-    response.status, .headers and .body appropriately.
+    The error code can be the string 'default' to match every HTTP error codes
+    or an int representing a given HTTP error code.
     
+    If a filename is provided, the file should contain a Python 
+    string-formatting templates, and can expect by default to receive format 
+    values with the mapping keys 'status', 'message', 'traceback', and 
+    'version'. The set of format mappings can be extended by overriding 
+    HTTPError.set_response.
+    
+    If a callable is provided, it will be called by default with keyword 
+    arguments 'status', 'message', 'traceback', and 'version', as for a
+    string-formatting template. The callable must return a string representing
+    an HTML document.
+    
     If no entry is given for an error code, the HTTPError's set_response
     method will handle the error (by setting .status, .headers, and .body).
     """
@@ -599,11 +607,7 @@
                     self.hooks.run('before_finalize')
                     cherrypy.response.finalize()
                 except (cherrypy.HTTPRedirect, cherrypy.HTTPError), inst:
-                    ep = self.error_page.get(inst.status, '')
-                    if ep and callable(ep):
-                        ep(inst)
-                    else:
-                        inst.set_response()
+                    inst.set_response()
                     self.stage = 'before_finalize (HTTPError)'
                     self.hooks.run('before_finalize')
                     cherrypy.response.finalize()
@@ -735,11 +739,7 @@
             self.hooks.run("after_error_response")
             cherrypy.response.finalize()
         except cherrypy.HTTPRedirect, inst:
-            ep = self.error_page.get(inst.status, '')
-            if ep and callable(ep):
-                ep(inst)
-            else:
-                inst.set_response()
+            inst.set_response()
             cherrypy.response.finalize()
 
 
Index: cherrypy/_cperror.py
===================================================================
--- cherrypy/_cperror.py	(révision 1946)
+++ cherrypy/_cperror.py	(copie de travail)
@@ -2,6 +2,7 @@
 
 from cgi import escape as _escape
 from sys import exc_info as _exc_info
+from traceback import format_exception as _format_exception
 from urlparse import urljoin as _urljoin
 from cherrypy.lib import http as _http
 
@@ -287,30 +288,32 @@
         kwargs['traceback'] = ''
     if kwargs.get('version') is None:
         kwargs['version'] = cherrypy.__version__
+    
     for k, v in kwargs.iteritems():
         if v is None:
             kwargs[k] = ""
         else:
             kwargs[k] = _escape(kwargs[k])
     
-    template = _HTTPErrorTemplate
-    
-    # Replace the default template with a custom one?
-    error_page_file = cherrypy.request.error_page.get(code, '')
-    if error_page_file:
+    # Use a custom template for the error page?
+    error_page = cherrypy.request.error_page.get(code) or cherrypy.request.error_page.get('default')
+    if error_page:
         try:
-            template = file(error_page_file, 'rb').read()
+            if callable(error_page):
+                return error_page(**kwargs)
+            else:
+                return file(error_page, 'rb').read() % kwargs
         except:
+            e = _format_exception(*_exc_info())[-1]
             m = kwargs['message']
             if m:
                 m += "<br />"
-            m += ("In addition, the custom error page "
-                  "failed:\n<br />%s" % (_exc_info()[1]))
+            m += "In addition, the custom error page failed:\n<br />%s" % e
             kwargs['message'] = m
-    
-    return template % kwargs
 
+    return _HTTPErrorTemplate % kwargs
 
+
 _ie_friendly_error_sizes = {
     400: 512, 403: 256, 404: 512, 405: 256,
     406: 512, 408: 512, 409: 512, 410: 256,
