[Checkins] SVN: z3c.rest/trunk/ Implemented simple error view support. By default errors are in XML

Stephan Richter srichter at cosmos.phy.tufts.edu
Mon Dec 10 19:07:36 EST 2007


Log message for revision 82249:
  Implemented simple error view support. By default errors are in XML 
  format.
  

Changed:
  U   z3c.rest/trunk/CHANGES.txt
  A   z3c.rest/trunk/src/z3c/rest/baseerror.pt
  U   z3c.rest/trunk/src/z3c/rest/configure.zcml
  A   z3c.rest/trunk/src/z3c/rest/error.pt
  A   z3c.rest/trunk/src/z3c/rest/error.py
  A   z3c.rest/trunk/src/z3c/rest/error.txt
  U   z3c.rest/trunk/src/z3c/rest/interfaces.py
  U   z3c.rest/trunk/src/z3c/rest/rest.py
  U   z3c.rest/trunk/src/z3c/rest/rest.txt
  U   z3c.rest/trunk/src/z3c/rest/tests/test.py

-=-
Modified: z3c.rest/trunk/CHANGES.txt
===================================================================
--- z3c.rest/trunk/CHANGES.txt	2007-12-10 22:25:31 UTC (rev 82248)
+++ z3c.rest/trunk/CHANGES.txt	2007-12-11 00:07:36 UTC (rev 82249)
@@ -9,8 +9,10 @@
 
   * Publisher hooks to build dedicated REST servers
 
-  * Simple REST traverser
+  * Error view support
 
+  * Pluggable REST traverser based on `z3c.traverser`
+
   * REST client
 
   * Minimal sample application

Added: z3c.rest/trunk/src/z3c/rest/baseerror.pt
===================================================================
--- z3c.rest/trunk/src/z3c/rest/baseerror.pt	                        (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/baseerror.pt	2007-12-11 00:07:36 UTC (rev 82249)
@@ -0,0 +1,10 @@
+<?xml version="1.0" ?>
+<error
+    xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <name tal:content="options/name">
+    NameError
+  </name>
+  <explanation tal:content="options/explanation">
+    'Foo' object has no attribute 'bar'
+  </explanation>
+</error>


Property changes on: z3c.rest/trunk/src/z3c/rest/baseerror.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: z3c.rest/trunk/src/z3c/rest/configure.zcml
===================================================================
--- z3c.rest/trunk/src/z3c/rest/configure.zcml	2007-12-10 22:25:31 UTC (rev 82248)
+++ z3c.rest/trunk/src/z3c/rest/configure.zcml	2007-12-11 00:07:36 UTC (rev 82249)
@@ -1,5 +1,6 @@
 <configure
     xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
     xmlns:zcml="http://namespaces.zope.org/zcml">
 
   <!-- NULL Resource support -->
@@ -62,4 +63,23 @@
 
   </configure>
 
+  <!-- Register error views -->
+
+  <!-- Setup the default view name for rest request exception views.
+       Have to use big hack to get this installed. -->
+
+  <browser:defaultView
+      for="zope.interface.common.interfaces.IException"
+      name=""
+      layer="z3c.rest.interfaces.IRESTRequest"
+      />
+
+  <view
+      for="zope.interface.common.interfaces.IException"
+      type=".interfaces.IRESTRequest"
+      factory=".error.XMLErrorView"
+      allowed_attributes="__call__"
+      permission="zope.Public"
+      />
+
 </configure>

Added: z3c.rest/trunk/src/z3c/rest/error.pt
===================================================================
--- z3c.rest/trunk/src/z3c/rest/error.pt	                        (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/error.pt	2007-12-11 00:07:36 UTC (rev 82249)
@@ -0,0 +1,10 @@
+<?xml version="1.0" ?>
+<error
+    xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <name tal:content="view/name">
+    NameError
+  </name>
+  <explanation tal:content="view/explanation">
+    'Foo' object has no attribute 'bar'
+  </explanation>
+</error>


Property changes on: z3c.rest/trunk/src/z3c/rest/error.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.rest/trunk/src/z3c/rest/error.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/error.py	                        (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/error.py	2007-12-11 00:07:36 UTC (rev 82249)
@@ -0,0 +1,33 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""REST publication and publisher factories
+
+$Id$
+"""
+import zope.component
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+from z3c.rest import rest
+
+
+class XMLErrorView(rest.RESTView):
+
+    template = ViewPageTemplateFile('error.pt')
+
+    def update(self):
+        self.name = self.context.__class__.__name__
+        self.explanation = unicode(str(self.context))
+
+    def __call__(self):
+        self.update()
+        return self.template()


Property changes on: z3c.rest/trunk/src/z3c/rest/error.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.rest/trunk/src/z3c/rest/error.txt
===================================================================
--- z3c.rest/trunk/src/z3c/rest/error.txt	                        (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/error.txt	2007-12-11 00:07:36 UTC (rev 82249)
@@ -0,0 +1,28 @@
+===========
+Error Views
+===========
+
+Error formatting is supported by looking up a view for the error.
+
+  >>> from z3c.rest import error
+
+
+Generic XML Error View
+----------------------
+
+There exists a generic error view class that formats the error into a simple
+XML document.
+
+  >>> class MyException(Exception):
+  ...     pass
+  >>> exc = MyException('some info')
+
+  >>> from z3c.rest import rest
+  >>> request = rest.RESTRequest(None, {})
+
+  >>> print error.XMLErrorView(exc, request)()
+  <?xml version="1.0" ?>
+  <error>
+    <name>MyException</name>
+    <explanation>some info</explanation>
+  </error>


Property changes on: z3c.rest/trunk/src/z3c/rest/error.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: z3c.rest/trunk/src/z3c/rest/interfaces.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/interfaces.py	2007-12-10 22:25:31 UTC (rev 82248)
+++ z3c.rest/trunk/src/z3c/rest/interfaces.py	2007-12-11 00:07:36 UTC (rev 82249)
@@ -23,7 +23,10 @@
 class IRESTRequest(http.IHTTPRequest):
     """A special type of request for handling REST-based requests."""
 
+class IRESTResponse(http.IHTTPResponse):
+    """A special type of response for handling REST-based requests."""
 
+
 class IRESTView(ILocation):
     """A REST view"""
 

Modified: z3c.rest/trunk/src/z3c/rest/rest.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/rest.py	2007-12-10 22:25:31 UTC (rev 82248)
+++ z3c.rest/trunk/src/z3c/rest/rest.py	2007-12-11 00:07:36 UTC (rev 82249)
@@ -16,12 +16,15 @@
 $Id$
 """
 import cgi
+import types
 import zope.interface
+from z3c.rest import interfaces
+from zope.pagetemplate.pagetemplatefile import PageTemplateFile
 from zope.app.publication.interfaces import IPublicationRequestFactory
 from zope.app.publication.http import HTTPPublication
-from zope.publisher.http import HTTPRequest
+from zope.publisher.http import HTTPRequest, HTTPResponse
+from zope.publisher.interfaces import Redirect
 
-from z3c.rest import interfaces
 
 class RESTRequest(HTTPRequest):
     zope.interface.implements(interfaces.IRESTRequest)
@@ -63,7 +66,34 @@
 
         return super(RESTRequest, self).get(key, default)
 
+    def _createResponse(self):
+        return RESTResponse()
 
+
+class RESTResponse(HTTPResponse):
+    zope.interface.implements(interfaces.IRESTResponse)
+
+    errorTemplate = PageTemplateFile("baseerror.pt")
+
+    def handleException(self, exc_info):
+        errorClass, error = exc_info[:2]
+        if isinstance(errorClass, (types.ClassType, type)):
+            if issubclass(errorClass, Redirect):
+                self.redirect(error.getLocation())
+                return
+            title = tname = errorClass.__name__
+        else:
+            title = tname = unicode(errorClass)
+
+        # Throwing non-protocol-specific exceptions is a good way
+        # for apps to control the status code.
+        self.setStatus(tname)
+
+        self.setHeader("Content-Type", "text/xml")
+        self.setResult(
+            self.errorTemplate(name=title, explanation=str(error)))
+
+
 class RESTView(object):
     zope.interface.implements(interfaces.IRESTView)
 

Modified: z3c.rest/trunk/src/z3c/rest/rest.txt
===================================================================
--- z3c.rest/trunk/src/z3c/rest/rest.txt	2007-12-10 22:25:31 UTC (rev 82248)
+++ z3c.rest/trunk/src/z3c/rest/rest.txt	2007-12-11 00:07:36 UTC (rev 82249)
@@ -69,10 +69,11 @@
   >>> publish.publish(request)
   <z3c.rest.rest.RESTRequest instance URL=http://localhost:8080>
   >>> print request.response.consumeBody()
-  <html><head><title>ComponentLookupError</title></head>
-  <body><h2>ComponentLookupError</h2>
-  A server error occurred.
-  </body></html>
+  <?xml version="1.0" ?>
+  <error>
+    <name>ComponentLookupError</name>
+    <explanation>(&lt;InterfaceClass ...IAuthentication&gt;, u'')</explanation>
+  </error>
 
 Let' unwind a bit. Originally, we started with the desire to create a
 Publisher WSGI Application instance that internally uses a REST request. All
@@ -98,10 +99,11 @@
   >>> wsgiEnv.update(env)
 
   >>> print '\n'.join(app(wsgiEnv, start_response))
-  <html><head><title>ComponentLookupError</title></head>
-  <body><h2>ComponentLookupError</h2>
-  A server error occurred.
-  </body></html>
+  <?xml version="1.0" ?>
+  <error>
+    <name>ComponentLookupError</name>
+    <explanation>(&lt;InterfaceClass ...IAuthentication&gt;, u'')</explanation>
+  </error>
 
 
 .. [1]: ``zope.app.wsgi.WSGIPublisherApplication.__call__``
@@ -150,6 +152,63 @@
   'default'
 
 
+The REST Response
+-----------------
+
+The REST Response is pretty much identical to the HTTP Response, except that
+its exception handler returns XML instead of HTML. This method, however, is
+only used for classic and string exceptions.
+
+Starting with a response, ...
+
+  >>> response = rest.RESTResponse()
+
+... we can now call the handler:
+
+  >>> class MyException(Exception):
+  ...     pass
+
+  >>> response.handleException((MyException, MyException(), None))
+  >>> response._status
+  500
+  >>> response._reason
+  'Internal Server Error'
+  >>> print '\n'.join(response._result)
+  <?xml version="1.0" ?>
+  <error>
+    <name>MyException</name>
+    <explanation></explanation>
+  </error>
+
+Let's try a string exception too:
+
+  >>> response.handleException(('StringException', 'Some details', None))
+  >>> response._status
+  500
+  >>> response._reason
+  'Internal Server Error'
+  >>> print '\n'.join(response._result)
+  <?xml version="1.0" ?>
+  <error>
+    <name>StringException</name>
+    <explanation>Some details</explanation>
+  </error>
+
+The `Redirect` exception is special. It actuaually causes the request to be
+redirected.
+
+  >>> response._request = rest.RESTRequest(None, {})
+
+  >>> from zope.publisher.interfaces import Redirect
+  >>> response.handleException((Redirect, Redirect('http://localhost'), None))
+  >>> response._status
+  302
+  >>> response._reason
+  'Moved Temporarily'
+  >>> response._headers['location']
+  ['http://localhost']
+
+
 REST Views
 ----------
 

Modified: z3c.rest/trunk/src/z3c/rest/tests/test.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/tests/test.py	2007-12-10 22:25:31 UTC (rev 82248)
+++ z3c.rest/trunk/src/z3c/rest/tests/test.py	2007-12-11 00:07:36 UTC (rev 82249)
@@ -19,6 +19,7 @@
 import os
 import unittest
 import zope.component
+import zope.traversing.testing
 from zope.testing import doctest, doctestunit
 from zope.traversing.browser import absoluteurl
 from zope.traversing.interfaces import IContainmentRoot
@@ -31,7 +32,8 @@
 
 def setUp(test):
     placelesssetup.setUp(test)
-
+    zope.traversing.testing.setUp()
+    
     # XXX: This really needs a REST equivalent w/o breadcrumbs.
 
     zope.component.provideAdapter(
@@ -68,6 +70,11 @@
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
             ),
         doctest.DocFileSuite(
+            '../error.txt',
+            setUp=setUp, tearDown=placelesssetup.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
             '../null.txt',
             setUp=setUp, tearDown=placelesssetup.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,



More information about the Checkins mailing list