[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>(<InterfaceClass ...IAuthentication>, 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>(<InterfaceClass ...IAuthentication>, 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