[Checkins] SVN: z3c.rest/trunk/ Okay, I am back working on REST
again. ; -) This time I am less
Stephan Richter
srichter at cosmos.phy.tufts.edu
Fri Nov 30 06:40:13 EST 2007
Log message for revision 82045:
Okay, I am back working on REST again. ;-) This time I am less
ambitious.
I have a special publisher for REST and a special request. The code is
still experimental, but I discovered already several flaws during
testing, so it is getting better. I also have a little demo for
folders that provides a very simple RESTive API.
I also developed a REST client that is somewhat similar to test
browser. It works very well and can be used in real applications as
well.
Everything is 100% tested!
Thanks to Bayview Financial for allowing me to develop this code under
the ZPL and publish it to svn.zope.org.
Changed:
A z3c.rest/trunk/
A z3c.rest/trunk/.installed.cfg
A z3c.rest/trunk/CHANGES.txt
A z3c.rest/trunk/README.txt
A z3c.rest/trunk/bin/
A z3c.rest/trunk/bin/buildout
A z3c.rest/trunk/bin/coverage
A z3c.rest/trunk/bin/coveragediff
A z3c.rest/trunk/bin/demo
A z3c.rest/trunk/bin/test
A z3c.rest/trunk/bootstrap.py
A z3c.rest/trunk/buildout.cfg
A z3c.rest/trunk/coverage/
A z3c.rest/trunk/coverage/report/
A z3c.rest/trunk/coverage/report/all.html
A z3c.rest/trunk/coverage/report/z3c.html
A z3c.rest/trunk/coverage/report/z3c.rest.__init__.html
A z3c.rest/trunk/coverage/report/z3c.rest.client.html
A z3c.rest/trunk/coverage/report/z3c.rest.html
A z3c.rest/trunk/coverage/report/z3c.rest.interfaces.html
A z3c.rest/trunk/coverage/report/z3c.rest.null.html
A z3c.rest/trunk/coverage/report/z3c.rest.rest.html
A z3c.rest/trunk/coverage/report/z3c.rest.traverser.html
A z3c.rest/trunk/coverage/z3c.rest.__init__.cover
A z3c.rest/trunk/coverage/z3c.rest.client.cover
A z3c.rest/trunk/coverage/z3c.rest.interfaces.cover
A z3c.rest/trunk/coverage/z3c.rest.null.cover
A z3c.rest/trunk/coverage/z3c.rest.rest.cover
A z3c.rest/trunk/coverage/z3c.rest.testing.cover
A z3c.rest/trunk/coverage/z3c.rest.tests.__init__.cover
A z3c.rest/trunk/coverage/z3c.rest.tests.folder.cover
A z3c.rest/trunk/coverage/z3c.rest.tests.test.cover
A z3c.rest/trunk/coverage/z3c.rest.traverser.cover
A z3c.rest/trunk/develop-eggs/
A z3c.rest/trunk/develop-eggs/z3c.rest.egg-link
A z3c.rest/trunk/parts/
A z3c.rest/trunk/parts/database/
A z3c.rest/trunk/parts/database/Data.fs
A z3c.rest/trunk/parts/database/Data.fs.index
A z3c.rest/trunk/parts/database/Data.fs.lock
A z3c.rest/trunk/parts/demo/
A z3c.rest/trunk/parts/demo/zdaemon.conf
A z3c.rest/trunk/parts/demo/zope.conf
A z3c.rest/trunk/parts/form-demo-app/
A z3c.rest/trunk/parts/form-demo-app/debugzope
A z3c.rest/trunk/parts/form-demo-app/runzope
A z3c.rest/trunk/parts/form-demo-app/site.zcml
A z3c.rest/trunk/parts/test/
A z3c.rest/trunk/rest.prof
A z3c.rest/trunk/setup.py
A z3c.rest/trunk/src/
A z3c.rest/trunk/src/z3c/
A z3c.rest/trunk/src/z3c/__init__.py
A z3c.rest/trunk/src/z3c/rest/
A z3c.rest/trunk/src/z3c/rest/README.txt
A z3c.rest/trunk/src/z3c/rest/__init__.py
A z3c.rest/trunk/src/z3c/rest/application.zcml
A z3c.rest/trunk/src/z3c/rest/client.py
A z3c.rest/trunk/src/z3c/rest/client.txt
A z3c.rest/trunk/src/z3c/rest/configure.zcml
A z3c.rest/trunk/src/z3c/rest/interfaces.py
A z3c.rest/trunk/src/z3c/rest/null.py
A z3c.rest/trunk/src/z3c/rest/null.txt
A z3c.rest/trunk/src/z3c/rest/rest.py
A z3c.rest/trunk/src/z3c/rest/rest.txt
A z3c.rest/trunk/src/z3c/rest/testing.py
A z3c.rest/trunk/src/z3c/rest/tests/
A z3c.rest/trunk/src/z3c/rest/tests/__init__.py
A z3c.rest/trunk/src/z3c/rest/tests/folder.pt
A z3c.rest/trunk/src/z3c/rest/tests/folder.py
A z3c.rest/trunk/src/z3c/rest/tests/folder.zcml
A z3c.rest/trunk/src/z3c/rest/tests/ftesting.zcml
A z3c.rest/trunk/src/z3c/rest/tests/test.py
A z3c.rest/trunk/src/z3c/rest/traverser.py
A z3c.rest/trunk/src/z3c/rest/traverser.txt
A z3c.rest/trunk/src/z3c/rest/twist.py
A z3c.rest/trunk/src/z3c/rest/zserver.py
A z3c.rest/trunk/src/z3c.rest.egg-info/
A z3c.rest/trunk/src/z3c.rest.egg-info/PKG-INFO
A z3c.rest/trunk/src/z3c.rest.egg-info/SOURCES.txt
A z3c.rest/trunk/src/z3c.rest.egg-info/dependency_links.txt
A z3c.rest/trunk/src/z3c.rest.egg-info/namespace_packages.txt
A z3c.rest/trunk/src/z3c.rest.egg-info/not-zip-safe
A z3c.rest/trunk/src/z3c.rest.egg-info/requires.txt
A z3c.rest/trunk/src/z3c.rest.egg-info/top_level.txt
-=-
Added: z3c.rest/trunk/.installed.cfg
===================================================================
--- z3c.rest/trunk/.installed.cfg (rev 0)
+++ z3c.rest/trunk/.installed.cfg 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,178 @@
+[buildout]
+installed_develop_eggs = /opt/zope/packages/z3c.rest/develop-eggs/z3c.rest.egg-link
+parts = test coverage database form-demo-app demo
+
+[test]
+__buildout_installed__ = /opt/zope/packages/z3c.rest/parts/test
+ /opt/zope/packages/z3c.rest/bin/test
+__buildout_signature__ = zc.recipe.testrunner-1.0.0-py2.4.egg zc.recipe.egg-1.0.0-py2.4.egg setuptools-0.6c7-py2.4.egg zope.testing-3.5.1-py2.4.egg zc.buildout-1.0.0b31-py2.4.egg zc.buildout-1.0.0b31-py2.4.egg
+_b = /opt/zope/packages/z3c.rest/bin
+_d = /opt/zope/packages/z3c.rest/develop-eggs
+_e = /opt/zope/packages/eggs
+bin-directory = /opt/zope/packages/z3c.rest/bin
+develop-eggs-directory = /opt/zope/packages/z3c.rest/develop-eggs
+eggs = z3c.rest [app, test]
+eggs-directory = /opt/zope/packages/eggs
+executable = /usr/bin/py24
+index = http://download.zope.org/zope3.4
+location = /opt/zope/packages/z3c.rest/parts/test
+recipe = zc.recipe.testrunner
+script = /opt/zope/packages/z3c.rest/bin/test
+
+[coverage]
+__buildout_installed__ = /opt/zope/packages/z3c.rest/bin/coverage
+ /opt/zope/packages/z3c.rest/bin/coveragediff
+__buildout_signature__ = zc.recipe.egg-1.0.0-py2.4.egg setuptools-0.6c7-py2.4.egg zc.buildout-1.0.0b31-py2.4.egg
+_b = /opt/zope/packages/z3c.rest/bin
+_d = /opt/zope/packages/z3c.rest/develop-eggs
+_e = /opt/zope/packages/eggs
+bin-directory = /opt/zope/packages/z3c.rest/bin
+develop-eggs-directory = /opt/zope/packages/z3c.rest/develop-eggs
+eggs = z3c.coverage
+eggs-directory = /opt/zope/packages/eggs
+executable = /usr/bin/py24
+index = http://download.zope.org/zope3.4
+recipe = zc.recipe.egg
+
+[database]
+__buildout_installed__ =
+__buildout_signature__ = zc.recipe.filestorage-1.0.0-py2.4.egg setuptools-0.6c7-py2.4.egg zope.testing-3.5.1-py2.4.egg zc.buildout-1.0.0b31-py2.4.egg
+blob-dir =
+path = /opt/zope/packages/z3c.rest/parts/database/Data.fs
+recipe = zc.recipe.filestorage
+zconfig = <zodb>
+ %(__buildout_space__)s%(__buildout_space__)s<filestorage>
+ %(__buildout_space__)s%(__buildout_space__)s%(__buildout_space__)s%(__buildout_space__)spath /opt/zope/packages/z3c.rest/parts/database/Data.fs
+ %(__buildout_space__)s%(__buildout_space__)s</filestorage>
+ </zodb>%(__buildout_space_n__)s
+
+[form-demo-app]
+__buildout_installed__ = /opt/zope/packages/z3c.rest/parts/form-demo-app
+__buildout_signature__ = zc.zope3recipes-0.6.0-py2.4.egg ZConfig-2.5-py2.4.egg zc.recipe.egg-1.0.0-py2.4.egg setuptools-0.6c7-py2.4.egg zope.testing-3.5.1-py2.4.egg zc.buildout-1.0.0b31-py2.4.egg zc.buildout-1.0.0b31-py2.4.egg
+_b = /opt/zope/packages/z3c.rest/bin
+_d = /opt/zope/packages/z3c.rest/develop-eggs
+_e = /opt/zope/packages/eggs
+bin-directory = /opt/zope/packages/z3c.rest/bin
+develop-eggs-directory = /opt/zope/packages/z3c.rest/develop-eggs
+eggs = z3c.rest [app]
+eggs-directory = /opt/zope/packages/eggs
+executable = /usr/bin/py24
+index = http://download.zope.org/zope3.4
+location = /opt/zope/packages/z3c.rest/parts/form-demo-app
+recipe = zc.zope3recipes:app
+scripts =
+servers = twisted
+site.zcml = <include package="z3c.rest" file="application.zcml" />
+zope3-location = /opt/zope/packages/z3c.rest/.
+
+[demo]
+__buildout_installed__ = /opt/zope/packages/z3c.rest/parts/demo
+ /opt/zope/packages/z3c.rest/bin/demo
+__buildout_signature__ = zc.zope3recipes-0.6.0-py2.4.egg ZConfig-2.5-py2.4.egg zc.recipe.egg-1.0.0-py2.4.egg setuptools-0.6c7-py2.4.egg zope.testing-3.5.1-py2.4.egg zc.buildout-1.0.0b31-py2.4.egg zc.buildout-1.0.0b31-py2.4.egg
+_b = /opt/zope/packages/z3c.rest/bin
+_d = /opt/zope/packages/z3c.rest/develop-eggs
+_e = /opt/zope/packages/eggs
+application = form-demo-app
+application-location = /opt/zope/packages/z3c.rest/parts/form-demo-app
+bin-directory = /opt/zope/packages/z3c.rest/bin
+develop-eggs-directory = /opt/zope/packages/z3c.rest/develop-eggs
+eggs = zdaemon
+ setuptools
+eggs-directory = /opt/zope/packages/eggs
+executable = /usr/bin/py24
+index = http://download.zope.org/zope3.4
+recipe = zc.zope3recipes:instance
+run-directory = /opt/zope/packages/z3c.rest/parts/demo
+scripts =
+servers = twisted
+zope.conf = <zodb>
+ %(__buildout_space__)s%(__buildout_space__)s<filestorage>
+ %(__buildout_space__)s%(__buildout_space__)s%(__buildout_space__)s%(__buildout_space__)spath /opt/zope/packages/z3c.rest/parts/database/Data.fs
+ %(__buildout_space__)s%(__buildout_space__)s</filestorage>
+ </zodb>
+ %(__buildout_space_n__)s<server>
+ type HTTP
+ address 8080
+ </server>
+ <server>
+ type REST-HTTP
+ address 8081
+ </server>
+
+[buildout]
+parts = coverage database form-demo-app demo test
+
+[buildout]
+parts = database form-demo-app demo test coverage
+
+[buildout]
+installed_develop_eggs = /opt/zope/packages/z3c.rest/develop-eggs/z3c.rest.egg-link
+
+[buildout]
+parts = form-demo-app demo test coverage database
+
+[buildout]
+parts = demo test coverage database form-demo-app
+
+[buildout]
+parts = test coverage database form-demo-app demo
+
+[buildout]
+parts = coverage database form-demo-app demo test
+
+[buildout]
+parts = database form-demo-app demo test coverage
+
+[buildout]
+installed_develop_eggs = /opt/zope/packages/z3c.rest/develop-eggs/z3c.rest.egg-link
+
+[buildout]
+parts = form-demo-app demo test coverage database
+
+[buildout]
+parts = demo test coverage database form-demo-app
+
+[buildout]
+parts = test coverage database form-demo-app demo
+
+[buildout]
+parts = coverage database form-demo-app demo test
+
+[buildout]
+parts = database form-demo-app demo test coverage
+
+[buildout]
+installed_develop_eggs = /opt/zope/packages/z3c.rest/develop-eggs/z3c.rest.egg-link
+
+[buildout]
+parts = form-demo-app demo test coverage database
+
+[buildout]
+parts = demo test coverage database form-demo-app
+
+[buildout]
+parts = test coverage database form-demo-app demo
+
+[buildout]
+parts = coverage database form-demo-app demo test
+
+[buildout]
+parts = database form-demo-app demo test coverage
+
+[buildout]
+installed_develop_eggs = /opt/zope/packages/z3c.rest/develop-eggs/z3c.rest.egg-link
+
+[buildout]
+parts = form-demo-app demo test coverage database
+
+[buildout]
+parts = demo test coverage database form-demo-app
+
+[buildout]
+parts = test coverage database form-demo-app demo
+
+[buildout]
+parts = coverage database form-demo-app demo test
+
+[buildout]
+parts = database form-demo-app demo test coverage
Added: z3c.rest/trunk/CHANGES.txt
===================================================================
--- z3c.rest/trunk/CHANGES.txt (rev 0)
+++ z3c.rest/trunk/CHANGES.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,16 @@
+=======
+CHANGES
+=======
+
+Version 0.1.0 (2007-12-??)
+--------------------------
+
+- Initial Release
+
+ * Publisher hooks to build dedicated REST servers
+
+ * Simple REST traverser
+
+ * REST client
+
+ * Minimal sample application
Property changes on: z3c.rest/trunk/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/README.txt
===================================================================
--- z3c.rest/trunk/README.txt (rev 0)
+++ z3c.rest/trunk/README.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1 @@
+This package provides a framework to build REST APIs on top of Zope 3.
Property changes on: z3c.rest/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/bin/buildout
===================================================================
--- z3c.rest/trunk/bin/buildout (rev 0)
+++ z3c.rest/trunk/bin/buildout 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,12 @@
+#!/usr/bin/py24
+
+import sys
+sys.path[0:0] = [
+ '/opt/zope/packages/eggs/setuptools-0.6c7-py2.4.egg',
+ '/opt/zope/packages/eggs/zc.buildout-1.0.0b31-py2.4.egg',
+ ]
+
+import zc.buildout.buildout
+
+if __name__ == '__main__':
+ zc.buildout.buildout.main()
Property changes on: z3c.rest/trunk/bin/buildout
___________________________________________________________________
Name: svn:executable
+
Added: z3c.rest/trunk/bin/coverage
===================================================================
--- z3c.rest/trunk/bin/coverage (rev 0)
+++ z3c.rest/trunk/bin/coverage 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,12 @@
+#!/usr/bin/py24
+
+import sys
+sys.path[0:0] = [
+ '/opt/zope/packages/eggs/z3c.coverage-1.0.1-py2.4.egg',
+ '/opt/zope/packages/eggs/setuptools-0.6c7-py2.4.egg',
+ ]
+
+import z3c.coverage.coveragereport
+
+if __name__ == '__main__':
+ z3c.coverage.coveragereport.main()
Property changes on: z3c.rest/trunk/bin/coverage
___________________________________________________________________
Name: svn:executable
+
Added: z3c.rest/trunk/bin/coveragediff
===================================================================
--- z3c.rest/trunk/bin/coveragediff (rev 0)
+++ z3c.rest/trunk/bin/coveragediff 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,12 @@
+#!/usr/bin/py24
+
+import sys
+sys.path[0:0] = [
+ '/opt/zope/packages/eggs/z3c.coverage-1.0.1-py2.4.egg',
+ '/opt/zope/packages/eggs/setuptools-0.6c7-py2.4.egg',
+ ]
+
+import z3c.coverage.coveragediff
+
+if __name__ == '__main__':
+ z3c.coverage.coveragediff.main()
Property changes on: z3c.rest/trunk/bin/coveragediff
___________________________________________________________________
Name: svn:executable
+
Added: z3c.rest/trunk/bin/demo
===================================================================
--- z3c.rest/trunk/bin/demo (rev 0)
+++ z3c.rest/trunk/bin/demo 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,19 @@
+#!/usr/bin/py24
+
+import sys
+sys.path[0:0] = [
+ '/opt/zope/packages/eggs/zdaemon-2.0.1-py2.4.egg',
+ '/opt/zope/packages/eggs/setuptools-0.6c7-py2.4.egg',
+ '/opt/zope/packages/eggs/ZConfig-2.5-py2.4.egg',
+ '/opt/zope/packages/eggs/zc.zope3recipes-0.6.0-py2.4.egg',
+ ]
+
+import zc.zope3recipes.ctl
+
+if __name__ == '__main__':
+ zc.zope3recipes.ctl.main([
+ '/opt/zope/packages/z3c.rest/parts/form-demo-app/debugzope',
+ '/opt/zope/packages/z3c.rest/parts/demo/zope.conf',
+ '-C', '/opt/zope/packages/z3c.rest/parts/demo/zdaemon.conf',
+ ]+sys.argv[1:]
+ )
Property changes on: z3c.rest/trunk/bin/demo
___________________________________________________________________
Name: svn:executable
+
Added: z3c.rest/trunk/bin/test
===================================================================
--- z3c.rest/trunk/bin/test (rev 0)
+++ z3c.rest/trunk/bin/test 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,108 @@
+#!/usr/bin/py24
+
+import sys
+sys.path[0:0] = [
+ '/opt/zope/packages/z3c.rest/src',
+ '/opt/zope/packages/eggs/zope.testing-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/setuptools-0.6c7-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.testing-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/z3c.etestbrowser-1.0.4-py2.4.egg',
+ '/opt/zope/packages/eggs/z3c.coverage-1.0.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.contentprovider-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.zcmlfiles-3.4.3-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.wsgi-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.twisted-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.securitypolicy-3.4.6-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.security-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.publication-3.4.2-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.publisher-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.form-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.error-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.container-3.5.3-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.app.component-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.authentication-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.appsetup-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.publisher-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/lxml-1.3.6-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.traversing-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.security-3.4.0-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.schema-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.interface-3.4.1-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.i18n-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.deferredimport-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.component-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.folder-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.dependable-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.debug-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.annotation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.testbrowser-3.4.2-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.tales-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.location-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.event-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.schema-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.pagetemplate-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.formlib-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.rotterdam-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.basicskin-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.principalannotation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.zopeappgenerations-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.locales-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.i18n-3.4.4-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.interface-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.generations-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.content-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.modulealias-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/ZConfig-2.5-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.zapi-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.server-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.applicationcontrol-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.exceptions-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.copypastemove-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zdaemon-2.0.1-py2.4.egg',
+ '/opt/zope/packages/eggs/ZODB3-3.8.0b4-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.securitypolicy-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.i18nmessageid-3.4.3-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.configuration-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.deprecation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.exception-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.http-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.error-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.pagetemplate-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.datetime-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.contenttype-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.proxy-3.4.0-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.cachedescriptors-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.broken-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.dublincore-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.size-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.filerepresentation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.lifecycleevent-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.dottedname-3.4.2-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.thread-3.4-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.session-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/pytz-2007g-py2.4.egg',
+ '/opt/zope/packages/eggs/ClientForm-0.2.7-py2.4.egg',
+ '/opt/zope/packages/eggs/mechanize-0.1.7b-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.tal-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zodbcode-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.renderer-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.server-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.hookable-3.4.0-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.session-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.minmax-1.1.0-py2.4.egg',
+ '/opt/zope/packages/eggs/RestrictedPython-3.4.2-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.structuredtext-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/docutils-0.4-py2.4.egg',
+ ]
+
+import os
+sys.argv[0] = os.path.abspath(sys.argv[0])
+os.chdir('/opt/zope/packages/z3c.rest/parts/test')
+
+
+import zope.testing.testrunner
+
+if __name__ == '__main__':
+ zope.testing.testrunner.run([
+ '--test-path', '/opt/zope/packages/z3c.rest/src',
+ ])
Property changes on: z3c.rest/trunk/bin/test
___________________________________________________________________
Name: svn:executable
+
Added: z3c.rest/trunk/bootstrap.py
===================================================================
--- z3c.rest/trunk/bootstrap.py (rev 0)
+++ z3c.rest/trunk/bootstrap.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+ cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, sys.executable,
+ '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
Property changes on: z3c.rest/trunk/bootstrap.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/buildout.cfg
===================================================================
--- z3c.rest/trunk/buildout.cfg (rev 0)
+++ z3c.rest/trunk/buildout.cfg 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,37 @@
+[buildout]
+develop = .
+index = http://download.zope.org/zope3.4
+parts = demo test coverage
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.rest [app, test]
+
+[coverage]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+
+[zope3]
+location = .
+
+[form-demo-app]
+recipe = zc.zope3recipes:app
+site.zcml = <include package="z3c.rest" file="application.zcml" />
+eggs = z3c.rest [app]
+
+[demo]
+recipe = zc.zope3recipes:instance
+application = form-demo-app
+zope.conf = ${database:zconfig}
+ <server>
+ type HTTP
+ address 8080
+ </server>
+
+ <server>
+ type REST-HTTP
+ address 8081
+ </server>
+
+[database]
+recipe = zc.recipe.filestorage
Added: z3c.rest/trunk/coverage/report/all.html
===================================================================
--- z3c.rest/trunk/coverage/report/all.html (rev 0)
+++ z3c.rest/trunk/coverage/report/all.html 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,29 @@
+
+ <html>
+ <head><title>Unit test coverage for z3c</title>
+ <style type="text/css">
+ a {text-decoration: none; display: block; padding-right: 1em;}
+ a:hover {background: #EFA;}
+ hr {height: 1px; border: none; border-top: 1px solid gray;}
+ .notcovered {background: #FCC;}
+ .footer {margin: 2em; font-size: small; color: gray;}
+ </style>
+ </head>
+ <body><h1>Unit test coverage for z3c</h1>
+ <table>
+
+<tr><td><a href="z3c.html">z3c/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.html"> rest/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.traverser.html"> traverser.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 23 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.interfaces.html"> interfaces.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 58 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.rest.html"> rest.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 50 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.client.html"> client.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 128 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.null.html"> null.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 23 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.__init__.html"> __init__.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 0 uncovered)</td></tr>
+</table><hr/>
+
+ <div class="footer">
+ Generated for revision exported on 2007-11-30 11:34:55.110922Z
+ </div>
+ </body>
+ </html>
Added: z3c.rest/trunk/coverage/report/z3c.html
===================================================================
--- z3c.rest/trunk/coverage/report/z3c.html (rev 0)
+++ z3c.rest/trunk/coverage/report/z3c.html 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,23 @@
+
+ <html>
+ <head><title>Unit test coverage for z3c</title>
+ <style type="text/css">
+ a {text-decoration: none; display: block; padding-right: 1em;}
+ a:hover {background: #EFA;}
+ hr {height: 1px; border: none; border-top: 1px solid gray;}
+ .notcovered {background: #FCC;}
+ .footer {margin: 2em; font-size: small; color: gray;}
+ </style>
+ </head>
+ <body><h1>Unit test coverage for z3c</h1>
+ <table>
+
+<tr><td><a href="z3c.html">z3c/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.html"> rest/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+</table><hr/>
+
+ <div class="footer">
+ Generated for revision exported on 2007-11-30 11:34:55.110922Z
+ </div>
+ </body>
+ </html>
Added: z3c.rest/trunk/coverage/report/z3c.rest.__init__.html
===================================================================
--- z3c.rest/trunk/coverage/report/z3c.rest.__init__.html (rev 0)
+++ z3c.rest/trunk/coverage/report/z3c.rest.__init__.html 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,27 @@
+
+ <html>
+ <head><title>Unit test coverage for z3c.rest.__init__</title>
+ <style type="text/css">
+ a {text-decoration: none; display: block; padding-right: 1em;}
+ a:hover {background: #EFA;}
+ hr {height: 1px; border: none; border-top: 1px solid gray;}
+ .notcovered {background: #FCC;}
+ .footer {margin: 2em; font-size: small; color: gray;}
+ </style>
+ </head>
+ <body><h1>Unit test coverage for z3c.rest.__init__</h1>
+ <table>
+
+<tr><td><a href="z3c.html">z3c/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.html"> rest/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.__init__.html"> __init__.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 0 uncovered)</td></tr>
+</table><hr/>
+<pre>
+ <I><FONT COLOR="#B22222"># Make a package
+</FONT></I></pre>
+
+ <div class="footer">
+ Generated for revision exported on 2007-11-30 11:34:55.110922Z
+ </div>
+ </body>
+ </html>
Added: z3c.rest/trunk/coverage/report/z3c.rest.client.html
===================================================================
--- z3c.rest/trunk/coverage/report/z3c.rest.client.html (rev 0)
+++ z3c.rest/trunk/coverage/report/z3c.rest.client.html 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,227 @@
+
+ <html>
+ <head><title>Unit test coverage for z3c.rest.client</title>
+ <style type="text/css">
+ a {text-decoration: none; display: block; padding-right: 1em;}
+ a:hover {background: #EFA;}
+ hr {height: 1px; border: none; border-top: 1px solid gray;}
+ .notcovered {background: #FCC;}
+ .footer {margin: 2em; font-size: small; color: gray;}
+ </style>
+ </head>
+ <body><h1>Unit test coverage for z3c.rest.client</h1>
+ <table>
+
+<tr><td><a href="z3c.html">z3c/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.html"> rest/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.client.html"> client.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 128 uncovered)</td></tr>
+</table><hr/>
+<pre>
+ <I><FONT COLOR="#B22222">##############################################################################
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222"># Copyright (c) 2007 Zope Corporation and Contributors.
+</FONT></I> <I><FONT COLOR="#B22222"># All Rights Reserved.
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222"># This software is subject to the provisions of the Zope Public License,
+</FONT></I> <I><FONT COLOR="#B22222"># Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+</FONT></I> <I><FONT COLOR="#B22222"># THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+</FONT></I> <I><FONT COLOR="#B22222"># WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+</FONT></I> <I><FONT COLOR="#B22222"># WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+</FONT></I> <I><FONT COLOR="#B22222"># FOR A PARTICULAR PURPOSE.
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222">##############################################################################
+</FONT></I> <B><FONT COLOR="#BC8F8F">"""REST Client
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """</FONT></B>
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> lxml
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> httplib
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> socket
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> urllib
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> urlparse
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> base64
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> zope.interface
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> z3c.rest <B><FONT COLOR="#A020F0">import</FONT></B> interfaces
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">isRelativeURL</FONT></B>(url):
+ <B><FONT COLOR="#BC8F8F">"""Determines whether the given URL is a relative path segment."""</FONT></B>
+ 37: pieces = urlparse.urlparse(url)
+ 37: <B><FONT COLOR="#A020F0">if</FONT></B> <B><FONT COLOR="#A020F0">not</FONT></B> pieces[0] <B><FONT COLOR="#A020F0">and</FONT></B> <B><FONT COLOR="#A020F0">not</FONT></B> pieces[1]:
+ 5: <B><FONT COLOR="#A020F0">return</FONT></B> True
+ 32: <B><FONT COLOR="#A020F0">return</FONT></B> False
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">absoluteURL</FONT></B>(base, url):
+ <B><FONT COLOR="#BC8F8F">"""Convertes a URL to an absolute URL given a base."""</FONT></B>
+ 37: <B><FONT COLOR="#A020F0">if</FONT></B> isRelativeURL(url):
+ 5: fullUrl = urlparse.urljoin(base, url)
+ <B><FONT COLOR="#A020F0">else</FONT></B>:
+ 32: fullUrl = url
+ 37: pieces = list(urlparse.urlparse(fullUrl))
+ 37: <B><FONT COLOR="#A020F0">if</FONT></B> <B><FONT COLOR="#A020F0">not</FONT></B> pieces[2].endswith(<B><FONT COLOR="#BC8F8F">'/'</FONT></B>):
+ 18: pieces[2] += <B><FONT COLOR="#BC8F8F">'/'</FONT></B>
+ 37: <B><FONT COLOR="#A020F0">return</FONT></B> urlparse.urlunparse(pieces)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">getFullPath</FONT></B>(pieces, params):
+ <B><FONT COLOR="#BC8F8F">"""Build a full httplib request path, including a query string."""</FONT></B>
+ 32: query = <B><FONT COLOR="#BC8F8F">''</FONT></B>
+ 32: <B><FONT COLOR="#A020F0">if</FONT></B> pieces[4]:
+ 2: query = pieces[4]
+ 32: <B><FONT COLOR="#A020F0">if</FONT></B> params:
+ 2: encParams = urllib.urlencode(params)
+ 2: <B><FONT COLOR="#A020F0">if</FONT></B> query:
+ 1: query += <B><FONT COLOR="#BC8F8F">'&'</FONT></B> + encParams
+ <B><FONT COLOR="#A020F0">else</FONT></B>:
+ 1: query = encParams
+ 32: <B><FONT COLOR="#A020F0">return</FONT></B> urlparse.urlunparse(
+ (<B><FONT COLOR="#BC8F8F">''</FONT></B>, <B><FONT COLOR="#BC8F8F">''</FONT></B>, pieces[2], pieces[3], query, pieces[5]))
+
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> XLink(object):
+ <B><FONT COLOR="#BC8F8F">"""A link implementation for simple XLinks."""</FONT></B>
+ 1: zope.interface.implements(interfaces.ILink)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__init__</FONT></B>(self, client, title, url):
+ 5: self._client = client
+ 5: self.title = title
+ 5: self.url = url
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">click</FONT></B>(self):
+ <B><FONT COLOR="#BC8F8F">"""See interfaces.ILink"""</FONT></B>
+ 3: self._client.get(self.url)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__repr__</FONT></B>(self):
+ 2: <B><FONT COLOR="#A020F0">return</FONT></B> <B><FONT COLOR="#BC8F8F">'<%s title=%r url=%r>'</FONT></B> %(
+ self.__class__.__name__, self.title, self.url)
+
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> RESTClient(object):
+ 1: zope.interface.implements(interfaces.IRESTClient)
+
+ 1: connectionFactory = httplib.HTTPConnection
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__init__</FONT></B>(self, url=None):
+ 3: self.requestHeaders = {}
+ 3: self._reset()
+ 3: self._history = []
+ 3: self._requestData = None
+ 3: self.url = <B><FONT COLOR="#BC8F8F">''</FONT></B>
+ 3: <B><FONT COLOR="#A020F0">if</FONT></B> url:
+ 1: self.open(url)
+
+ 1: @property
+ <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">fullStatus</FONT></B>(self):
+ 12: <B><FONT COLOR="#A020F0">return</FONT></B> <B><FONT COLOR="#BC8F8F">'%i %s'</FONT></B> %(self.status, self.reason)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">_reset</FONT></B>(self):
+ 35: self.headers = []
+ 35: self.contents = {}
+ 35: self.status = None
+ 35: self.reason = None
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">open</FONT></B>(self, url=<B><FONT COLOR="#BC8F8F">''</FONT></B>, data=None, params=None, headers=None, method=<B><FONT COLOR="#BC8F8F">'GET'</FONT></B>):
+ <I><FONT COLOR="#B22222"># Create a correct absolute URL and set it.
+</FONT></I> 32: self.url = absoluteURL(self.url, url)
+
+ <I><FONT COLOR="#B22222"># Create the full set of request headers
+</FONT></I> 32: requestHeaders = self.requestHeaders.copy()
+ 32: <B><FONT COLOR="#A020F0">if</FONT></B> headers:
+ 1: requestHeaders.update(headers)
+
+ <I><FONT COLOR="#B22222"># Let's now reset all response values
+</FONT></I> 32: self._reset()
+
+ <I><FONT COLOR="#B22222"># Store all the request data
+</FONT></I> 32: self._requestData = (url, data, params, headers, method)
+
+ <I><FONT COLOR="#B22222"># Make a connection and retrieve the result
+</FONT></I> 32: pieces = urlparse.urlparse(self.url)
+ 32: connection = self.connectionFactory(pieces[1])
+ 32: <B><FONT COLOR="#A020F0">try</FONT></B>:
+ 32: connection.request(
+ method, getFullPath(pieces, params), data, requestHeaders)
+ 30: response = connection.getresponse()
+ 30: connection.close()
+ 2: <B><FONT COLOR="#A020F0">except</FONT></B> socket.error, e:
+ 1: connection.close()
+ 1: self.status, self.reason = e.args
+ 1: self._addHistory()
+ 1: <B><FONT COLOR="#A020F0">raise</FONT></B> e
+ <B><FONT COLOR="#A020F0">else</FONT></B>:
+ 30: self.headers = response.getheaders()
+ 30: self.contents = response.read()
+ 30: self.status = response.status
+ 30: self.reason = response.reason
+ 30: self._addHistory()
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">get</FONT></B>(self, url=<B><FONT COLOR="#BC8F8F">''</FONT></B>, params=None, headers=None):
+ 17: self.open(url, None, params, headers)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">put</FONT></B>(self, url=<B><FONT COLOR="#BC8F8F">''</FONT></B>, data=<B><FONT COLOR="#BC8F8F">''</FONT></B>, params=None, headers=None):
+ 6: self.open(url, data, params, headers, <B><FONT COLOR="#BC8F8F">'PUT'</FONT></B>)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">post</FONT></B>(self, url=<B><FONT COLOR="#BC8F8F">''</FONT></B>, data=<B><FONT COLOR="#BC8F8F">''</FONT></B>, params=None, headers=None):
+ 1: self.open(url, data, params, headers, <B><FONT COLOR="#BC8F8F">'POST'</FONT></B>)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">delete</FONT></B>(self, url=<B><FONT COLOR="#BC8F8F">''</FONT></B>, params=None, headers=None):
+ 4: self.open(url, None, params, headers, <B><FONT COLOR="#BC8F8F">'DELETE'</FONT></B>)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">setCredentials</FONT></B>(self, username, password):
+ 1: creds = username + u<B><FONT COLOR="#BC8F8F">':'</FONT></B> + password
+ 1: creds = <B><FONT COLOR="#BC8F8F">"Basic "</FONT></B> + base64.encodestring(creds.encode(<B><FONT COLOR="#BC8F8F">'utf-8'</FONT></B>)).strip()
+ 1: self.requestHeaders[<B><FONT COLOR="#BC8F8F">'Authorization'</FONT></B>] = creds
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">_addHistory</FONT></B>(self):
+ 31: self._history.append((
+ self.url, self.requestHeaders, self.headers, self.contents,
+ self.status, self.reason, self._requestData
+ ))
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">goBack</FONT></B>(self, count=1):
+ <I><FONT COLOR="#B22222"># The user really does not want to go back.
+</FONT></I> 4: <B><FONT COLOR="#A020F0">if</FONT></B> count == 0:
+ 1: <B><FONT COLOR="#A020F0">return</FONT></B>
+ <I><FONT COLOR="#B22222"># The user wants to reach before a pre-historical state.
+</FONT></I> 3: <B><FONT COLOR="#A020F0">if</FONT></B> len(self._history) < count:
+ 1: <B><FONT COLOR="#A020F0">raise</FONT></B> ValueError(<B><FONT COLOR="#BC8F8F">'There is not enough history.'</FONT></B>)
+ <I><FONT COLOR="#B22222"># Let's now get the entry and set the history back to that state.
+</FONT></I> 2: entry = self._history[-(count+1)]
+ 2: self._history = self._history[:-count]
+ <I><FONT COLOR="#B22222"># Reset the state.
+</FONT></I> 2: (self.url, self.requestHeaders, self.headers, self.contents,
+ self.status, self.reason, self._requestData) = entry
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">reload</FONT></B>(self):
+ 1: self.open(*self._requestData)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">getLink</FONT></B>(self, title=None, url=None, index=0):
+ 6: nsmap = {<B><FONT COLOR="#BC8F8F">'xlink'</FONT></B>: <B><FONT COLOR="#BC8F8F">"http://www.w3.org/1999/xlink"</FONT></B>}
+ 6: tree = lxml.etree.fromstring(self.contents)
+ 6: res = []
+ 6: <B><FONT COLOR="#A020F0">if</FONT></B> title <B><FONT COLOR="#A020F0">is</FONT></B> <B><FONT COLOR="#A020F0">not</FONT></B> None:
+ 4: res = tree.xpath(
+ <B><FONT COLOR="#BC8F8F">'//*[@xlink:title="%s"]'</FONT></B> %title, nsmap)
+ 2: <B><FONT COLOR="#A020F0">elif</FONT></B> url <B><FONT COLOR="#A020F0">is</FONT></B> <B><FONT COLOR="#A020F0">not</FONT></B> None:
+ 1: res = tree.xpath(
+ <B><FONT COLOR="#BC8F8F">'//*[@xlink:href="%s"]'</FONT></B> %url, nsmap)
+ <B><FONT COLOR="#A020F0">else</FONT></B>:
+ 1: <B><FONT COLOR="#A020F0">raise</FONT></B> ValueError(<B><FONT COLOR="#BC8F8F">'You must specify a title or URL.'</FONT></B>)
+ 5: elem = res[index]
+ 5: url = elem.attrib.get(<B><FONT COLOR="#BC8F8F">'{%(xlink)s}href'</FONT></B> %nsmap, <B><FONT COLOR="#BC8F8F">''</FONT></B>)
+ 5: <B><FONT COLOR="#A020F0">return</FONT></B> XLink(self,
+ 5: elem.attrib.get(<B><FONT COLOR="#BC8F8F">'{%(xlink)s}title'</FONT></B> %nsmap),
+ 5: absoluteURL(self.url, url))
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">xpath</FONT></B>(self, expr, nsmap=None, single=False):
+ 3: res = lxml.etree.fromstring(self.contents).xpath(expr, nsmap)
+ 3: <B><FONT COLOR="#A020F0">if</FONT></B> <B><FONT COLOR="#A020F0">not</FONT></B> single:
+ 1: <B><FONT COLOR="#A020F0">return</FONT></B> res
+ 2: <B><FONT COLOR="#A020F0">if</FONT></B> len(res) != 1:
+ 1: <B><FONT COLOR="#A020F0">raise</FONT></B> ValueError(<B><FONT COLOR="#BC8F8F">'XPath expression returned more than one result.'</FONT></B>)
+ 1: <B><FONT COLOR="#A020F0">return</FONT></B> res[0]
+</pre>
+
+ <div class="footer">
+ Generated for revision exported on 2007-11-30 11:34:55.110922Z
+ </div>
+ </body>
+ </html>
Added: z3c.rest/trunk/coverage/report/z3c.rest.html
===================================================================
--- z3c.rest/trunk/coverage/report/z3c.rest.html (rev 0)
+++ z3c.rest/trunk/coverage/report/z3c.rest.html 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,29 @@
+
+ <html>
+ <head><title>Unit test coverage for z3c.rest</title>
+ <style type="text/css">
+ a {text-decoration: none; display: block; padding-right: 1em;}
+ a:hover {background: #EFA;}
+ hr {height: 1px; border: none; border-top: 1px solid gray;}
+ .notcovered {background: #FCC;}
+ .footer {margin: 2em; font-size: small; color: gray;}
+ </style>
+ </head>
+ <body><h1>Unit test coverage for z3c.rest</h1>
+ <table>
+
+<tr><td><a href="z3c.html">z3c/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.html"> rest/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.__init__.html"> __init__.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 0 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.client.html"> client.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 128 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.interfaces.html"> interfaces.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 58 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.null.html"> null.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 23 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.rest.html"> rest.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 50 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.traverser.html"> traverser.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 23 uncovered)</td></tr>
+</table><hr/>
+
+ <div class="footer">
+ Generated for revision exported on 2007-11-30 11:34:55.110922Z
+ </div>
+ </body>
+ </html>
Added: z3c.rest/trunk/coverage/report/z3c.rest.interfaces.html
===================================================================
--- z3c.rest/trunk/coverage/report/z3c.rest.interfaces.html (rev 0)
+++ z3c.rest/trunk/coverage/report/z3c.rest.interfaces.html 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,240 @@
+
+ <html>
+ <head><title>Unit test coverage for z3c.rest.interfaces</title>
+ <style type="text/css">
+ a {text-decoration: none; display: block; padding-right: 1em;}
+ a:hover {background: #EFA;}
+ hr {height: 1px; border: none; border-top: 1px solid gray;}
+ .notcovered {background: #FCC;}
+ .footer {margin: 2em; font-size: small; color: gray;}
+ </style>
+ </head>
+ <body><h1>Unit test coverage for z3c.rest.interfaces</h1>
+ <table>
+
+<tr><td><a href="z3c.html">z3c/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.html"> rest/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.interfaces.html"> interfaces.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 58 uncovered)</td></tr>
+</table><hr/>
+<pre>
+ <I><FONT COLOR="#B22222">##############################################################################
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222"># Copyright (c) 2007 Zope Corporation and Contributors.
+</FONT></I> <I><FONT COLOR="#B22222"># All Rights Reserved.
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222"># This software is subject to the provisions of the Zope Public License,
+</FONT></I> <I><FONT COLOR="#B22222"># Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+</FONT></I> <I><FONT COLOR="#B22222"># THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+</FONT></I> <I><FONT COLOR="#B22222"># WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+</FONT></I> <I><FONT COLOR="#B22222"># WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+</FONT></I> <I><FONT COLOR="#B22222"># FOR A PARTICULAR PURPOSE.
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222">##############################################################################
+</FONT></I> <B><FONT COLOR="#BC8F8F">"""REST publishing interfaces.
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """</FONT></B>
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> zope.interface
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> zope.schema
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.location.interfaces <B><FONT COLOR="#A020F0">import</FONT></B> ILocation
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.publisher.interfaces <B><FONT COLOR="#A020F0">import</FONT></B> http
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> IRESTRequest(http.IHTTPRequest):
+ 1: <B><FONT COLOR="#BC8F8F">"""A special type of request for handling REST-based requests."""</FONT></B>
+
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> IRESTView(ILocation):
+ 1: <B><FONT COLOR="#BC8F8F">"""A REST view"""</FONT></B>
+
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> ILink(zope.interface.Interface):
+ <B><FONT COLOR="#BC8F8F">"""An object representing a hyperlink."""</FONT></B>
+
+ 1: href = zope.schema.TextLine(
+ title=u<B><FONT COLOR="#BC8F8F">"URL"</FONT></B>,
+ 1: description=u<B><FONT COLOR="#BC8F8F">"The normalized URL of the link"</FONT></B>,
+ 1: required=False)
+
+ 1: title = zope.schema.TextLine(
+ title=u<B><FONT COLOR="#BC8F8F">'Title'</FONT></B>,
+ 1: description=u<B><FONT COLOR="#BC8F8F">'The title of the link'</FONT></B>,
+ 1: required=False)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">click</FONT></B>():
+ <B><FONT COLOR="#BC8F8F">"""click the link, going to the URL referenced"""</FONT></B>
+
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> IRESTClient(zope.interface.Interface):
+ <B><FONT COLOR="#BC8F8F">"""A REST client.
+
+ This client provides a high-level API to access RESTful Web APIs. The
+ interface is designed to resemble the test browser API as much as
+ practical.
+ """</FONT></B>
+
+ 1: connectionFactory = zope.schema.Field(
+ title=u<B><FONT COLOR="#BC8F8F">"Connection Facotry"</FONT></B>,
+ 1: description=(u<B><FONT COLOR="#BC8F8F">'A callable that creates an `httplib`-compliant '</FONT></B>
+ u<B><FONT COLOR="#BC8F8F">'connection object.'</FONT></B>),
+ 1: required=True)
+
+ 1: requestHeaders = zope.schema.Dict(
+ title=u<B><FONT COLOR="#BC8F8F">"Request Headers"</FONT></B>,
+ 1: description=(u<B><FONT COLOR="#BC8F8F">"A set of headers that will be sent in every request."</FONT></B>),
+ 1: required=True)
+
+ 1: url = zope.schema.URI(
+ title=u<B><FONT COLOR="#BC8F8F">"URL"</FONT></B>,
+ 1: description=(u<B><FONT COLOR="#BC8F8F">"The URL the browser is currently showing. It is "</FONT></B>
+ u<B><FONT COLOR="#BC8F8F">"always a full, absolute URL."</FONT></B>),
+ 1: required=True)
+
+ 1: headers = zope.schema.List(
+ title=u<B><FONT COLOR="#BC8F8F">"Response Headers"</FONT></B>,
+ 1: description=(u<B><FONT COLOR="#BC8F8F">'A list of all headers that have been returned '</FONT></B>
+ u<B><FONT COLOR="#BC8F8F">'in the last request.'</FONT></B>),
+ 1: required=True)
+
+ 1: contents = zope.schema.Text(
+ title=u<B><FONT COLOR="#BC8F8F">"Contents"</FONT></B>,
+ 1: description=u<B><FONT COLOR="#BC8F8F">"The complete response body of the HTTP request."</FONT></B>,
+ 1: required=True)
+
+ 1: status = zope.schema.Int(
+ title=u<B><FONT COLOR="#BC8F8F">"Status"</FONT></B>,
+ 1: description=u<B><FONT COLOR="#BC8F8F">"The status code of the last response."</FONT></B>,
+ 1: min=0,
+ 1: required=True)
+
+ 1: reason = zope.schema.TextLine(
+ title=u<B><FONT COLOR="#BC8F8F">"Reason"</FONT></B>,
+ 1: description=u<B><FONT COLOR="#BC8F8F">"A short explanation of the status of the last response."</FONT></B>,
+ 1: required=True)
+
+ 1: fullStatus = zope.schema.TextLine(
+ title=u<B><FONT COLOR="#BC8F8F">"Full Status"</FONT></B>,
+ 1: description=u<B><FONT COLOR="#BC8F8F">"The status code and reason of the last response."</FONT></B>,
+ 1: required=True)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">open</FONT></B>(url=<B><FONT COLOR="#BC8F8F">''</FONT></B>, data=None, params=None, headers=None, method=<B><FONT COLOR="#BC8F8F">'GET'</FONT></B>):
+ <B><FONT COLOR="#BC8F8F">"""Open a URL and retrieve the result.
+
+ The `url` argument can either be a full URL or a URL relative to the
+ previous one. If no URL is specified, then the previous URL will be
+ used.
+
+ The `data` is the contents of the request body. It is used to send
+ information to the server.
+
+ The `params` describe additional query parameters that will be added
+ to the request. Query string parameters are frequently used by RESTive
+ APIs to provide additional return value options.
+
+ The `headers` specify additional request headers that are specific for
+ this particular request.
+
+ The `method` specifies the HTTP method or verb to use to access the
+ resource on the server. While there are only a few methods in RFC
+ 2616, an string is allowed, since any particular API can extend the
+ set of allowed methods.
+ """</FONT></B>
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">get</FONT></B>(url=<B><FONT COLOR="#BC8F8F">''</FONT></B>, params=None, headers=None):
+ <B><FONT COLOR="#BC8F8F">"""Make a GET request to the server.
+
+ For argument details see ``open()``.
+ """</FONT></B>
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">put</FONT></B>(url=<B><FONT COLOR="#BC8F8F">''</FONT></B>, data=<B><FONT COLOR="#BC8F8F">''</FONT></B>, params=None, headers=None):
+ <B><FONT COLOR="#BC8F8F">"""Make a PUT request to the server.
+
+ For argument details see ``open()``.
+ """</FONT></B>
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">post</FONT></B>(url=<B><FONT COLOR="#BC8F8F">''</FONT></B>, data=<B><FONT COLOR="#BC8F8F">''</FONT></B>, params=None, headers=None):
+ <B><FONT COLOR="#BC8F8F">"""Make a POST request to the server.
+
+ For argument details see ``open()``.
+ """</FONT></B>
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">delete</FONT></B>(url=<B><FONT COLOR="#BC8F8F">''</FONT></B>, params=None, headers=None):
+ <B><FONT COLOR="#BC8F8F">"""Make a DELETE request to the server.
+
+ For argument details see ``open()``.
+ """</FONT></B>
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">setCredentials</FONT></B>(username, password):
+ <B><FONT COLOR="#BC8F8F">"""Set the credentials.
+
+ This method adds the necessary information to authenticate the user. A
+ common example is basic auth, which inserts the `Authentication`
+ request header.
+ """</FONT></B>
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">goBack</FONT></B>(count=1):
+ <B><FONT COLOR="#BC8F8F">"""Go back in history by a certain amount of visisted pages.
+
+ The ``count`` argument specifies how far to go back. It is set to 1 by
+ default.
+ """</FONT></B>
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">reload</FONT></B>():
+ <B><FONT COLOR="#BC8F8F">"""Reload the current resource.
+
+ All arguments, including the HTTP method, parameters and additional
+ headers are honored.
+ """</FONT></B>
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">getLink</FONT></B>(title=None, url=None, index=0):
+ <B><FONT COLOR="#BC8F8F">"""Return an ILink of the found link.
+
+ This method assumes that the current content type of the response body
+ is XML.
+
+ The link is found by the arguments of the method. Only one can be
+ used at a time:
+
+ o ``title`` -- The title or a sub-string thereof of the link.
+
+ o ``url`` -- The URL the link is going to.
+
+ If multiple matching links are found, the `index` specifies which one
+ to use.
+ """</FONT></B>
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">xpath</FONT></B>(expr, nsmap=None, single=False):
+ <B><FONT COLOR="#BC8F8F">"""Returns the result of an XPath search expression.
+
+ This method assumes that the current content type of the response body
+ is XML.
+
+ The `expr` argument is the actual XPath expression. If the expression
+ is incorrect, an unspecified error must be raised.
+
+ The `nsmap` is a mapping from the short version to the full URL of
+ each XML namespace used in the expression.
+
+ If `single` is set to ``True``, then only one result is returned,
+ instead of a list. If the XPath expression results in more than one
+ result, a ``ValueError`` must be raised.
+ """</FONT></B>
+
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> IPublisherRESTClient(IRESTClient):
+ <B><FONT COLOR="#BC8F8F">"""An extension to the REST client to support test-specific features."""</FONT></B>
+
+ 1: handleErrors = zope.schema.Bool(
+ title=u<B><FONT COLOR="#BC8F8F">"Handle Errors"</FONT></B>,
+ 1: description=(u<B><FONT COLOR="#BC8F8F">"Describes whether server-side errors will be handled "</FONT></B>
+ u<B><FONT COLOR="#BC8F8F">"by the publisher. If set to ``False``, the error will "</FONT></B>
+ u<B><FONT COLOR="#BC8F8F">"progress all the way to the test, which is good for "</FONT></B>
+ u<B><FONT COLOR="#BC8F8F">"debugging."</FONT></B>),
+ 1: default=True,
+ 1: required=True)
+</pre>
+
+ <div class="footer">
+ Generated for revision exported on 2007-11-30 11:34:55.110922Z
+ </div>
+ </body>
+ </html>
Added: z3c.rest/trunk/coverage/report/z3c.rest.null.html
===================================================================
--- z3c.rest/trunk/coverage/report/z3c.rest.null.html (rev 0)
+++ z3c.rest/trunk/coverage/report/z3c.rest.null.html 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,80 @@
+
+ <html>
+ <head><title>Unit test coverage for z3c.rest.null</title>
+ <style type="text/css">
+ a {text-decoration: none; display: block; padding-right: 1em;}
+ a:hover {background: #EFA;}
+ hr {height: 1px; border: none; border-top: 1px solid gray;}
+ .notcovered {background: #FCC;}
+ .footer {margin: 2em; font-size: small; color: gray;}
+ </style>
+ </head>
+ <body><h1>Unit test coverage for z3c.rest.null</h1>
+ <table>
+
+<tr><td><a href="z3c.html">z3c/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.html"> rest/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.null.html"> null.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 23 uncovered)</td></tr>
+</table><hr/>
+<pre>
+ <I><FONT COLOR="#B22222">##############################################################################
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222"># Copyright (c) 2007 Zope Corporation and Contributors.
+</FONT></I> <I><FONT COLOR="#B22222"># All Rights Reserved.
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222"># This software is subject to the provisions of the Zope Public License,
+</FONT></I> <I><FONT COLOR="#B22222"># Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+</FONT></I> <I><FONT COLOR="#B22222"># THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+</FONT></I> <I><FONT COLOR="#B22222"># WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+</FONT></I> <I><FONT COLOR="#B22222"># WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+</FONT></I> <I><FONT COLOR="#B22222"># FOR A PARTICULAR PURPOSE.
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222">##############################################################################
+</FONT></I> <B><FONT COLOR="#BC8F8F">"""Null resource.
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """</FONT></B>
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> zope.component
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> zope.interface
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.app.http.interfaces <B><FONT COLOR="#A020F0">import</FONT></B> INullResource
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.location <B><FONT COLOR="#A020F0">import</FONT></B> location
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.traversing.browser <B><FONT COLOR="#A020F0">import</FONT></B> absoluteURL
+
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> z3c.rest <B><FONT COLOR="#A020F0">import</FONT></B> rest
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> NullResource(location.Location):
+ <B><FONT COLOR="#BC8F8F">"""Object representing objects to be created by a `PUT`."""</FONT></B>
+ 1: zope.interface.implements(INullResource)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__init__</FONT></B>(self, container, name):
+ 6: self.__parent__ = self.container = container
+ 6: self.__name__ = self.name = name
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__repr__</FONT></B>(self):
+ 1: <B><FONT COLOR="#A020F0">return</FONT></B> <B><FONT COLOR="#BC8F8F">'<%s %r>'</FONT></B> %(self.__class__.__name__, self.name)
+
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> NullPUT(rest.RESTView):
+ <B><FONT COLOR="#BC8F8F">"""Put handler for null resources"""</FONT></B>
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">PUT</FONT></B>(self):
+ 6: nullPut = zope.component.queryMultiAdapter(
+ (self.context.container, self.request), name=<B><FONT COLOR="#BC8F8F">'NullPUT'</FONT></B>)
+ 6: <B><FONT COLOR="#A020F0">if</FONT></B> nullPut <B><FONT COLOR="#A020F0">is</FONT></B> None:
+ <I><FONT COLOR="#B22222"># See RFC 2616, section 9.6
+</FONT></I> 1: self.request.response.setStatus(501)
+ 1: <B><FONT COLOR="#A020F0">return</FONT></B>
+
+ 5: newObj = nullPut.NullPUT(self.context)
+
+ <I><FONT COLOR="#B22222"># See RFC 2616, section 9.6
+</FONT></I> 4: self.request.response.setStatus(201)
+ 4: self.request.response.setHeader(
+ <B><FONT COLOR="#BC8F8F">'Location'</FONT></B>, absoluteURL(newObj, self.request))
+</pre>
+
+ <div class="footer">
+ Generated for revision exported on 2007-11-30 11:34:55.110922Z
+ </div>
+ </body>
+ </html>
Added: z3c.rest/trunk/coverage/report/z3c.rest.rest.html
===================================================================
--- z3c.rest/trunk/coverage/report/z3c.rest.rest.html (rev 0)
+++ z3c.rest/trunk/coverage/report/z3c.rest.rest.html 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,120 @@
+
+ <html>
+ <head><title>Unit test coverage for z3c.rest.rest</title>
+ <style type="text/css">
+ a {text-decoration: none; display: block; padding-right: 1em;}
+ a:hover {background: #EFA;}
+ hr {height: 1px; border: none; border-top: 1px solid gray;}
+ .notcovered {background: #FCC;}
+ .footer {margin: 2em; font-size: small; color: gray;}
+ </style>
+ </head>
+ <body><h1>Unit test coverage for z3c.rest.rest</h1>
+ <table>
+
+<tr><td><a href="z3c.html">z3c/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.html"> rest/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.rest.html"> rest.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 50 uncovered)</td></tr>
+</table><hr/>
+<pre>
+ <I><FONT COLOR="#B22222">##############################################################################
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222"># Copyright (c) 2007 Zope Corporation and Contributors.
+</FONT></I> <I><FONT COLOR="#B22222"># All Rights Reserved.
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222"># This software is subject to the provisions of the Zope Public License,
+</FONT></I> <I><FONT COLOR="#B22222"># Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+</FONT></I> <I><FONT COLOR="#B22222"># THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+</FONT></I> <I><FONT COLOR="#B22222"># WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+</FONT></I> <I><FONT COLOR="#B22222"># WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+</FONT></I> <I><FONT COLOR="#B22222"># FOR A PARTICULAR PURPOSE.
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222">##############################################################################
+</FONT></I> <B><FONT COLOR="#BC8F8F">"""REST publication and publisher factories
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """</FONT></B>
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> cgi
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> zope.interface
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.app.publication.interfaces <B><FONT COLOR="#A020F0">import</FONT></B> IPublicationRequestFactory
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.app.publication.http <B><FONT COLOR="#A020F0">import</FONT></B> HTTPPublication
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.publisher.http <B><FONT COLOR="#A020F0">import</FONT></B> HTTPRequest
+
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> z3c.rest <B><FONT COLOR="#A020F0">import</FONT></B> interfaces
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> RESTRequest(HTTPRequest):
+ 1: zope.interface.implements(interfaces.IRESTRequest)
+
+ 1: __slots__ = (
+ <B><FONT COLOR="#BC8F8F">'parameters'</FONT></B>, <I><FONT COLOR="#B22222"># Parameters sent via the query string.
+</FONT></I> )
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__init__</FONT></B>(self, body_instream, environ, response=None):
+ 39: self.parameters = {}
+ 39: super(RESTRequest, self).__init__(body_instream, environ, response)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">processInputs</FONT></B>(self):
+ <B><FONT COLOR="#BC8F8F">'See IPublisherRequest'</FONT></B>
+ 35: <B><FONT COLOR="#A020F0">if</FONT></B> <B><FONT COLOR="#BC8F8F">'QUERY_STRING'</FONT></B> <B><FONT COLOR="#A020F0">not</FONT></B> <B><FONT COLOR="#A020F0">in</FONT></B> self._environ:
+ 31: <B><FONT COLOR="#A020F0">return</FONT></B>
+ <I><FONT COLOR="#B22222"># Parse the query string into our parameters dictionary.
+</FONT></I> 4: self.parameters = cgi.parse_qs(
+ self._environ[<B><FONT COLOR="#BC8F8F">'QUERY_STRING'</FONT></B>], keep_blank_values=1)
+ <I><FONT COLOR="#B22222"># Since the parameter value is always a list (sigh), let's at least
+</FONT></I> <I><FONT COLOR="#B22222"># detect single values and store them.
+</FONT></I> 11: <B><FONT COLOR="#A020F0">for</FONT></B> name, value <B><FONT COLOR="#A020F0">in</FONT></B> self.parameters.items():
+ 7: <B><FONT COLOR="#A020F0">if</FONT></B> len(value) == 1:
+ 6: self.parameters[name] = value[0]
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">keys</FONT></B>(self):
+ <B><FONT COLOR="#BC8F8F">'See Interface.Common.Mapping.IEnumerableMapping'</FONT></B>
+ 7: d = {}
+ 7: d.update(self._environ)
+ 7: d.update(self.parameters)
+ 7: <B><FONT COLOR="#A020F0">return</FONT></B> d.keys()
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">get</FONT></B>(self, key, default=None):
+ <B><FONT COLOR="#BC8F8F">'See Interface.Common.Mapping.IReadMapping'</FONT></B>
+ 300: marker = object()
+ 300: result = self.parameters.get(key, marker)
+ 300: <B><FONT COLOR="#A020F0">if</FONT></B> result <B><FONT COLOR="#A020F0">is</FONT></B> <B><FONT COLOR="#A020F0">not</FONT></B> marker:
+ 8: <B><FONT COLOR="#A020F0">return</FONT></B> result
+
+ 292: <B><FONT COLOR="#A020F0">return</FONT></B> super(RESTRequest, self).get(key, default)
+
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> RESTView(object):
+ 1: zope.interface.implements(interfaces.IRESTView)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__init__</FONT></B>(self, context, request):
+ 39: self.context = context
+ 39: self.request = request
+
+ 1: @apply
+ <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__parent__</FONT></B>():
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">get</FONT></B>(self):
+ 2: <B><FONT COLOR="#A020F0">return</FONT></B> getattr(self, <B><FONT COLOR="#BC8F8F">'_parent'</FONT></B>, self.context)
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">set</FONT></B>(self, parent):
+ 1: self._parent = parent
+ 1: <B><FONT COLOR="#A020F0">return</FONT></B> property(get, set)
+
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> RESTPublicationRequestFactory(object):
+ 1: zope.interface.implements(IPublicationRequestFactory)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__init__</FONT></B>(self, db):
+ <B><FONT COLOR="#BC8F8F">"""See zope.app.publication.interfaces.IPublicationRequestFactory"""</FONT></B>
+ 2: self.publication = HTTPPublication(db)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__call__</FONT></B>(self, input_stream, env, output_stream=None):
+ <B><FONT COLOR="#BC8F8F">"""See zope.app.publication.interfaces.IPublicationRequestFactory"""</FONT></B>
+ 5: request = RESTRequest(input_stream, env)
+ 5: request.setPublication(self.publication)
+ 5: <B><FONT COLOR="#A020F0">return</FONT></B> request
+</pre>
+
+ <div class="footer">
+ Generated for revision exported on 2007-11-30 11:34:55.110922Z
+ </div>
+ </body>
+ </html>
Added: z3c.rest/trunk/coverage/report/z3c.rest.traverser.html
===================================================================
--- z3c.rest/trunk/coverage/report/z3c.rest.traverser.html (rev 0)
+++ z3c.rest/trunk/coverage/report/z3c.rest.traverser.html 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,76 @@
+
+ <html>
+ <head><title>Unit test coverage for z3c.rest.traverser</title>
+ <style type="text/css">
+ a {text-decoration: none; display: block; padding-right: 1em;}
+ a:hover {background: #EFA;}
+ hr {height: 1px; border: none; border-top: 1px solid gray;}
+ .notcovered {background: #FCC;}
+ .footer {margin: 2em; font-size: small; color: gray;}
+ </style>
+ </head>
+ <body><h1>Unit test coverage for z3c.rest.traverser</h1>
+ <table>
+
+<tr><td><a href="z3c.html">z3c/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.html"> rest/</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 282 uncovered)</td></tr>
+<tr><td><a href="z3c.rest.traverser.html"> traverser.py</a></td> <td style="background: green"> </td> <td>covered 100% (0 of 23 uncovered)</td></tr>
+</table><hr/>
+<pre>
+ <I><FONT COLOR="#B22222">##############################################################################
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222"># Copyright (c) 2007 Zope Corporation and Contributors.
+</FONT></I> <I><FONT COLOR="#B22222"># All Rights Reserved.
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222"># This software is subject to the provisions of the Zope Public License,
+</FONT></I> <I><FONT COLOR="#B22222"># Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+</FONT></I> <I><FONT COLOR="#B22222"># THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+</FONT></I> <I><FONT COLOR="#B22222"># WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+</FONT></I> <I><FONT COLOR="#B22222"># WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+</FONT></I> <I><FONT COLOR="#B22222"># FOR A PARTICULAR PURPOSE.
+</FONT></I> <I><FONT COLOR="#B22222">#
+</FONT></I> <I><FONT COLOR="#B22222">##############################################################################
+</FONT></I> <B><FONT COLOR="#BC8F8F">"""Some basic traversers for REST
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """</FONT></B>
+ 1: <B><FONT COLOR="#A020F0">import</FONT></B> zope.interface
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.publisher.interfaces.http <B><FONT COLOR="#A020F0">import</FONT></B> IHTTPPublisher
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.app.container.interfaces <B><FONT COLOR="#A020F0">import</FONT></B> IItemContainer
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> zope.publisher.interfaces <B><FONT COLOR="#A020F0">import</FONT></B> NotFound
+ 1: <B><FONT COLOR="#A020F0">from</FONT></B> z3c.rest <B><FONT COLOR="#A020F0">import</FONT></B> interfaces, null
+
+ 2: <B><FONT COLOR="#A020F0">class</FONT></B> ItemTraverser(object):
+ 1: zope.interface.implements(IHTTPPublisher)
+ 1: zope.component.adapts(IItemContainer, interfaces.IRESTRequest)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">__init__</FONT></B>(self, container, request):
+ 20: self.context = container
+ 20: self.request = request
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">publishTraverse</FONT></B>(self, request, name):
+ 23: <B><FONT COLOR="#A020F0">try</FONT></B>:
+ 23: <B><FONT COLOR="#A020F0">return</FONT></B> self.context[name]
+ 12: <B><FONT COLOR="#A020F0">except</FONT></B> KeyError:
+ 12: <B><FONT COLOR="#A020F0">return</FONT></B> self.nullResource(request, name)
+
+ 1: <B><FONT COLOR="#A020F0">def</FONT></B> <B><FONT COLOR="#0000FF">nullResource</FONT></B>(self, request, name):
+ <I><FONT COLOR="#B22222"># we traversed to something that doesn't exist.
+</FONT></I>
+ <I><FONT COLOR="#B22222"># The name must be the last name in the path, so the traversal
+</FONT></I> <I><FONT COLOR="#B22222"># name stack better be empty:
+</FONT></I> 12: <B><FONT COLOR="#A020F0">if</FONT></B> request.getTraversalStack():
+ 2: <B><FONT COLOR="#A020F0">raise</FONT></B> NotFound(self.context, name, request)
+
+ <I><FONT COLOR="#B22222"># This should only happen for a PUT:
+</FONT></I> 10: <B><FONT COLOR="#A020F0">if</FONT></B> request.method != <B><FONT COLOR="#BC8F8F">'PUT'</FONT></B>:
+ 5: <B><FONT COLOR="#A020F0">raise</FONT></B> NotFound(self.context, name, request)
+
+ 5: <B><FONT COLOR="#A020F0">return</FONT></B> null.NullResource(self.context, name)
+</pre>
+
+ <div class="footer">
+ Generated for revision exported on 2007-11-30 11:34:55.110922Z
+ </div>
+ </body>
+ </html>
Added: z3c.rest/trunk/coverage/z3c.rest.__init__.cover
===================================================================
--- z3c.rest/trunk/coverage/z3c.rest.__init__.cover (rev 0)
+++ z3c.rest/trunk/coverage/z3c.rest.__init__.cover 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1 @@
+ # Make a package
Added: z3c.rest/trunk/coverage/z3c.rest.client.cover
===================================================================
--- z3c.rest/trunk/coverage/z3c.rest.client.cover (rev 0)
+++ z3c.rest/trunk/coverage/z3c.rest.client.cover 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,201 @@
+ ##############################################################################
+ #
+ # 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 Client
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """
+ 1: import lxml
+ 1: import httplib
+ 1: import socket
+ 1: import urllib
+ 1: import urlparse
+ 1: import base64
+ 1: import zope.interface
+ 1: from z3c.rest import interfaces
+
+ 1: def isRelativeURL(url):
+ """Determines whether the given URL is a relative path segment."""
+ 37: pieces = urlparse.urlparse(url)
+ 37: if not pieces[0] and not pieces[1]:
+ 5: return True
+ 32: return False
+
+ 1: def absoluteURL(base, url):
+ """Convertes a URL to an absolute URL given a base."""
+ 37: if isRelativeURL(url):
+ 5: fullUrl = urlparse.urljoin(base, url)
+ else:
+ 32: fullUrl = url
+ 37: pieces = list(urlparse.urlparse(fullUrl))
+ 37: if not pieces[2].endswith('/'):
+ 18: pieces[2] += '/'
+ 37: return urlparse.urlunparse(pieces)
+
+ 1: def getFullPath(pieces, params):
+ """Build a full httplib request path, including a query string."""
+ 32: query = ''
+ 32: if pieces[4]:
+ 2: query = pieces[4]
+ 32: if params:
+ 2: encParams = urllib.urlencode(params)
+ 2: if query:
+ 1: query += '&' + encParams
+ else:
+ 1: query = encParams
+ 32: return urlparse.urlunparse(
+ ('', '', pieces[2], pieces[3], query, pieces[5]))
+
+
+ 2: class XLink(object):
+ """A link implementation for simple XLinks."""
+ 1: zope.interface.implements(interfaces.ILink)
+
+ 1: def __init__(self, client, title, url):
+ 5: self._client = client
+ 5: self.title = title
+ 5: self.url = url
+
+ 1: def click(self):
+ """See interfaces.ILink"""
+ 3: self._client.get(self.url)
+
+ 1: def __repr__(self):
+ 2: return '<%s title=%r url=%r>' %(
+ self.__class__.__name__, self.title, self.url)
+
+
+ 2: class RESTClient(object):
+ 1: zope.interface.implements(interfaces.IRESTClient)
+
+ 1: connectionFactory = httplib.HTTPConnection
+
+ 1: def __init__(self, url=None):
+ 3: self.requestHeaders = {}
+ 3: self._reset()
+ 3: self._history = []
+ 3: self._requestData = None
+ 3: self.url = ''
+ 3: if url:
+ 1: self.open(url)
+
+ 1: @property
+ def fullStatus(self):
+ 12: return '%i %s' %(self.status, self.reason)
+
+ 1: def _reset(self):
+ 35: self.headers = []
+ 35: self.contents = {}
+ 35: self.status = None
+ 35: self.reason = None
+
+ 1: def open(self, url='', data=None, params=None, headers=None, method='GET'):
+ # Create a correct absolute URL and set it.
+ 32: self.url = absoluteURL(self.url, url)
+
+ # Create the full set of request headers
+ 32: requestHeaders = self.requestHeaders.copy()
+ 32: if headers:
+ 1: requestHeaders.update(headers)
+
+ # Let's now reset all response values
+ 32: self._reset()
+
+ # Store all the request data
+ 32: self._requestData = (url, data, params, headers, method)
+
+ # Make a connection and retrieve the result
+ 32: pieces = urlparse.urlparse(self.url)
+ 32: connection = self.connectionFactory(pieces[1])
+ 32: try:
+ 32: connection.request(
+ method, getFullPath(pieces, params), data, requestHeaders)
+ 30: response = connection.getresponse()
+ 30: connection.close()
+ 2: except socket.error, e:
+ 1: connection.close()
+ 1: self.status, self.reason = e.args
+ 1: self._addHistory()
+ 1: raise e
+ else:
+ 30: self.headers = response.getheaders()
+ 30: self.contents = response.read()
+ 30: self.status = response.status
+ 30: self.reason = response.reason
+ 30: self._addHistory()
+
+ 1: def get(self, url='', params=None, headers=None):
+ 17: self.open(url, None, params, headers)
+
+ 1: def put(self, url='', data='', params=None, headers=None):
+ 6: self.open(url, data, params, headers, 'PUT')
+
+ 1: def post(self, url='', data='', params=None, headers=None):
+ 1: self.open(url, data, params, headers, 'POST')
+
+ 1: def delete(self, url='', params=None, headers=None):
+ 4: self.open(url, None, params, headers, 'DELETE')
+
+ 1: def setCredentials(self, username, password):
+ 1: creds = username + u':' + password
+ 1: creds = "Basic " + base64.encodestring(creds.encode('utf-8')).strip()
+ 1: self.requestHeaders['Authorization'] = creds
+
+ 1: def _addHistory(self):
+ 31: self._history.append((
+ self.url, self.requestHeaders, self.headers, self.contents,
+ self.status, self.reason, self._requestData
+ ))
+
+ 1: def goBack(self, count=1):
+ # The user really does not want to go back.
+ 4: if count == 0:
+ 1: return
+ # The user wants to reach before a pre-historical state.
+ 3: if len(self._history) < count:
+ 1: raise ValueError('There is not enough history.')
+ # Let's now get the entry and set the history back to that state.
+ 2: entry = self._history[-(count+1)]
+ 2: self._history = self._history[:-count]
+ # Reset the state.
+ 2: (self.url, self.requestHeaders, self.headers, self.contents,
+ self.status, self.reason, self._requestData) = entry
+
+ 1: def reload(self):
+ 1: self.open(*self._requestData)
+
+ 1: def getLink(self, title=None, url=None, index=0):
+ 6: nsmap = {'xlink': "http://www.w3.org/1999/xlink"}
+ 6: tree = lxml.etree.fromstring(self.contents)
+ 6: res = []
+ 6: if title is not None:
+ 4: res = tree.xpath(
+ '//*[@xlink:title="%s"]' %title, nsmap)
+ 2: elif url is not None:
+ 1: res = tree.xpath(
+ '//*[@xlink:href="%s"]' %url, nsmap)
+ else:
+ 1: raise ValueError('You must specify a title or URL.')
+ 5: elem = res[index]
+ 5: url = elem.attrib.get('{%(xlink)s}href' %nsmap, '')
+ 5: return XLink(self,
+ 5: elem.attrib.get('{%(xlink)s}title' %nsmap),
+ 5: absoluteURL(self.url, url))
+
+ 1: def xpath(self, expr, nsmap=None, single=False):
+ 3: res = lxml.etree.fromstring(self.contents).xpath(expr, nsmap)
+ 3: if not single:
+ 1: return res
+ 2: if len(res) != 1:
+ 1: raise ValueError('XPath expression returned more than one result.')
+ 1: return res[0]
Added: z3c.rest/trunk/coverage/z3c.rest.interfaces.cover
===================================================================
--- z3c.rest/trunk/coverage/z3c.rest.interfaces.cover (rev 0)
+++ z3c.rest/trunk/coverage/z3c.rest.interfaces.cover 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,214 @@
+ ##############################################################################
+ #
+ # 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 publishing interfaces.
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """
+ 1: import zope.interface
+ 1: import zope.schema
+ 1: from zope.location.interfaces import ILocation
+ 1: from zope.publisher.interfaces import http
+
+ 2: class IRESTRequest(http.IHTTPRequest):
+ 1: """A special type of request for handling REST-based requests."""
+
+
+ 2: class IRESTView(ILocation):
+ 1: """A REST view"""
+
+
+ 2: class ILink(zope.interface.Interface):
+ """An object representing a hyperlink."""
+
+ 1: href = zope.schema.TextLine(
+ title=u"URL",
+ 1: description=u"The normalized URL of the link",
+ 1: required=False)
+
+ 1: title = zope.schema.TextLine(
+ title=u'Title',
+ 1: description=u'The title of the link',
+ 1: required=False)
+
+ 1: def click():
+ """click the link, going to the URL referenced"""
+
+
+ 2: class IRESTClient(zope.interface.Interface):
+ """A REST client.
+
+ This client provides a high-level API to access RESTful Web APIs. The
+ interface is designed to resemble the test browser API as much as
+ practical.
+ """
+
+ 1: connectionFactory = zope.schema.Field(
+ title=u"Connection Facotry",
+ 1: description=(u'A callable that creates an `httplib`-compliant '
+ u'connection object.'),
+ 1: required=True)
+
+ 1: requestHeaders = zope.schema.Dict(
+ title=u"Request Headers",
+ 1: description=(u"A set of headers that will be sent in every request."),
+ 1: required=True)
+
+ 1: url = zope.schema.URI(
+ title=u"URL",
+ 1: description=(u"The URL the browser is currently showing. It is "
+ u"always a full, absolute URL."),
+ 1: required=True)
+
+ 1: headers = zope.schema.List(
+ title=u"Response Headers",
+ 1: description=(u'A list of all headers that have been returned '
+ u'in the last request.'),
+ 1: required=True)
+
+ 1: contents = zope.schema.Text(
+ title=u"Contents",
+ 1: description=u"The complete response body of the HTTP request.",
+ 1: required=True)
+
+ 1: status = zope.schema.Int(
+ title=u"Status",
+ 1: description=u"The status code of the last response.",
+ 1: min=0,
+ 1: required=True)
+
+ 1: reason = zope.schema.TextLine(
+ title=u"Reason",
+ 1: description=u"A short explanation of the status of the last response.",
+ 1: required=True)
+
+ 1: fullStatus = zope.schema.TextLine(
+ title=u"Full Status",
+ 1: description=u"The status code and reason of the last response.",
+ 1: required=True)
+
+ 1: def open(url='', data=None, params=None, headers=None, method='GET'):
+ """Open a URL and retrieve the result.
+
+ The `url` argument can either be a full URL or a URL relative to the
+ previous one. If no URL is specified, then the previous URL will be
+ used.
+
+ The `data` is the contents of the request body. It is used to send
+ information to the server.
+
+ The `params` describe additional query parameters that will be added
+ to the request. Query string parameters are frequently used by RESTive
+ APIs to provide additional return value options.
+
+ The `headers` specify additional request headers that are specific for
+ this particular request.
+
+ The `method` specifies the HTTP method or verb to use to access the
+ resource on the server. While there are only a few methods in RFC
+ 2616, an string is allowed, since any particular API can extend the
+ set of allowed methods.
+ """
+
+ 1: def get(url='', params=None, headers=None):
+ """Make a GET request to the server.
+
+ For argument details see ``open()``.
+ """
+
+ 1: def put(url='', data='', params=None, headers=None):
+ """Make a PUT request to the server.
+
+ For argument details see ``open()``.
+ """
+
+ 1: def post(url='', data='', params=None, headers=None):
+ """Make a POST request to the server.
+
+ For argument details see ``open()``.
+ """
+
+ 1: def delete(url='', params=None, headers=None):
+ """Make a DELETE request to the server.
+
+ For argument details see ``open()``.
+ """
+
+ 1: def setCredentials(username, password):
+ """Set the credentials.
+
+ This method adds the necessary information to authenticate the user. A
+ common example is basic auth, which inserts the `Authentication`
+ request header.
+ """
+
+ 1: def goBack(count=1):
+ """Go back in history by a certain amount of visisted pages.
+
+ The ``count`` argument specifies how far to go back. It is set to 1 by
+ default.
+ """
+
+ 1: def reload():
+ """Reload the current resource.
+
+ All arguments, including the HTTP method, parameters and additional
+ headers are honored.
+ """
+
+ 1: def getLink(title=None, url=None, index=0):
+ """Return an ILink of the found link.
+
+ This method assumes that the current content type of the response body
+ is XML.
+
+ The link is found by the arguments of the method. Only one can be
+ used at a time:
+
+ o ``title`` -- The title or a sub-string thereof of the link.
+
+ o ``url`` -- The URL the link is going to.
+
+ If multiple matching links are found, the `index` specifies which one
+ to use.
+ """
+
+ 1: def xpath(expr, nsmap=None, single=False):
+ """Returns the result of an XPath search expression.
+
+ This method assumes that the current content type of the response body
+ is XML.
+
+ The `expr` argument is the actual XPath expression. If the expression
+ is incorrect, an unspecified error must be raised.
+
+ The `nsmap` is a mapping from the short version to the full URL of
+ each XML namespace used in the expression.
+
+ If `single` is set to ``True``, then only one result is returned,
+ instead of a list. If the XPath expression results in more than one
+ result, a ``ValueError`` must be raised.
+ """
+
+
+ 2: class IPublisherRESTClient(IRESTClient):
+ """An extension to the REST client to support test-specific features."""
+
+ 1: handleErrors = zope.schema.Bool(
+ title=u"Handle Errors",
+ 1: description=(u"Describes whether server-side errors will be handled "
+ u"by the publisher. If set to ``False``, the error will "
+ u"progress all the way to the test, which is good for "
+ u"debugging."),
+ 1: default=True,
+ 1: required=True)
Added: z3c.rest/trunk/coverage/z3c.rest.null.cover
===================================================================
--- z3c.rest/trunk/coverage/z3c.rest.null.cover (rev 0)
+++ z3c.rest/trunk/coverage/z3c.rest.null.cover 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,54 @@
+ ##############################################################################
+ #
+ # 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.
+ #
+ ##############################################################################
+ """Null resource.
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """
+ 1: import zope.component
+ 1: import zope.interface
+ 1: from zope.app.http.interfaces import INullResource
+ 1: from zope.location import location
+ 1: from zope.traversing.browser import absoluteURL
+
+ 1: from z3c.rest import rest
+
+ 2: class NullResource(location.Location):
+ """Object representing objects to be created by a `PUT`."""
+ 1: zope.interface.implements(INullResource)
+
+ 1: def __init__(self, container, name):
+ 6: self.__parent__ = self.container = container
+ 6: self.__name__ = self.name = name
+
+ 1: def __repr__(self):
+ 1: return '<%s %r>' %(self.__class__.__name__, self.name)
+
+
+ 2: class NullPUT(rest.RESTView):
+ """Put handler for null resources"""
+
+ 1: def PUT(self):
+ 6: nullPut = zope.component.queryMultiAdapter(
+ (self.context.container, self.request), name='NullPUT')
+ 6: if nullPut is None:
+ # See RFC 2616, section 9.6
+ 1: self.request.response.setStatus(501)
+ 1: return
+
+ 5: newObj = nullPut.NullPUT(self.context)
+
+ # See RFC 2616, section 9.6
+ 4: self.request.response.setStatus(201)
+ 4: self.request.response.setHeader(
+ 'Location', absoluteURL(newObj, self.request))
Added: z3c.rest/trunk/coverage/z3c.rest.rest.cover
===================================================================
--- z3c.rest/trunk/coverage/z3c.rest.rest.cover (rev 0)
+++ z3c.rest/trunk/coverage/z3c.rest.rest.cover 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,94 @@
+ ##############################################################################
+ #
+ # 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: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """
+ 1: import cgi
+ 1: import zope.interface
+ 1: from zope.app.publication.interfaces import IPublicationRequestFactory
+ 1: from zope.app.publication.http import HTTPPublication
+ 1: from zope.publisher.http import HTTPRequest
+
+ 1: from z3c.rest import interfaces
+
+ 2: class RESTRequest(HTTPRequest):
+ 1: zope.interface.implements(interfaces.IRESTRequest)
+
+ 1: __slots__ = (
+ 'parameters', # Parameters sent via the query string.
+ )
+
+ 1: def __init__(self, body_instream, environ, response=None):
+ 39: self.parameters = {}
+ 39: super(RESTRequest, self).__init__(body_instream, environ, response)
+
+ 1: def processInputs(self):
+ 'See IPublisherRequest'
+ 35: if 'QUERY_STRING' not in self._environ:
+ 31: return
+ # Parse the query string into our parameters dictionary.
+ 4: self.parameters = cgi.parse_qs(
+ self._environ['QUERY_STRING'], keep_blank_values=1)
+ # Since the parameter value is always a list (sigh), let's at least
+ # detect single values and store them.
+ 11: for name, value in self.parameters.items():
+ 7: if len(value) == 1:
+ 6: self.parameters[name] = value[0]
+
+ 1: def keys(self):
+ 'See Interface.Common.Mapping.IEnumerableMapping'
+ 7: d = {}
+ 7: d.update(self._environ)
+ 7: d.update(self.parameters)
+ 7: return d.keys()
+
+ 1: def get(self, key, default=None):
+ 'See Interface.Common.Mapping.IReadMapping'
+ 300: marker = object()
+ 300: result = self.parameters.get(key, marker)
+ 300: if result is not marker:
+ 8: return result
+
+ 292: return super(RESTRequest, self).get(key, default)
+
+
+ 2: class RESTView(object):
+ 1: zope.interface.implements(interfaces.IRESTView)
+
+ 1: def __init__(self, context, request):
+ 39: self.context = context
+ 39: self.request = request
+
+ 1: @apply
+ def __parent__():
+ 1: def get(self):
+ 2: return getattr(self, '_parent', self.context)
+ 1: def set(self, parent):
+ 1: self._parent = parent
+ 1: return property(get, set)
+
+
+ 2: class RESTPublicationRequestFactory(object):
+ 1: zope.interface.implements(IPublicationRequestFactory)
+
+ 1: def __init__(self, db):
+ """See zope.app.publication.interfaces.IPublicationRequestFactory"""
+ 2: self.publication = HTTPPublication(db)
+
+ 1: def __call__(self, input_stream, env, output_stream=None):
+ """See zope.app.publication.interfaces.IPublicationRequestFactory"""
+ 5: request = RESTRequest(input_stream, env)
+ 5: request.setPublication(self.publication)
+ 5: return request
Added: z3c.rest/trunk/coverage/z3c.rest.testing.cover
===================================================================
--- z3c.rest/trunk/coverage/z3c.rest.testing.cover (rev 0)
+++ z3c.rest/trunk/coverage/z3c.rest.testing.cover 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,100 @@
+ ##############################################################################
+ #
+ # 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 testing support.
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """
+ 1: import sys
+ 1: import zope.interface
+ 1: from z3c.rest import client, rest, interfaces
+ 1: from zope.app.publication.http import HTTPPublication
+ 1: from zope.app.testing.functional import HTTPCaller
+
+
+ 2: class RESTCaller(HTTPCaller):
+ """An HTTP caller for REST functional page tests"""
+
+ 1: def chooseRequestClass(self, method, path, environment):
+ """Always returns HTTPRequests regardless of methods and content"""
+ 31: return rest.RESTRequest, HTTPPublication
+
+
+ 2: class PublisherConnection(object):
+
+ 1: def __init__(self, server, port=None):
+ 31: self._response = None
+ 31: self.server = server
+ 31: self.port = port
+
+ 1: def request(self, method, path, body, headers):
+ # Extract the handle_error option header
+ 31: if sys.version_info >= (2,5):
+>>>>>> handleErrorsKey = 'X-Zope-Handle-Errors'
+ else:
+ 31: handleErrorsKey = 'X-zope-handle-errors'
+ 31: handleErrors = headers.get(handleErrorsKey, True)
+ 31: if handleErrorsKey in headers:
+ 27: del headers[handleErrorsKey]
+
+ # Construct the request body and call the publisher
+ 31: body = body or ''
+ 31: request = ["%s %s HTTP/1.1" % (method, path)]
+ 54: for hdr, value in headers.items():
+ 23: request.append("%s: %s" % (hdr, value))
+ 31: request_string = "\n".join(request) + "\n\n" + body
+ 31: self._response = RESTCaller()(
+ request_string, handle_errors=handleErrors)
+
+ 1: def getresponse(self):
+ 30: return PublisherResponse(self._response)
+
+ 1: def close(self):
+ 30: self._response = None
+
+
+ 2: class PublisherResponse(object):
+ """Adapter of Zope 3 response objects into httplib.HTTPResponse."""
+
+ 1: def __init__(self, response):
+ 30: self._response = response
+ 30: self.status = response.getStatus()
+ 30: self.reason = response._reason
+
+ 1: def getheaders(self):
+ 30: return self._response.getHeaders()
+
+ 1: def read(self):
+ 30: return self._response.consumeBody()
+
+
+ 2: class RESTClient(client.RESTClient):
+ 1: zope.interface.implements(interfaces.IPublisherRESTClient)
+
+ 1: connectionFactory = PublisherConnection
+
+ 1: @apply
+ def handleErrors():
+ """See zope.testbrowser.interfaces.IBrowser"""
+ 1: headerKey = 'X-zope-handle-errors'
+
+ 1: def get(self):
+ 2: return self.requestHeaders.get(headerKey, True)
+
+ 1: def set(self, value):
+ 2: current_value = get(self)
+ 2: if current_value == value:
+>>>>>> return
+ 2: self.requestHeaders[headerKey] = value
+
+ 1: return property(get, set)
Added: z3c.rest/trunk/coverage/z3c.rest.tests.__init__.cover
===================================================================
--- z3c.rest/trunk/coverage/z3c.rest.tests.__init__.cover (rev 0)
+++ z3c.rest/trunk/coverage/z3c.rest.tests.__init__.cover 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1 @@
+ # Make a package
Added: z3c.rest/trunk/coverage/z3c.rest.tests.folder.cover
===================================================================
--- z3c.rest/trunk/coverage/z3c.rest.tests.folder.cover (rev 0)
+++ z3c.rest/trunk/coverage/z3c.rest.tests.folder.cover 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,57 @@
+ ##############################################################################
+ #
+ # 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.
+ #
+ ##############################################################################
+ """Test REST view for folders.
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """
+ 1: import lxml.etree
+ 1: from z3c.rest import rest
+ 1: from zope.app.folder import folder
+ 1: from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+ 1: from zope.app.publication.http import MethodNotAllowed
+ 1: from zope.dublincore.interfaces import IZopeDublinCore
+
+ 2: class FolderAPI(rest.RESTView):
+ """A simple REST view for folders."""
+
+ 1: template = ViewPageTemplateFile("folder.pt")
+
+ 1: def __init__(self, context, request):
+ 31: super(FolderAPI, self).__init__(context, request)
+ 93: for param in ('noitems', 'notitle'):
+ 62: if 'HTTP_DEMO_' + param.upper() in self.request:
+ 3: self.request.parameters[param] = 1
+
+ 1: def GET(self):
+ 17: return self.template()
+
+ 1: def POST(self):
+ 2: tree = lxml.etree.parse(self.request.bodyStream)
+ 2: title = tree.find('title').text
+ 2: dc = IZopeDublinCore(self.context)
+ 2: dc.title = unicode(title)
+
+ # For existing resources, PUT pretty much behaves like POST
+ 1: PUT = POST
+
+ 1: def NullPUT(self, nullResource):
+ 4: name = nullResource.name
+ 4: self.context[name] = folder.Folder()
+ 3: return self.context[name]
+
+ 1: def DELETE(self):
+ 3: container = self.context.__parent__
+ 3: if container is None:
+ 1: raise MethodNotAllowed(self.context, self.request)
+ 2: del container[self.context.__name__]
Added: z3c.rest/trunk/coverage/z3c.rest.tests.test.cover
===================================================================
--- z3c.rest/trunk/coverage/z3c.rest.tests.test.cover (rev 0)
+++ z3c.rest/trunk/coverage/z3c.rest.tests.test.cover 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,79 @@
+ ##############################################################################
+ #
+ # Copyright (c) 2004 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 Tests
+
+ $Id$
+ 1: """
+ 1: __docformat__ = "reStructuredText"
+ 1: import os
+ 1: import unittest
+ 1: import zope.component
+ 1: from zope.testing import doctest, doctestunit
+ 1: from zope.traversing.browser import absoluteurl
+ 1: from zope.traversing.interfaces import IContainmentRoot
+ 1: from zope.app.testing import functional, placelesssetup
+ 1: from z3c.rest import interfaces
+
+ 1: RESTLayer = functional.ZCMLLayer(
+ os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'),
+ 1: __name__, 'RESTLayer', allow_teardown=True)
+
+ 1: def setUp(test):
+ 1: placelesssetup.setUp(test)
+
+ # XXX: This really needs a REST equivalent w/o breadcrumbs.
+
+ 1: zope.component.provideAdapter(
+ absoluteurl.AbsoluteURL,
+ 1: (None, interfaces.IRESTRequest),
+ 1: absoluteurl.IAbsoluteURL)
+
+ 1: zope.component.provideAdapter(
+ absoluteurl.SiteAbsoluteURL,
+ 1: (IContainmentRoot, interfaces.IRESTRequest),
+ 1: absoluteurl.IAbsoluteURL,
+ 1: 'absolute_url')
+
+ 1: zope.component.provideAdapter(
+ absoluteurl.SiteAbsoluteURL,
+ 1: (IContainmentRoot, interfaces.IRESTRequest),
+ 1: absoluteurl.IAbsoluteURL)
+
+ 1: zope.component.provideAdapter(
+ absoluteurl.AbsoluteURL,
+ 1: (None, interfaces.IRESTRequest),
+ 1: absoluteurl.IAbsoluteURL,
+ 1: 'absolute_url')
+
+
+ 1: def test_suite():
+ 1: client = functional.FunctionalDocFileSuite('../client.txt')
+ 1: client.layer = RESTLayer
+ 1: return unittest.TestSuite((
+ client,
+ doctest.DocFileSuite(
+ '../rest.txt',
+ 1: globs={'pprint': doctestunit.pprint},
+ 1: optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ doctest.DocFileSuite(
+ '../null.txt',
+ 1: setUp=setUp, tearDown=placelesssetup.tearDown,
+ 1: optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ doctest.DocFileSuite(
+ '../traverser.txt',
+ 1: optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ ))
Added: z3c.rest/trunk/coverage/z3c.rest.traverser.cover
===================================================================
--- z3c.rest/trunk/coverage/z3c.rest.traverser.cover (rev 0)
+++ z3c.rest/trunk/coverage/z3c.rest.traverser.cover 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,50 @@
+ ##############################################################################
+ #
+ # 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.
+ #
+ ##############################################################################
+ """Some basic traversers for REST
+
+ $Id: http.py 72310 2007-02-01 21:39:01Z mkerrin $
+ 1: """
+ 1: import zope.interface
+ 1: from zope.publisher.interfaces.http import IHTTPPublisher
+ 1: from zope.app.container.interfaces import IItemContainer
+ 1: from zope.publisher.interfaces import NotFound
+ 1: from z3c.rest import interfaces, null
+
+ 2: class ItemTraverser(object):
+ 1: zope.interface.implements(IHTTPPublisher)
+ 1: zope.component.adapts(IItemContainer, interfaces.IRESTRequest)
+
+ 1: def __init__(self, container, request):
+ 20: self.context = container
+ 20: self.request = request
+
+ 1: def publishTraverse(self, request, name):
+ 23: try:
+ 23: return self.context[name]
+ 12: except KeyError:
+ 12: return self.nullResource(request, name)
+
+ 1: def nullResource(self, request, name):
+ # we traversed to something that doesn't exist.
+
+ # The name must be the last name in the path, so the traversal
+ # name stack better be empty:
+ 12: if request.getTraversalStack():
+ 2: raise NotFound(self.context, name, request)
+
+ # This should only happen for a PUT:
+ 10: if request.method != 'PUT':
+ 5: raise NotFound(self.context, name, request)
+
+ 5: return null.NullResource(self.context, name)
Added: z3c.rest/trunk/develop-eggs/z3c.rest.egg-link
===================================================================
--- z3c.rest/trunk/develop-eggs/z3c.rest.egg-link (rev 0)
+++ z3c.rest/trunk/develop-eggs/z3c.rest.egg-link 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,2 @@
+/opt/zope/packages/z3c.rest/src
+../
\ No newline at end of file
Added: z3c.rest/trunk/parts/database/Data.fs
===================================================================
(Binary files differ)
Property changes on: z3c.rest/trunk/parts/database/Data.fs
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: z3c.rest/trunk/parts/database/Data.fs.index
===================================================================
(Binary files differ)
Property changes on: z3c.rest/trunk/parts/database/Data.fs.index
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: z3c.rest/trunk/parts/database/Data.fs.lock
===================================================================
--- z3c.rest/trunk/parts/database/Data.fs.lock (rev 0)
+++ z3c.rest/trunk/parts/database/Data.fs.lock 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1 @@
+ 11734
Added: z3c.rest/trunk/parts/demo/zdaemon.conf
===================================================================
--- z3c.rest/trunk/parts/demo/zdaemon.conf (rev 0)
+++ z3c.rest/trunk/parts/demo/zdaemon.conf 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,13 @@
+<runner>
+ daemon on
+ directory /opt/zope/packages/z3c.rest/parts/demo
+ program /opt/zope/packages/z3c.rest/parts/form-demo-app/runzope -C /opt/zope/packages/z3c.rest/parts/demo/zope.conf
+ socket-name /opt/zope/packages/z3c.rest/parts/demo/zdaemon.sock
+ transcript /opt/zope/packages/z3c.rest/parts/demo/z3.log
+</runner>
+
+<eventlog>
+ <logfile>
+ path /opt/zope/packages/z3c.rest/parts/demo/z3.log
+ </logfile>
+</eventlog>
Added: z3c.rest/trunk/parts/demo/zope.conf
===================================================================
--- z3c.rest/trunk/parts/demo/zope.conf (rev 0)
+++ z3c.rest/trunk/parts/demo/zope.conf 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,30 @@
+site-definition /opt/zope/packages/z3c.rest/parts/form-demo-app/site.zcml
+
+<zodb>
+ <filestorage>
+ path /opt/zope/packages/z3c.rest/parts/database/Data.fs
+ </filestorage>
+</zodb>
+
+<server>
+ address 8080
+ type HTTP
+</server>
+
+<server>
+ address 8081
+ type REST-HTTP
+</server>
+
+<accesslog>
+ <logfile>
+ path /opt/zope/packages/z3c.rest/parts/demo/access.log
+ </logfile>
+</accesslog>
+
+<eventlog>
+ <logfile>
+ formatter zope.exceptions.log.Formatter
+ path STDOUT
+ </logfile>
+</eventlog>
Added: z3c.rest/trunk/parts/form-demo-app/debugzope
===================================================================
--- z3c.rest/trunk/parts/form-demo-app/debugzope (rev 0)
+++ z3c.rest/trunk/parts/form-demo-app/debugzope 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,101 @@
+#!/usr/bin/py24
+
+import sys
+sys.path[0:0] = [
+ '/opt/zope/packages/z3c.rest/src',
+ '/opt/zope/packages/eggs/zope.contentprovider-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/setuptools-0.6c7-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.zcmlfiles-3.4.3-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.wsgi-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.twisted-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.securitypolicy-3.4.6-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.security-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.publication-3.4.2-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.publisher-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.form-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.error-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.container-3.5.3-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.app.component-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.authentication-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.appsetup-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.publisher-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/lxml-1.3.6-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.tales-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.schema-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.location-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.interface-3.4.1-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.event-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.component-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.schema-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.pagetemplate-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.i18n-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.formlib-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.rotterdam-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.basicskin-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.principalannotation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.zopeappgenerations-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.locales-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.i18n-3.4.4-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.interface-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.generations-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.content-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.dependable-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.annotation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.modulealias-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.security-3.4.0-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/ZConfig-2.5-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.zapi-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.server-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.applicationcontrol-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.exceptions-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.copypastemove-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zdaemon-2.0.1-py2.4.egg',
+ '/opt/zope/packages/eggs/ZODB3-3.8.0b4-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.securitypolicy-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.i18nmessageid-3.4.3-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.configuration-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.deprecation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.deferredimport-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.exception-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.http-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.error-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.traversing-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.pagetemplate-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.datetime-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.contenttype-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.proxy-3.4.0-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.cachedescriptors-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.broken-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.dublincore-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.size-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.filerepresentation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.lifecycleevent-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.dottedname-3.4.2-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.thread-3.4-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.session-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.folder-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.testing-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.testing-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.tal-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/pytz-2007g-py2.4.egg',
+ '/opt/zope/packages/eggs/zodbcode-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.renderer-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.server-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.hookable-3.4.0-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.session-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.minmax-1.1.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.debug-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/RestrictedPython-3.4.2-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.structuredtext-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/docutils-0.4-py2.4.egg',
+ '/opt/zope/packages/z3c.rest/./src',
+ '/opt/zope/packages/eggs/zc.zope3recipes-0.6.0-py2.4.egg',
+ ]
+
+import zope.app.twisted.main
+
+
+import zc.zope3recipes.debugzope
+
+if __name__ == '__main__':
+ zc.zope3recipes.debugzope.debug(main_module=zope.app.twisted.main)
Property changes on: z3c.rest/trunk/parts/form-demo-app/debugzope
___________________________________________________________________
Name: svn:executable
+
Added: z3c.rest/trunk/parts/form-demo-app/runzope
===================================================================
--- z3c.rest/trunk/parts/form-demo-app/runzope (rev 0)
+++ z3c.rest/trunk/parts/form-demo-app/runzope 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,97 @@
+#!/usr/bin/py24
+
+import sys
+sys.path[0:0] = [
+ '/opt/zope/packages/z3c.rest/src',
+ '/opt/zope/packages/eggs/zope.contentprovider-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/setuptools-0.6c7-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.zcmlfiles-3.4.3-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.wsgi-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.twisted-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.securitypolicy-3.4.6-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.security-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.publication-3.4.2-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.publisher-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.form-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.error-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.container-3.5.3-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.app.component-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.authentication-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.appsetup-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.publisher-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/lxml-1.3.6-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.tales-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.schema-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.location-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.interface-3.4.1-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.event-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.component-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.schema-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.pagetemplate-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.i18n-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.formlib-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.rotterdam-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.basicskin-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.principalannotation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.zopeappgenerations-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.locales-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.i18n-3.4.4-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.interface-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.generations-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.content-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.dependable-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.annotation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.modulealias-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.security-3.4.0-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/ZConfig-2.5-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.zapi-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.server-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.applicationcontrol-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.exceptions-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.copypastemove-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zdaemon-2.0.1-py2.4.egg',
+ '/opt/zope/packages/eggs/ZODB3-3.8.0b4-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.securitypolicy-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.i18nmessageid-3.4.3-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.configuration-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.deprecation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.deferredimport-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.exception-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.http-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.error-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.traversing-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.pagetemplate-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.datetime-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.contenttype-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.proxy-3.4.0-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.cachedescriptors-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.broken-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.dublincore-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.size-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.filerepresentation-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.lifecycleevent-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.dottedname-3.4.2-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.thread-3.4-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.session-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.folder-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.testing-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.testing-3.5.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.tal-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/pytz-2007g-py2.4.egg',
+ '/opt/zope/packages/eggs/zodbcode-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.renderer-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.server-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.hookable-3.4.0-py2.4-linux-i686.egg',
+ '/opt/zope/packages/eggs/zope.session-3.4.1-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.minmax-1.1.0-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.app.debug-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/RestrictedPython-3.4.2-py2.4.egg',
+ '/opt/zope/packages/eggs/zope.structuredtext-3.4.0-py2.4.egg',
+ '/opt/zope/packages/eggs/docutils-0.4-py2.4.egg',
+ '/opt/zope/packages/z3c.rest/./src',
+ ]
+
+import zope.app.twisted.main
+
+if __name__ == '__main__':
+ zope.app.twisted.main.main()
Property changes on: z3c.rest/trunk/parts/form-demo-app/runzope
___________________________________________________________________
Name: svn:executable
+
Added: z3c.rest/trunk/parts/form-demo-app/site.zcml
===================================================================
--- z3c.rest/trunk/parts/form-demo-app/site.zcml (rev 0)
+++ z3c.rest/trunk/parts/form-demo-app/site.zcml 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,5 @@
+<configure xmlns='http://namespaces.zope.org/zope'
+ xmlns:meta="http://namespaces.zope.org/meta"
+ >
+<include package="z3c.rest" file="application.zcml" />
+</configure>
Property changes on: z3c.rest/trunk/parts/form-demo-app/site.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/rest.prof
===================================================================
--- z3c.rest/trunk/rest.prof (rev 0)
+++ z3c.rest/trunk/rest.prof 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,277 @@
+hotshot-version51405requested-frame-timingsyesrequested-line-eventsnorequested-line-timingsnoplatformlinux2
+executable
+/usr/bin/py24executable-version\2.4.4 (#7, Oct 26 2007, 03:03:08)
+[GCC 4.2.2 20070909 (prerelease) (4.2.2-0.RC.1mdv2008.0)]observed-interval-getrusage10000observed-interval-gettimeofday2current-directory/opt/zope/packages/z3c.restsys-path-entry/opt/zope/packages/z3c.rest/srcsys-path-entry</opt/zope/packages/eggs/zope.contentprovider-3.4.0-py2.4.eggsys-path-entry2/opt/zope/packages/eggs/setuptools-0.6c7-py2.4.eggsys-path-entry:/opt/zope/packages/eggs/zope.app.zcmlfiles-3.4.3-py2.4.eggsys-path-entry5/opt/zope/packages/eggs/zope.app.wsgi-3.4.0-py2.4.eggsys-path-entry8/opt/zope/packages/eggs/zope.app.twisted-3.4.0-py2.4.eggsys-path-entry?/opt/zope/packages/eggs/zope.app.securitypolicy-3.4.6-py2.4.eggsys-path-entry9/opt/zope/packages/eggs/zope.app.security-3.4.0-py2.4.eggsys-path-entry</opt/zope/packages/eggs/zope.app.publication-3.4.2-py2.4.eggsys-path-entry:/opt/zope/packages/eggs/zope.app.publisher-3.4.1-py2.4.eggsys-path-entry5/opt/zope/packages/eggs/zope.app.form-3.4.1-py2.4.eggsys-path-entry6/opt/zope/packages/eggs/zope.app.error-3.5.1-py2.4.eggsys-path-entryE/opt/zope/packages/eggs/zope.app.container-3.5.3-py2.4-linux-i686.eggsys-path-entry:/opt/zope/packages/eggs/zope.app.component-3.4.1-py2.4.eggsys-path-entry?/opt/zope/packages/eggs/zope.app.authentication-3.4.1-py2.4.eggsys-path-entry9/opt/zope/packages/eggs/zope.app.appsetup-3.4.1-py2.4.eggsys-path-entry6/opt/zope/packages/eggs/zope.publisher-3.4.1-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/lxml-1.3.6-py2.4-linux-i686.eggsys-path-entry2/opt/zope/packages/eggs/zope.tales-3.4.0-py2.4.eggsys-path-entry3/opt/zope/packages/eggs/zope.schema-3.4.0-py2.4.eggsys-path-entry5/opt/zope/packages/eggs/zope.location-3.4.0-py2.4.eggsys-path-entryA/opt/zope/packages/eggs/zope.interface-3.4.1-py2.4-linux-i686.eggsys-path-entry2/opt/zope/packages/eggs/zope.event-3.4.0-py2.4.eggsys-path-entry6/opt/zope/packages/eggs/zope.component-3.4.0-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/zope.app.schema-3.4.0-py2.4.eggsys-path-entry=/opt/zope/packages/eggs/zope.app.pagetemplate-3.4.0-py2.4.eggsys-path-entry1/opt/zope/packages/eggs/zope.i18n-3.4.0-py2.4.eggsys-path-entry4/opt/zope/packages/eggs/zope.formlib-3.4.0-py2.4.eggsys-path-entry:/opt/zope/packages/eggs/zope.app.rotterdam-3.4.1-py2.4.eggsys-path-entry:/opt/zope/packages/eggs/zope.app.basicskin-3.4.0-py2.4.eggsys-path-entryD/opt/zope/packages/eggs/zope.app.principalannotation-3.4.0-py2.4.eggsys-path-entryC/opt/zope/packages/eggs/zope.app.zopeappgenerations-3.4.0-py2.4.eggsys-path-entry8/opt/zope/packages/eggs/zope.app.locales-3.4.0-py2.4.eggsys-path-entry5/opt/zope/packages/eggs/zope.app.i18n-3.4.4-py2.4.eggsys-path-entry:/opt/zope/packages/eggs/zope.app.interface-3.4.0-py2.4.eggsys-path-entry</opt/zope/packages/eggs/zope.app.generations-3.4.1-py2.4.eggsys-path-entry8/opt/zope/packages/eggs/zope.app.content-3.4.0-py2.4.eggsys-path-entry;/opt/zope/packages/eggs/zope.app.dependable-3.4.0-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/zope.annotation-3.4.0-py2.4.eggsys-path-entry8/opt/zope/packages/eggs/zope.modulealias-3.4.0-py2.4.eggsys-path-entry@/opt/zope/packages/eggs/zope.security-3.4.0-py2.4-linux-i686.eggsys-path-entry-/opt/zope/packages/eggs/ZConfig-2.5-py2.4.eggsys-path-entry5/opt/zope/packages/eggs/zope.app.zapi-3.4.0-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/zope.app.server-3.4.0-py2.4.eggsys-path-entryC/opt/zope/packages/eggs/zope.app.applicationcontrol-3.4.1-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/zope.exceptions-3.4.0-py2.4.eggsys-path-entry:/opt/zope/packages/eggs/zope.copypastemove-3.4.0-py2.4.eggsys-path-entry//opt/zope/packages/eggs/zdaemon-2.0.1-py2.4.eggsys-path-entry:/opt/zope/packages/eggs/ZODB3-3.8.0b4-py2.4-linux-i686.eggsys-path-entry;/opt/zope/packages/eggs/zope.securitypolicy-3.4.0-py2.4.eggsys-path-entryE/opt/zope/packages/eggs/zope.i18nmessageid-3.4.3-py2.4-linux-i686.eggsys-path-entry:/opt/zope/packages/eggs/zope.configuration-3.4.0-py2.4.eggsys-path-entry8/opt/zope/packages/eggs/zope.deprecation-3.4.0-py2.4.eggsys-path-entry;/opt/zope/packages/eggs/zope.deferredimport-3.4.0-py2.4.eggsys-path-entry:/opt/zope/packages/eggs/zope.app.exception-3.4.1-py2.4.eggsys-path-entry5/opt/zope/packages/eggs/zope.app.http-3.4.1-py2.4.eggsys-path-entry2/opt/zope/packages/eggs/zope.error-3.5.1-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/zope.traversing-3.4.0-py2.4.eggsys-path-entry9/opt/zope/packages/eggs/zope.pagetemplate-3.4.0-py2.4.eggsys-path-entry5/opt/zope/packages/eggs/zope.datetime-3.4.0-py2.4.eggsys-path-entry8/opt/zope/packages/eggs/zope.contenttype-3.4.0-py2.4.eggsys-path-entry=/opt/zope/packages/eggs/zope.proxy-3.4.0-py2.4-linux-i686.eggsys-path-entry=/opt/zope/packages/eggs/zope.cachedescriptors-3.4.0-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/zope.app.broken-3.4.0-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/zope.dublincore-3.4.0-py2.4.eggsys-path-entry1/opt/zope/packages/eggs/zope.size-3.4.0-py2.4.eggsys-path-entry?/opt/zope/packages/eggs/zope.filerepresentation-3.4.0-py2.4.eggsys-path-entry;/opt/zope/packages/eggs/zope.lifecycleevent-3.4.0-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/zope.dottedname-3.4.2-py2.4.eggsys-path-entry1/opt/zope/packages/eggs/zope.thread-3.4-py2.4.eggsys-path-entry8/opt/zope/packages/eggs/zope.app.session-3.5.1-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/zope.app.folder-3.4.0-py2.4.eggsys-path-entry8/opt/zope/packages/eggs/zope.app.testing-3.4.1-py2.4.eggsys-path-entry4/opt/zope/packages/eggs/zope.testing-3.5.1-py2.4.eggsys-path-entry0/opt/zope/packages/eggs/zope.tal-3.4.0-py2.4.eggsys-path-entry,/opt/zope/packages/eggs/pytz-2007g-py2.4.eggsys-path-entry0/opt/zope/packages/eggs/zodbcode-3.4.0-py2.4.eggsys-path-entry9/opt/zope/packages/eggs/zope.app.renderer-3.4.0-py2.4.eggsys-path-entry3/opt/zope/packages/eggs/zope.server-3.4.1-py2.4.eggsys-path-entry@/opt/zope/packages/eggs/zope.hookable-3.4.0-py2.4-linux-i686.eggsys-path-entry4/opt/zope/packages/eggs/zope.session-3.4.1-py2.4.eggsys-path-entry3/opt/zope/packages/eggs/zope.minmax-1.1.0-py2.4.eggsys-path-entry6/opt/zope/packages/eggs/zope.app.debug-3.4.0-py2.4.eggsys-path-entry8/opt/zope/packages/eggs/RestrictedPython-3.4.2-py2.4.eggsys-path-entry;/opt/zope/packages/eggs/zope.structuredtext-3.4.0-py2.4.eggsys-path-entry./opt/zope/packages/eggs/docutils-0.4-py2.4.eggsys-path-entry7/opt/zope/packages/eggs/zc.zope3recipes-0.6.0-py2.4.eggsys-path-entry//opt/zope/packages/z3c.rest/parts/form-demo-appsys-path-entry/opt/zope/packages/z3c.restsys-path-entry/usr/lib/ooo-2.2/programsys-path-entry/opt/python2.4/lib/python24.zipsys-path-entry/opt/python2.4/lib/python2.4sys-path-entry(/opt/python2.4/lib/python2.4/plat-linux2sys-path-entry#/opt/python2.4/lib/python2.4/lib-tksys-path-entry(/opt/python2.4/lib/python2.4/lib-dynloadsys-path-entry*/opt/python2.4/lib/python2.4/site-packagessys-path-entry./opt/python2.4/lib/python2.4/site-packages/PILS3 # N/opt/zope/packages/eggs/zope.app.debug-3.4.0-py2.4.egg/zope/app/debug/debug.pyC bpublish bC 6_request 6#)/opt/python2.4//lib/python2.4/StringIO.pyC6__init__6
+1#'/opt/python2.4//lib/python2.4/urllib.pyC§unquote§
+!#d/opt/zope/packages/eggs/zope.app.publication-3.4.2-py2.4.egg/zope/app/publication/zopepublication.pyCN__init__N#0/opt/zope/packages/z3c.rest/src/z3c/rest/rest.pyC!__init__!#M/opt/zope/packages/eggs/zope.publisher-3.4.1-py2.4.egg/zope/publisher/http.pyCª__init__ªCÀ__init__À)#M/opt/zope/packages/eggs/zope.publisher-3.4.1-py2.4.egg/zope/publisher/base.pyC»__init__»Cü_createResponseüCî__init__îC/__init__/
+CóresetóCAresetA -C@sane_environment@#0/opt/python2.4//lib/python2.4/encodings/utf_8.pyCdecode!C__setupCookies C¥__setupPath¥Cð_setupPath_helperðC9get9CËgetËCÔgetÔ C§setTraversalStack§
+ CÚ__setupURLBaseÚCó__deduceServerURLóACÂsetupLocaleÂ#U/opt/zope/packages/eggs/zope.app.twisted-3.4.0-py2.4.egg/twisted/python/components.pyCl_hook l%# V/opt/zope/packages/eggs/zope.app.component-3.4.1-py2.4.egg/zope/app/component/hooks.pyC ]adapter_hook$]C __get__$C &adapter_hook$&% #
+P/opt/zope/packages/eggs/zope.publisher-3.4.1-py2.4.egg/zope/publisher/browser.pyC
+ö__init__(ö
+#a/opt/zope/packages/eggs/zope.app.publisher-3.4.1-py2.4.egg/zope/app/publisher/browser/__init__.pyC[getPreferredLanguages,[Cd_getLanguagesData,dC
+ùgetPreferredLanguages(ù9ËÔ
+ C
+ìnormalize_lang(ì1
+#O/opt/zope/packages/eggs/zope.i18n-3.4.0-py2.4.egg/zope/i18n/locales/provider.pyCB getLocale0B
+ CåsetPublicationå
+ C_getResponse #
+P/opt/zope/packages/eggs/zope.publisher-3.4.1-py2.4.egg/zope/publisher/publish.pyC
+tpublish4t C×_getPublication×
+C%
+processInputs%
+CRbeforeTraversalR#W/opt/zope/packages/eggs/zope.component-3.4.0-py2.4.egg/zope/component/globalregistry.pyCgetGlobalSiteManager8
+#Q/opt/zope/packages/eggs/zope.component-3.4.0-py2.4.egg/zope/component/registry.pyC
+getUtility< %#`/opt/zope/packages/eggs/zope.app.security-3.4.0-py2.4.egg/zope/app/security/principalregistry.pyC%authenticate@% l
+$]#_/opt/zope/packages/eggs/zope.app.security-3.4.0-py2.4.egg/zope/app/security/basicauthadapter.pyC__init__D
+Cæ_authUserPWæ #\/opt/zope/packages/eggs/zope.app.security-3.4.0-py2.4.egg/zope/app/security/loginpassword.pyC__init__H
+C getLoginH C>unauthenticatedPrincipal@>
+#M/opt/zope/packages/eggs/zope.component-3.4.0-py2.4.egg/zope/component/_api.pyCË
+getUtilityLËCÑqueryUtilityLÑC IgetSiteManager$I
+CqueryUtility< CòsetPrincipalòCÒsetPrincipalÒ l$]#_/opt/zope/packages/eggs/zope.app.security-3.4.0-py2.4.egg/zope/app/security/principallogging.pyC__init__P
+
+C
+getLogMessageP
+#\/opt/zope/packages/eggs/zope.security-3.4.0-py2.4-linux-i686.egg/zope/security/management.pyCWnewInteractionTW×CMqueryInteractionTM)C7getSecurityPolicyT7 #]/opt/zope/packages/eggs/zope.securitypolicy-3.4.0-py2.4.egg/zope/securitypolicy/zopepolicy.pyC6__init__X6 #`/opt/zope/packages/eggs/zope.security-3.4.0-py2.4-linux-i686.egg/zope/security/simplepolicies.pyC__init__\C add\ #R/opt/zope/packages/eggs/ZODB3-3.8.0b4-py2.4-linux-i686.egg/transaction/_manager.pyCtbegin`t#V/opt/zope/packages/eggs/ZODB3-3.8.0b4-py2.4-linux-i686.egg/transaction/_transaction.pyC__init__d#1/opt/python2.4//lib/python2.4/logging/__init__.pyCÔ getLoggerhÔ CÇ getLoggerhÇC«_acquireLockh«#*/opt/python2.4//lib/python2.4/threading.pyC]acquirel]C
+currentThreadl C,_notel,
+ C·_releaseLockh· Coreleaselol l,
+ C¯debugh¯CðgetEffectiveLevelhðA C/_new_transaction`/#H/opt/zope/packages/eggs/ZODB3-3.8.0b4-py2.4-linux-i686.egg/ZODB/utils.pyCö__len__pö#)/opt/python2.4//lib/python2.4/UserDict.pyC__len__t
+CmappCas_weakref_listpC1<lambda>`1#M/opt/zope/packages/eggs/ZODB3-3.8.0b4-py2.4-linux-i686.egg/ZODB/Connection.pyC
+_storage_syncxCÖ_flush_invalidationsxÖi
+
+CgetApplicationCgetTraversalStack9ËÔ
+ #E/opt/zope/packages/eggs/ZODB3-3.8.0b4-py2.4-linux-i686.egg/ZODB/DB.pyCºopen|ºl]l
+l,
+ Cpop|Cù__contains__pù # (/opt/python2.4//lib/python2.4/weakref.pyC <__contains__<
+CÊopenxÊxÖ
+AC
+registerSynch`CýaddpýC M__setitem__MC º__new__ºC ¿__init__¿
+
+!Có_connectionMap|ól]l l,
+ C£map|£lol
+l, lol
+l,
+ C:__init__:
+C£hold£CopenedConnection CØrootxØCâgetxâC4gett4C'has_keyt'C__getitem__t
+ #!Y/opt/zope/packages/eggs/zope.security-3.4.0-py2.4-linux-i686.egg/zope/security/checker.pyC!;ProxyFactory;E CÂtraverseÂCítraverseí×
+C}callTraversalHooks}#"_/opt/zope/packages/eggs/zope.app.publication-3.4.2-py2.4.egg/zope/app/publication/interfaces.pyC"*__init__*##I/opt/zope/packages/eggs/zope.event-3.4.0-py2.4.egg/zope/event/__init__.pyC#notify#$N/opt/zope/packages/eggs/zope.component-3.4.0-py2.4.egg/zope/component/event.pyC$dispatchC|subscribersL|$I
+C¡subscribers<¡#%[/opt/zope/packages/eggs/zope.interface-3.4.1-py2.4-linux-i686.egg/zope/interface/adapter.pyC%subscribersC$objectEventNotifyL|$I
+<¡#&U/opt/zope/packages/eggs/zope.app.component-3.4.1-py2.4.egg/zope/app/component/site.pyC&òthreadSiteSubscriberò%C /setSite$/C&bgetSiteManagerb
+
+
+Cb_maybePlacefullyAuthenticatebCÕ<lambda>Õ
+b
+<5@% l $]$$&D æ
+H H @>
+CafterTraversalbÕ b
+<@% l$]Dæ H H @> #'Y/opt/zope/packages/eggs/zope.app.publication-3.4.2-py2.4.egg/zope/app/publication/http.pyC'A
+callObjectACjqueryMultiAdapterLj$I
+CÍqueryMultiAdapter<ÍC%ÝqueryMultiAdapterÝ#(^/opt/zope/packages/eggs/zope.app.component-3.4.1-py2.4.egg/zope/app/component/metaconfigure.pyC(__call__ "#)8/opt/zope/packages/z3c.rest/src/z3c/rest/tests/folder.pyC)__init__¤CF__init__FCÜ__contains__Ü 9ËÔ
+ Ü9ËÔ
+ #*M/opt/zope/packages/eggs/zope.component-3.4.0-py2.4.egg/zope/component/zcml.pyC*proxify¨ CgetPositionalArguments C
+<mapply4<C
+unwrapMethod4QC
+p
+debug_call4pC)$GET¤$#+k/opt/zope/packages/eggs/zope.app.pagetemplate-3.4.0-py2.4.egg/zope/app/pagetemplate/viewpagetemplatefile.pyC+:__get__¬:C+G__init__¬G
+C+N__call__¬NC++__call__¬+C+"
+pt_getContext¬"#,[/opt/zope/packages/eggs/zope.pagetemplate-3.4.0-py2.4.egg/zope/pagetemplate/pagetemplate.pyC,T
+pt_getContext°T
+#-]/opt/zope/packages/eggs/zope.app.pagetemplate-3.4.0-py2.4.egg/zope/app/pagetemplate/engine.pyC-Ýpt_getEngine´Ý
+#.F/opt/zope/packages/eggs/zope.tales-3.4.0-py2.4.egg/zope/tales/tales.pyC.ÇgetBaseNames¸Ç
+C+>__init__¬>
+
+C,f pt_render°f
+#/_/opt/zope/packages/eggs/zope.pagetemplate-3.4.0-py2.4.egg/zope/pagetemplate/pagetemplatefile.pyC/]_cook_check¼]#0*/opt/python2.4//lib/python2.4/posixpath.pyC0getmtimeÀ 6C,`pt_getEngineContext°`´Ý C-
+getContext´C.ñ__init__¸ñC.
+setContext¸
+¸ ¸ ¸
+% #1K/opt/zope/packages/eggs/zope.tal-3.4.0-py2.4.egg/zope/tal/talinterpreter.pyC1__init__ÄC.Ô
+getDefault¸Ô C1 popStreamÄ
+#2O/opt/zope/packages/eggs/zope.tal-3.4.0-py2.4.egg/zope/tal/translationcontext.pyC2__init__È
+C1__call__ÄC1Ë interpretÄËC1Þ
+do_versionÄÞ C1âdo_modeÄâC1ìdo_setSourceFileÄìC.×
+setSourceFile¸×
+ C1Ëdo_rawtextColumnÄËCÑwriteÑC&_complain_ifclosed& ! C1þdo_startTagÄþÑ&
+C1§do_rawtextBeginScope_talħÑ& C.ÚsetPosition¸Ú
+C.
+beginScope¸%C.setLocal¸
+ ÄþÑ& C1îdo_insertText_talÄîC.¿evaluateText¸¿C.³evaluate¸³#3L/opt/zope/packages/eggs/zope.tales-3.4.0-py2.4.egg/zope/tales/expressions.pyC3Ö__call__ÌÖ
+C3¶_eval̶C3l_evalÌlC-5__call__´5#4S/opt/zope/packages/eggs/zope.traversing-3.4.0-py2.4.egg/zope/traversing/adapters.pyC4ztraversePathElementÐz l=$]#5e/opt/zope/packages/eggs/zope.app.container-3.5.3-py2.4-linux-i686.egg/zope/app/container/traversal.pyC5Y__init__ÔY
+
+C5]traverseÔ]CcheckPermissionXÕ
+Cµ
+_groupsForXµ!CFcached_decisionXFC=cacheX=Cqcached_prinperXq!X= l$]#6Y/opt/zope/packages/eggs/zope.security-3.4.0-py2.4-linux-i686.egg/zope/security/adapter.pyC6P__call__ØP
+#7^/opt/zope/packages/eggs/zope.securitypolicy-3.4.0-py2.4.egg/zope/securitypolicy/securitymap.pyC7__init__Ü
+ l
+$]#8T/opt/zope/packages/eggs/zope.annotation-3.4.0-py2.4.egg/zope/annotation/attribute.pyC8#__init__à#
+C8)getà)5C6J_customizeUnprotectedØJ
+#9f/opt/zope/packages/eggs/zope.securitypolicy-3.4.0-py2.4.egg/zope/securitypolicy/principalpermission.pyC93
+getSettingä3C7S queryCellÜS XqX=C9g
+getSettingägÜS
+
+C®cached_rolesX®X=X®X=#:a/opt/zope/packages/eggs/zope.securitypolicy-3.4.0-py2.4.egg/zope/securitypolicy/rolepermission.pyC:XgetRolesForPermissionèXC7agetRowÜa 1 l
+$]ØPÜ l$]à# à)!ØJ C7__nonzero__Ü
+Cåcached_principal_rolesXåX=XåX=#;`/opt/zope/packages/eggs/zope.securitypolicy-3.4.0-py2.4.egg/zope/securitypolicy/principalrole.pyC;YgetRolesForPrincipalìYC7hgetColÜh l$]ØPÜ l
+$]à#
+à)ØJ
+Ü -#<Q/opt/zope/packages/eggs/zope.app.folder-3.4.0-py2.4.egg/zope/app/folder/folder.pyC<@getð@
+-5 C-1<lambda>´1
+)
+¸Ô
+ ħÑ&!¸Ú C.endScope¸!¸%¸
+ C1Õdo_conditionÄÕC.¼evaluateBoolean¸¼¸³C3__call__̸¼¸³ÌÖ̶Ìl´5 Ðz l$]C4&__init__Ð&
+
+
+C4)traverseÐ)CÌ__getitem__Ì9ËÔ !-)
+Ìl
+´5
+!!
+ ÄËÄþÑ&
+Ä¸³ÌÖ̶Ìl´5C-ò namespace´òCW
+getAdapterLWC]queryAdapterL]$]#=_/opt/zope/packages/eggs/zope.app.pagetemplate-3.4.0-py2.4.egg/zope/app/pagetemplate/talesapi.pyC=!__init__ô!
+
+ C=$ setEngineô$
+ ´5Ðz l
+$]Ð&
+Ð)C='titleô' l9$]ØP#>]/opt/zope/packages/eggs/zope.dublincore-3.4.0-py2.4.egg/zope/dublincore/annotatableadapter.pyC>'__init__ø' l $]à# à)!#?Y/opt/zope/packages/eggs/zope.dublincore-3.4.0-py2.4.egg/zope/dublincore/zopedublincore.pyC?g__init__üg
+ C6k_customizeProtectedØkØJ! ;)
+
+XÕ
+XµXFX=XqX= l$]XqX= l$]ØP Ü l$]à# à)!ØJ
+ä3ÜS
+ XqX=äg
+ÜS
+
+X®X=X®X=X®X=
+èX
+Üa 1 l$]ØP Ü l $]à# à)ØJ
+Ü l
+$] XåX=XåX=
+
+ l$] )C?!__get__ü!t4t' ´1 % ¸Ô
+C1ã
+_writeTextÄãÑ& C1Ðdo_rawtextOffsetÄÐÑ&
+
+ ħÑ& ¸Ú
+¸)¸%¸
+ ÄÕ¸¼¸³Ì¸¼¸³ÌÖ̶Ìl´5 Ðz l$]Ð&
+
+ Ð)Ì9ËÔ )%
+Ìl ´5
+
+%
+
+
+ ÄËÄþÑ&
+C1õdo_setPositionÄõ¸Ú C1ºdo_beginScope_talĺ¸)¸
+ C1µdo_loop_talĵC.© setRepeat¸©¸³ÌÖ̶Ìl´5Ðz l=$]ÔY
+ Ô]XÕ XµXFX=
+ð@ !X Õ XµXFX=
+! ´1
+C<.valuesð.! C.@__init__¸@u!C.tnext¸t¸
+ ÄËÄËÑ&
+C1údo_startEndTagÄúÄþC1×attrAction_talÄ׸¿¸³ÌÖ̶Ìl´5Ðz#@T/opt/zope/packages/eggs/zope.traversing-3.4.0-py2.4.egg/zope/traversing/namespace.pyC at unsParseuC@(namespaceLookup(Lj$I
+<ÍÝC@Ñ__init__Ñ!
+ C@ÕtraverseÕLj $I
+<ÍÝ C
+³__init__(³¨
+´1
+#A^/opt/zope/packages/eggs/zope.traversing-3.4.0-py2.4.egg/zope/traversing/browser/absoluteurl.pyCA*__str__*CÁgetVirtualHostRootÁ
+CdgetMultiAdapterLdLj$I
+<ÍÝ (³
+¨ CAl__str__lÁ CgetApplicationURL%CAF_getContextNameFC¾quote¾=
+ ¸Ô#BD/opt/zope/packages/eggs/zope.tal-3.4.0-py2.4.egg/zope/tal/taldefs.pyCBÇquoteÇ #C$/opt/python2.4//lib/python2.4/cgi.pyCCescape)Ä×
+¸¿¸³ÌÖ̶Ìl´5Ðz l=$]ÔY
+
+Ô]XÕ
+XµXFX=XqX= l$]ØP Ü l
+$]à#
+à)!ØJ ä3ÜS
+ XqX=
+
+X®X=X®X= l
+$]ØP Ü l
+$]à# à)ØJ Ü Xå X=XåX=
+ l $]ØPÜ l
+$]à# à)ØJ Ü
+
+1ð@
+1 ´1 % ¸Ô
+Ç1Ñ&
+
+
+¸t¸ ÄËÄËÑ& ÄúÄþÄ׸¿¸³ÌÖ̶Ìl´5Ðzu(Lj$I
+<ÍÝÑ
+ ÕLj$I <ÍÝ (³
+¨
+´1
+
+*Á
+LdLj$I
+<ÍÝ (³
+¨
+ lÁ %F¾ 1
+
+¸Ô Ç!Ä× ¸¿¸³ÌÖ̶Ìl´5Ðz l=$]ÔY(
+
+Ô]XÕ
+XµXFX=!XqX= l$]ØP Ü l
+$]à#
+à)!1ØJ
+ä3ÜS XqX= X®X=X®X=
+
+ l
+$]ØP Ü l
+$]à# à)ØJ Ü
+XåX=XåX=
+ l$]ØPÜ l
+$]à# à)ØJ
+Ü
+
+-ð@
+-
+´1
+%
+¸ÔÇ-Ñ&
+
+
+¸t C1Ádo_endScopeÄÁ¸1ÄËÑ&
+
+ÄÁ¸
+ÄËÑ&
+Cgetvalue =
+
+C¨ getHeader¨C setHeader
+ Cñ setResultñLj$I
+<ÍÝQ C_implicitResultCÉgetCharsetUsingRequestÉ l$]C__init__
+ CgetPreferredCharsetsܬ9ËÔ 9ËÔ 19¨#DT/opt/zope/packages/eggs/zope.publisher-3.4.1-py2.4.egg/zope/publisher/contenttype.pyCDparse
+CD$parseOrdered$CD`_check_token`
+-` MC<generator expression>
+
+ Cþ setStatusþ
+- C© afterCall©Cget`CÂisDoomeddÂ
+C'6annotateTransaction6CµannotateTransactionµÕ Õ CôsetUserdô lA$]C4<__init__Ð<
+ C4?getPathÐ? C÷setExtendedInfod÷ #E`/opt/zope/packages/eggs/zope.interface-3.4.1-py2.4-linux-i686.egg/zope/interface/declarations.pyCER__iter__R#F]/opt/zope/packages/eggs/zope.interface-3.4.1-py2.4-linux-i686.egg/zope/interface/interface.pyCFÂ
+interfacesÂCF¡<lambda>¡CFî
+interfacesî
+CFäextendsä%CFEgetNameE d÷
+ CgetURLd÷ CµcommitdµCð_callBeforeCommitHooksdð
+ppCÁ<lambda>dÁCùbeforeCompletionxù dÁxù C_commitResourcesd!Cfree`pp
+CÎ<lambda>dÎxxÖ U dÎxxÖ9
+C_callAfterCommitHooksd h¯hð9 C±
+endRequest±CbendInteractionTbC"8__init__8L|$I
+<¡C&úclearThreadSiteSubscriberú$/8
+
+CcloseC=release=
+CclosexCunregisterSynch`
+CremovepC__delitem__t
+
+ CÓ
+_returnToPool|Ól]l l, #GR/opt/zope/packages/eggs/ZODB3-3.8.0b4-py2.4-linux-i686.egg/ZODB/ActivityMonitor.pyCG$closedConnection$ CgetTransferCountsxCG+trim+ Cprepush|ppù<
+ C{_reduce_size|{lol
+l,
+)CA__del__A
+
+ C²
+getHeaders²Cì_cookie_listì #H'/opt/python2.4//lib/python2.4/Cookie.pyCH·__init__ · CHÈoutput È CgetStatusString) CconsumeBody
+C getStatus>
+
\ No newline at end of file
Added: z3c.rest/trunk/setup.py
===================================================================
--- z3c.rest/trunk/setup.py (rev 0)
+++ z3c.rest/trunk/setup.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,78 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation 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.
+#
+##############################################################################
+"""Setup
+
+$Id$
+"""
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+ return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup (
+ name='z3c.rest',
+ version='1.5.1',
+ author = "Stephan Richter and the Zope Community",
+ author_email = "zope3-dev at zope.org",
+ description = "A REST Framework for Zope 3 Applications",
+ long_description=(
+ read('README.txt')
+ + '\n\n' +
+ read('CHANGES.txt')
+ ),
+ license = "ZPL 2.1",
+ keywords = "zope3 form widget",
+ classifiers = [
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Programming Language :: Python',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Framework :: Zope3'],
+ url = 'http://cheeseshop.python.org/pypi/z3c.rest',
+ packages = find_packages('src'),
+ include_package_data = True,
+ package_dir = {'':'src'},
+ namespace_packages = ['z3c'],
+ extras_require = dict(
+ app = ['zope.app.appsetup',
+ 'zope.app.authentication',
+ 'zope.app.component',
+ 'zope.app.container',
+ 'zope.app.error',
+ 'zope.app.form',
+ 'zope.app.publisher',
+ 'zope.app.publication',
+ 'zope.app.security',
+ 'zope.app.securitypolicy',
+ 'zope.app.twisted',
+ 'zope.app.wsgi',
+ 'zope.app.zcmlfiles',
+ 'zope.contentprovider',
+ ],
+ test = ['z3c.coverage',
+ 'z3c.etestbrowser',
+ 'zope.app.testing'],
+ ),
+ install_requires = [
+ 'lxml',
+ 'setuptools',
+ 'zope.publisher',
+ ],
+ zip_safe = False,
+ )
Property changes on: z3c.rest/trunk/setup.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/__init__.py
===================================================================
--- z3c.rest/trunk/src/z3c/__init__.py (rev 0)
+++ z3c.rest/trunk/src/z3c/__init__.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,8 @@
+try:
+ # Declare this a namespace package if pkg_resources is available.
+ import pkg_resources
+ pkg_resources.declare_namespace('z3c')
+except ImportError:
+ pass
+
+
Property changes on: z3c.rest/trunk/src/z3c/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/README.txt
===================================================================
--- z3c.rest/trunk/src/z3c/rest/README.txt (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/README.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,29 @@
+===================================================
+A Framework for Building RESTive Services in Zope 3
+===================================================
+
+This package implements several components that relate to building RESTive Web
+services using the Zope publisher. Each set of components is documented in a
+corresponding text file.
+
+* ``client.txt`` [must read]
+
+ This package also provides a REST Web client, which can be used for testing
+ or for accessing a RESTive API within an application.
+
+* ``null.txt`` [advanced user]
+
+ In order to create new resources, the publisher must be able to traverse to
+ resources/objects that do not yet exist. This file explains how those null
+ resources work.
+
+* ``traverser.txt`` [advanced user]
+
+ The ``traverser`` module contains several traversal helper components for
+ common traversal scenarios, suhc as containers and null resources.
+
+* ``rest.txt`` [informative]
+
+ This document introduces the hooks required to manage RESTive requests in
+ the publisher. It also discusses hwo those components are used by the
+ publisher.
Property changes on: z3c.rest/trunk/src/z3c/rest/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c/rest/__init__.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/__init__.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/__init__.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1 @@
+# Make a package
Property changes on: z3c.rest/trunk/src/z3c/rest/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/application.zcml
===================================================================
--- z3c.rest/trunk/src/z3c/rest/application.zcml (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/application.zcml 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,72 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ i18n_domain="rest">
+
+ <include package="zope.app.component" file="meta.zcml" />
+ <include package="zope.app.component.browser" file="meta.zcml" />
+ <include package="zope.app.container.browser" file="meta.zcml" />
+ <include package="zope.app.form.browser" file="meta.zcml" />
+ <include package="zope.app.pagetemplate" file="meta.zcml" />
+ <include package="zope.app.publication" file="meta.zcml" />
+ <include package="zope.app.publisher" file="meta.zcml" />
+ <include package="zope.app.security" file="meta.zcml" />
+ <include package="zope.app.securitypolicy" file="meta.zcml" />
+
+ <browser:menu id="zmi_views" title="Views" />
+ <browser:menu id="zmi_actions" title="Actions" />
+
+ <include package="zope.app.appsetup" />
+ <include package="zope.app.authentication" />
+ <include package="zope.app.component" />
+ <include package="zope.app.container" />
+ <include package="zope.app.error" />
+ <include package="zope.app.folder" />
+ <include package="zope.app.http" />
+ <include package="zope.app.i18n" />
+ <include package="zope.app.pagetemplate" />
+ <include package="zope.app.publication" />
+ <include package="zope.app.security" />
+ <include package="zope.app.securitypolicy" />
+ <include package="zope.app.session" />
+ <include package="zope.app.twisted" />
+ <include package="zope.app.wsgi" />
+ <include package="zope.annotation" />
+ <include package="zope.component" />
+ <include package="zope.contentprovider" />
+ <include package="zope.dublincore" />
+ <include package="zope.location" />
+ <include package="zope.publisher" />
+ <include package="zope.traversing" />
+ <include package="zope.traversing.browser" />
+
+
+ <include file="configure.zcml" />
+ <include package=".tests" file="folder.zcml" />
+
+ <securityPolicy
+ component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
+
+ <role id="zope.Anonymous" title="Everybody" />
+ <grant permission="zope.View"
+ role="zope.Anonymous" />
+ <grant permission="zope.app.dublincore.view"
+ role="zope.Anonymous" />
+
+ <role id="zope.Manager" title="Site Manager" />
+ <grantAll role="zope.Manager" />
+
+ <principal
+ id="zope.mgr"
+ title="Manager"
+ login="mgr"
+ password="mgrpw" />
+
+ <principal
+ id="zope.globalmgr"
+ title="Manager"
+ login="globalmgr"
+ password="globalmgrpw" />
+
+ <grant role="zope.Manager" principal="zope.globalmgr" />
+</configure>
Property changes on: z3c.rest/trunk/src/z3c/rest/application.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c/rest/client.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/client.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/client.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,201 @@
+##############################################################################
+#
+# 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 Client
+
+$Id$
+"""
+import lxml
+import httplib
+import socket
+import urllib
+import urlparse
+import base64
+import zope.interface
+from z3c.rest import interfaces
+
+def isRelativeURL(url):
+ """Determines whether the given URL is a relative path segment."""
+ pieces = urlparse.urlparse(url)
+ if not pieces[0] and not pieces[1]:
+ return True
+ return False
+
+def absoluteURL(base, url):
+ """Convertes a URL to an absolute URL given a base."""
+ if isRelativeURL(url):
+ fullUrl = urlparse.urljoin(base, url)
+ else:
+ fullUrl = url
+ pieces = list(urlparse.urlparse(fullUrl))
+ if not pieces[2].endswith('/'):
+ pieces[2] += '/'
+ return urlparse.urlunparse(pieces)
+
+def getFullPath(pieces, params):
+ """Build a full httplib request path, including a query string."""
+ query = ''
+ if pieces[4]:
+ query = pieces[4]
+ if params:
+ encParams = urllib.urlencode(params)
+ if query:
+ query += '&' + encParams
+ else:
+ query = encParams
+ return urlparse.urlunparse(
+ ('', '', pieces[2], pieces[3], query, pieces[5]))
+
+
+class XLink(object):
+ """A link implementation for simple XLinks."""
+ zope.interface.implements(interfaces.ILink)
+
+ def __init__(self, client, title, url):
+ self._client = client
+ self.title = title
+ self.url = url
+
+ def click(self):
+ """See interfaces.ILink"""
+ self._client.get(self.url)
+
+ def __repr__(self):
+ return '<%s title=%r url=%r>' %(
+ self.__class__.__name__, self.title, self.url)
+
+
+class RESTClient(object):
+ zope.interface.implements(interfaces.IRESTClient)
+
+ connectionFactory = httplib.HTTPConnection
+
+ def __init__(self, url=None):
+ self.requestHeaders = {}
+ self._reset()
+ self._history = []
+ self._requestData = None
+ self.url = ''
+ if url:
+ self.open(url)
+
+ @property
+ def fullStatus(self):
+ return '%i %s' %(self.status, self.reason)
+
+ def _reset(self):
+ self.headers = []
+ self.contents = {}
+ self.status = None
+ self.reason = None
+
+ def open(self, url='', data=None, params=None, headers=None, method='GET'):
+ # Create a correct absolute URL and set it.
+ self.url = absoluteURL(self.url, url)
+
+ # Create the full set of request headers
+ requestHeaders = self.requestHeaders.copy()
+ if headers:
+ requestHeaders.update(headers)
+
+ # Let's now reset all response values
+ self._reset()
+
+ # Store all the request data
+ self._requestData = (url, data, params, headers, method)
+
+ # Make a connection and retrieve the result
+ pieces = urlparse.urlparse(self.url)
+ connection = self.connectionFactory(pieces[1])
+ try:
+ connection.request(
+ method, getFullPath(pieces, params), data, requestHeaders)
+ response = connection.getresponse()
+ connection.close()
+ except socket.error, e:
+ connection.close()
+ self.status, self.reason = e.args
+ self._addHistory()
+ raise e
+ else:
+ self.headers = response.getheaders()
+ self.contents = response.read()
+ self.status = response.status
+ self.reason = response.reason
+ self._addHistory()
+
+ def get(self, url='', params=None, headers=None):
+ self.open(url, None, params, headers)
+
+ def put(self, url='', data='', params=None, headers=None):
+ self.open(url, data, params, headers, 'PUT')
+
+ def post(self, url='', data='', params=None, headers=None):
+ self.open(url, data, params, headers, 'POST')
+
+ def delete(self, url='', params=None, headers=None):
+ self.open(url, None, params, headers, 'DELETE')
+
+ def setCredentials(self, username, password):
+ creds = username + u':' + password
+ creds = "Basic " + base64.encodestring(creds.encode('utf-8')).strip()
+ self.requestHeaders['Authorization'] = creds
+
+ def _addHistory(self):
+ self._history.append((
+ self.url, self.requestHeaders, self.headers, self.contents,
+ self.status, self.reason, self._requestData
+ ))
+
+ def goBack(self, count=1):
+ # The user really does not want to go back.
+ if count == 0:
+ return
+ # The user wants to reach before a pre-historical state.
+ if len(self._history) < count:
+ raise ValueError('There is not enough history.')
+ # Let's now get the entry and set the history back to that state.
+ entry = self._history[-(count+1)]
+ self._history = self._history[:-count]
+ # Reset the state.
+ (self.url, self.requestHeaders, self.headers, self.contents,
+ self.status, self.reason, self._requestData) = entry
+
+ def reload(self):
+ self.open(*self._requestData)
+
+ def getLink(self, title=None, url=None, index=0):
+ nsmap = {'xlink': "http://www.w3.org/1999/xlink"}
+ tree = lxml.etree.fromstring(self.contents)
+ res = []
+ if title is not None:
+ res = tree.xpath(
+ '//*[@xlink:title="%s"]' %title, nsmap)
+ elif url is not None:
+ res = tree.xpath(
+ '//*[@xlink:href="%s"]' %url, nsmap)
+ else:
+ raise ValueError('You must specify a title or URL.')
+ elem = res[index]
+ url = elem.attrib.get('{%(xlink)s}href' %nsmap, '')
+ return XLink(self,
+ elem.attrib.get('{%(xlink)s}title' %nsmap),
+ absoluteURL(self.url, url))
+
+ def xpath(self, expr, nsmap=None, single=False):
+ res = lxml.etree.fromstring(self.contents).xpath(expr, nsmap)
+ if not single:
+ return res
+ if len(res) != 1:
+ raise ValueError('XPath expression returned more than one result.')
+ return res[0]
Property changes on: z3c.rest/trunk/src/z3c/rest/client.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/client.txt
===================================================================
--- z3c.rest/trunk/src/z3c/rest/client.txt (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/client.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,508 @@
+===========
+REST Client
+===========
+
+The REST client provides a simple Python API to interact easily with RESTive
+Web services. It was designed with to have a similar API to Zope's test
+browser.
+
+Let's start by instantiating the the client. Of course we have a version of
+the client that talks directly to the Zope publisher:
+
+ >>> from z3c.rest import testing
+ >>> client = testing.RESTClient()
+
+For testing purposes, we have defined a simple REST API for folders. The
+simplest call is to retrieve the contents of the root folder:
+
+ >>> client.open('http://localhost/')
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ <items>
+ </items>
+ </folder>
+
+You can also instantiate the client providing a URL:
+
+ >>> client = testing.RESTClient('http://localhost/')
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ <items>
+ </items>
+ </folder>
+
+
+Getting Resources
+-----------------
+
+The ``open()`` method implicitely uses the "GET" HTTP method. An alternative
+would be to use this:
+
+ >>> client.get('http://localhost/')
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ <items>
+ </items>
+ </folder>
+
+There are several other pieces of information of the response that are
+available:
+
+ >>> client.url
+ 'http://localhost/'
+ >>> client.status
+ 200
+ >>> client.reason
+ 'Ok'
+ >>> client.fullStatus
+ '200 Ok'
+ >>> client.headers
+ [('X-Powered-By', 'Zope (www.zope.org), Python (www.python.org)'),
+ ('Content-Length', '140'),
+ ('Content-Type', 'text/xml;charset=utf-8')]
+
+If we try to access a non-existent resource, no exception is raised, but the
+status is '404' (not found) of course:
+
+ >>> client.get('http://localhost/unknown')
+ >>> client.fullStatus
+ '404 Not Found'
+ >>> client.contents
+ ''
+ >>> client.headers
+ [('X-Powered-By', 'Zope (www.zope.org), Python (www.python.org)'),
+ ('Content-Length', '0')]
+
+As in the original test browser, I can turn off the Zope error handling and
+the Python exception will propagate through the publisher:
+
+ >>> client.handleErrors = False
+ >>> client.get('http://localhost/unknown')
+ Traceback (most recent call last):
+ ...
+ NotFound: Object: <zope.app.folder.folder.Folder ...>, name: u'unknown'
+
+ >>> client.handleErrors = True
+
+As RESTive APIs often use query string key-value pairs to parameterize the
+request, this REST client has strong support for it. For example, you can
+simply specify the parameters in the URL:
+
+ >>> client.get('http://localhost/?noitems=1')
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ </folder>
+
+You can also specify the parameter via an argument:
+
+ >>> client.get('http://localhost/', params={'noitems': 1})
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ </folder>
+
+You can even combine the two methods of specifying parameters:
+
+ >>> client.get('http://localhost/?noitems=1', params={'notitle': 1})
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ </folder>
+
+But our little demo API can do morw. Parameters can also be specified as a
+header with a special prefix. Headers can be globally specified and are then
+used for every request:
+
+ >>> client.requestHeaders['demo-noitems'] = 'on'
+ >>> client.get('http://localhost/')
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ </folder>
+
+There is also a headers argument to the "open" methods that specify the header
+once:
+
+ >>> client.get('http://localhost/', headers={'demo-notitle': 1})
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ </folder>
+
+ >>> del client.requestHeaders['demo-noitems']
+
+Finally, when dealing with a real site, a socket error might occur. The error
+is propagated, but the error number and message are recorded:
+
+ >>> from z3c.rest.client import RESTClient
+ >>> realClient = RESTClient()
+ >>> realClient.open('http://localhost:65000')
+ Traceback (most recent call last):
+ ...
+ error: (111, 'Connection refused')
+
+ >>> realClient.fullStatus
+ '111 Connection refused'
+
+
+Creating new resources
+----------------------
+
+Let's now create a new resource in the server root. Our little sample
+application will simply create another collection:
+
+ >>> client.put(
+ ... 'http://localhost/folder1',
+ ... '''<?xml version="1.0" ?>
+ ... <folder />''')
+
+ >>> client.fullStatus
+ '401 Unauthorized'
+
+Accessing the folder resource is available to everyone. But if you want to
+modify any resource, you have to log in:
+
+ >>> client.setCredentials('globalmgr', 'globalmgrpw')
+
+So let's try this again:
+
+ >>> client.put(
+ ... 'http://localhost/folder1',
+ ... '''<?xml version="1.0" ?>
+ ... <folder />''')
+
+ >>> client.fullStatus
+ '201 Created'
+ >>> client.headers
+ [('X-Powered-By', 'Zope (www.zope.org), Python (www.python.org)'),
+ ('Content-Length', '0'),
+ ('Location', 'http://localhost/folder1')]
+
+We can now look at the root container and see the item there:
+
+ >>> client.get('http://localhost/')
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ <items>
+ <item xlink:type="simple"
+ xlink:href="http://localhost/folder1"
+ xlink:title="folder1"/>
+ </items>
+ </folder>
+
+By the way, you can now use a relative URL to access the `folder1` resource:
+
+ >>> client.get('folder1')
+
+ >>> client.url
+ 'http://localhost/folder1/'
+
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name>folder1</name>
+ <title></title>
+ <items>
+ </items>
+ </folder>
+
+When we try to create a resource on top of a non-existent resource, we get a
+404 error:
+
+ >>> client.put(
+ ... 'http://localhost/folder2/folder21',
+ ... '''<?xml version="1.0" ?>
+ ... <folder />''')
+
+ >>> client.fullStatus
+ '404 Not Found'
+
+
+Modifying Resources
+-------------------
+
+Modifying a given resource can be done via POST or PUT, but they have different
+semantics. Let's have a look at POST first. We would now like to change the
+title of the folder; this can be done as follows:
+
+ >>> client.post(
+ ... 'http://localhost/folder1',
+ ... '''<?xml version="1.0" ?>
+ ... <folder>
+ ... <title>My Folder 1</title>
+ ... </folder>''')
+
+ >>> client.fullStatus
+ '200 Ok'
+
+ >>> client.get()
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name>folder1</name>
+ <title>My Folder 1</title>
+ <items>
+ </items>
+ </folder>
+
+As mentioned above, it must also work for PUT:
+
+ >>> client.put(
+ ... 'http://localhost/folder1',
+ ... '''<?xml version="1.0" ?>
+ ... <folder>
+ ... <title>Folder 1</title>
+ ... </folder>''')
+
+ >>> client.fullStatus
+ '200 Ok'
+
+ >>> client.get()
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name>folder1</name>
+ <title>Folder 1</title>
+ <items>
+ </items>
+ </folder>
+
+
+Deleting Resources
+------------------
+
+Deleting a resource is as simple as all of the other methods. Let's delete our
+`folder1`:
+
+ >>> client.delete('http://localhost/folder1')
+
+ >>> client.fullStatus
+ '200 Ok'
+
+So the resource is really gone:
+
+ >>> client.get()
+ >>> client.fullStatus
+ '404 Not Found'
+
+It should not be possible to delete a non-existing resource:
+
+ >>> client.delete('http://localhost/folder2')
+ >>> client.fullStatus
+ '404 Not Found'
+
+Also, we cannot delete the root folder:
+
+ >>> client.delete('http://localhost/')
+ >>> client.fullStatus
+ '405 Method Not Allowed'
+
+
+Searching the Response Data
+---------------------------
+
+While not required, most REST services are XML-based. Thus, the client
+supports inspecting the result XML using XPath. Let's create a couple of
+folders for this to be more interesting:
+
+ >>> client.put(
+ ... 'http://localhost/folder1',
+ ... '''<?xml version="1.0" ?>
+ ... <folder />''')
+
+ >>> client.put(
+ ... 'http://localhost/folder2',
+ ... '''<?xml version="1.0" ?>
+ ... <folder />''')
+
+Next we get the root folder resource:
+
+ >>> client.get('http://localhost/')
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ <items>
+ <item xlink:type="simple"
+ xlink:href="http://localhost/folder1"
+ xlink:title="folder1"/>
+ <item xlink:type="simple"
+ xlink:href="http://localhost/folder2"
+ xlink:title="folder2"/>
+ </items>
+ </folder>
+
+But in general, inspecting the XML output on the string level is tedious. So
+let's write a cool XPath expression that extracts the xlink title of all
+items:
+
+ >>> nsmap = {'xlink': "http://www.w3.org/1999/xlink"}
+ >>> client.xpath('//folder/items/item/@xlink:title', nsmap)
+ ['folder1', 'folder2']
+
+Oftentimes, however, we specifically query for one result. In those cases we
+do not want to receive a list:
+
+ >>> client.xpath('//folder/items/item[@xlink:title="folder1"]', nsmap, True)
+ <Element item ...>
+
+Now, if multiple matches are detected, even though we only expect one, then a
+``ValueError`` is raised:
+
+ >>> client.xpath('//folder/items/item', nsmap, True)
+ Traceback (most recent call last):
+ ...
+ ValueError: XPath expression returned more than one result.
+
+
+Accessing Links
+---------------
+
+Since we want the REST client to behave like a browser -- at least a little
+bit -- we can also use the ``getLink()`` method to access links:
+
+ >>> client.getLink('folder1')
+ <XLink title='folder1' url='http://localhost/folder1/'>
+
+By default, the link is looked up by title. But you can also look it up by
+URL:
+
+ >>> client.getLink(url='http://localhost/folder1')
+ <XLink title='folder1' url='http://localhost/folder1/'>
+
+If you forget to specify a title or URL, you receive a ``ValueError``:
+
+ >>> client.getLink()
+ Traceback (most recent call last):
+ ...
+ ValueError: You must specify a title or URL.
+
+The cool part about the link is that you can click it:
+
+ >>> client.url
+ 'http://localhost/'
+
+ >>> client.getLink('folder1').click()
+
+ >>> client.url
+ 'http://localhost/folder1/'
+
+
+Moving through time
+-------------------
+
+Like in a real browser, you can go back to a previous state. For example,
+currently we are looking at `folder1`, ...
+
+ >>> client.url
+ 'http://localhost/folder1/'
+
+but if I go back one step, I am back at the root folder:
+
+ >>> client.goBack()
+
+ >>> client.url
+ 'http://localhost/'
+
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ <items>
+ <item xlink:type="simple"
+ xlink:href="http://localhost/folder1"
+ xlink:title="folder1"/>
+ <item xlink:type="simple"
+ xlink:href="http://localhost/folder2"
+ xlink:title="folder2"/>
+ </items>
+ </folder>
+
+But going back in history is only cool, if you can also reload. So let's
+delete `folder2`:
+
+ >>> client.getLink('folder2').click()
+ >>> client.delete()
+
+Now we go back 2 steps:
+
+ >>> client.goBack(2)
+
+ >>> client.url
+ 'http://localhost/'
+
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ <items>
+ <item xlink:type="simple"
+ xlink:href="http://localhost/folder1"
+ xlink:title="folder1"/>
+ <item xlink:type="simple"
+ xlink:href="http://localhost/folder2"
+ xlink:title="folder2"/>
+ </items>
+ </folder>
+
+As expected, the contents has not changed yet. So let's reload:
+
+ >>> client.reload()
+
+ >>> client.url
+ 'http://localhost/'
+
+ >>> print client.contents
+ <?xml version="1.0" ?>
+ <folder xmlns:xlink="http://www.w3.org/1999/xlink">
+ <name></name>
+ <title></title>
+ <items>
+ <item xlink:type="simple"
+ xlink:href="http://localhost/folder1"
+ xlink:title="folder1"/>
+ </items>
+ </folder>
+
+Note that going back zero steps does nothing:
+
+ >>> client.url
+ 'http://localhost/'
+
+ >>> client.getLink('folder1').click()
+ >>> client.goBack(0)
+
+ >>> client.url
+ 'http://localhost/folder1/'
+
+Also, if you try to go back beyond the beginning of time, a value error is
+raised:
+
+ >>> client.goBack(1000)
+ Traceback (most recent call last):
+ ...
+ ValueError: There is not enough history.
Property changes on: z3c.rest/trunk/src/z3c/rest/client.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c/rest/configure.zcml
===================================================================
--- z3c.rest/trunk/src/z3c/rest/configure.zcml (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/configure.zcml 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,70 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:zcml="http://namespaces.zope.org/zcml">
+
+ <!-- NULL Resource support -->
+
+ <class class=".null.NullResource">
+ <allow interface="zope.app.http.interfaces.INullResource" />
+ </class>
+
+ <!-- Basic Container traversers -->
+
+ <view
+ for="zope.app.http.interfaces.INullResource"
+ name="PUT"
+ type=".interfaces.IRESTRequest"
+ factory=".null.NullPUT"
+ permission="zope.Public"
+ allowed_attributes="PUT"
+ />
+
+ <view
+ for="zope.app.container.interfaces.IItemContainer"
+ type=".interfaces.IRESTRequest"
+ provides="zope.publisher.interfaces.IPublishTraverse"
+ factory=".traverser.ItemTraverser"
+ permission="zope.Public"
+ allowed_interface="zope.publisher.interfaces.IPublishTraverse"
+ />
+
+ <view
+ for="zope.app.container.interfaces.ISimpleReadContainer"
+ type=".interfaces.IRESTRequest"
+ provides="zope.publisher.interfaces.IPublishTraverse"
+ factory=".traverser.ItemTraverser"
+ permission="zope.Public"
+ allowed_interface="zope.publisher.interfaces.IPublishTraverse"
+ />
+
+ <!-- Basic REST publishing setup -->
+
+ <class class=".rest.RESTRequest">
+ <require
+ permission="zope.View"
+ interface="zope.publisher.interfaces.http.IHTTPApplicationRequest"/>
+ </class>
+
+ <configure
+ zcml:condition="have twisted">
+
+ <utility
+ name="REST-HTTP"
+ component=".twist.rest"
+ provides="zope.app.twisted.interfaces.IServerType"
+ />
+
+ </configure>
+
+ <configure
+ zcml:condition="have zserver">
+
+ <utility
+ name="REST-HTTP"
+ component=".zserver.rest"
+ provides="zope.app.server.interfaces.IServerType"
+ />
+
+ </configure>
+
+</configure>
Property changes on: z3c.rest/trunk/src/z3c/rest/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c/rest/interfaces.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/interfaces.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/interfaces.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,214 @@
+##############################################################################
+#
+# 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 publishing interfaces.
+
+$Id$
+"""
+import zope.interface
+import zope.schema
+from zope.location.interfaces import ILocation
+from zope.publisher.interfaces import http
+
+class IRESTRequest(http.IHTTPRequest):
+ """A special type of request for handling REST-based requests."""
+
+
+class IRESTView(ILocation):
+ """A REST view"""
+
+
+class ILink(zope.interface.Interface):
+ """An object representing a hyperlink."""
+
+ href = zope.schema.TextLine(
+ title=u"URL",
+ description=u"The normalized URL of the link",
+ required=False)
+
+ title = zope.schema.TextLine(
+ title=u'Title',
+ description=u'The title of the link',
+ required=False)
+
+ def click():
+ """click the link, going to the URL referenced"""
+
+
+class IRESTClient(zope.interface.Interface):
+ """A REST client.
+
+ This client provides a high-level API to access RESTful Web APIs. The
+ interface is designed to resemble the test browser API as much as
+ practical.
+ """
+
+ connectionFactory = zope.schema.Field(
+ title=u"Connection Facotry",
+ description=(u'A callable that creates an `httplib`-compliant '
+ u'connection object.'),
+ required=True)
+
+ requestHeaders = zope.schema.Dict(
+ title=u"Request Headers",
+ description=(u"A set of headers that will be sent in every request."),
+ required=True)
+
+ url = zope.schema.URI(
+ title=u"URL",
+ description=(u"The URL the browser is currently showing. It is "
+ u"always a full, absolute URL."),
+ required=True)
+
+ headers = zope.schema.List(
+ title=u"Response Headers",
+ description=(u'A list of all headers that have been returned '
+ u'in the last request.'),
+ required=True)
+
+ contents = zope.schema.Text(
+ title=u"Contents",
+ description=u"The complete response body of the HTTP request.",
+ required=True)
+
+ status = zope.schema.Int(
+ title=u"Status",
+ description=u"The status code of the last response.",
+ min=0,
+ required=True)
+
+ reason = zope.schema.TextLine(
+ title=u"Reason",
+ description=u"A short explanation of the status of the last response.",
+ required=True)
+
+ fullStatus = zope.schema.TextLine(
+ title=u"Full Status",
+ description=u"The status code and reason of the last response.",
+ required=True)
+
+ def open(url='', data=None, params=None, headers=None, method='GET'):
+ """Open a URL and retrieve the result.
+
+ The `url` argument can either be a full URL or a URL relative to the
+ previous one. If no URL is specified, then the previous URL will be
+ used.
+
+ The `data` is the contents of the request body. It is used to send
+ information to the server.
+
+ The `params` describe additional query parameters that will be added
+ to the request. Query string parameters are frequently used by RESTive
+ APIs to provide additional return value options.
+
+ The `headers` specify additional request headers that are specific for
+ this particular request.
+
+ The `method` specifies the HTTP method or verb to use to access the
+ resource on the server. While there are only a few methods in RFC
+ 2616, an string is allowed, since any particular API can extend the
+ set of allowed methods.
+ """
+
+ def get(url='', params=None, headers=None):
+ """Make a GET request to the server.
+
+ For argument details see ``open()``.
+ """
+
+ def put(url='', data='', params=None, headers=None):
+ """Make a PUT request to the server.
+
+ For argument details see ``open()``.
+ """
+
+ def post(url='', data='', params=None, headers=None):
+ """Make a POST request to the server.
+
+ For argument details see ``open()``.
+ """
+
+ def delete(url='', params=None, headers=None):
+ """Make a DELETE request to the server.
+
+ For argument details see ``open()``.
+ """
+
+ def setCredentials(username, password):
+ """Set the credentials.
+
+ This method adds the necessary information to authenticate the user. A
+ common example is basic auth, which inserts the `Authentication`
+ request header.
+ """
+
+ def goBack(count=1):
+ """Go back in history by a certain amount of visisted pages.
+
+ The ``count`` argument specifies how far to go back. It is set to 1 by
+ default.
+ """
+
+ def reload():
+ """Reload the current resource.
+
+ All arguments, including the HTTP method, parameters and additional
+ headers are honored.
+ """
+
+ def getLink(title=None, url=None, index=0):
+ """Return an ILink of the found link.
+
+ This method assumes that the current content type of the response body
+ is XML.
+
+ The link is found by the arguments of the method. Only one can be
+ used at a time:
+
+ o ``title`` -- The title or a sub-string thereof of the link.
+
+ o ``url`` -- The URL the link is going to.
+
+ If multiple matching links are found, the `index` specifies which one
+ to use.
+ """
+
+ def xpath(expr, nsmap=None, single=False):
+ """Returns the result of an XPath search expression.
+
+ This method assumes that the current content type of the response body
+ is XML.
+
+ The `expr` argument is the actual XPath expression. If the expression
+ is incorrect, an unspecified error must be raised.
+
+ The `nsmap` is a mapping from the short version to the full URL of
+ each XML namespace used in the expression.
+
+ If `single` is set to ``True``, then only one result is returned,
+ instead of a list. If the XPath expression results in more than one
+ result, a ``ValueError`` must be raised.
+ """
+
+
+class IPublisherRESTClient(IRESTClient):
+ """An extension to the REST client to support test-specific features."""
+
+ handleErrors = zope.schema.Bool(
+ title=u"Handle Errors",
+ description=(u"Describes whether server-side errors will be handled "
+ u"by the publisher. If set to ``False``, the error will "
+ u"progress all the way to the test, which is good for "
+ u"debugging."),
+ default=True,
+ required=True)
Property changes on: z3c.rest/trunk/src/z3c/rest/interfaces.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/null.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/null.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/null.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,54 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Null resource.
+
+$Id$
+"""
+import zope.component
+import zope.interface
+from zope.app.http.interfaces import INullResource
+from zope.location import location
+from zope.traversing.browser import absoluteURL
+
+from z3c.rest import rest
+
+class NullResource(location.Location):
+ """Object representing objects to be created by a `PUT`."""
+ zope.interface.implements(INullResource)
+
+ def __init__(self, container, name):
+ self.__parent__ = self.container = container
+ self.__name__ = self.name = name
+
+ def __repr__(self):
+ return '<%s %r>' %(self.__class__.__name__, self.name)
+
+
+class NullPUT(rest.RESTView):
+ """Put handler for null resources"""
+
+ def PUT(self):
+ nullPut = zope.component.queryMultiAdapter(
+ (self.context.container, self.request), name='NullPUT')
+ if nullPut is None:
+ # See RFC 2616, section 9.6
+ self.request.response.setStatus(501)
+ return
+
+ newObj = nullPut.NullPUT(self.context)
+
+ # See RFC 2616, section 9.6
+ self.request.response.setStatus(201)
+ self.request.response.setHeader(
+ 'Location', absoluteURL(newObj, self.request))
Property changes on: z3c.rest/trunk/src/z3c/rest/null.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/null.txt
===================================================================
--- z3c.rest/trunk/src/z3c/rest/null.txt (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/null.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,103 @@
+==============
+Null Resources
+==============
+
+It is sometimes necessary to traverse to resources that do not yet exist. In
+particular, this is needed when creating resources using "PUT" or "POST". It
+is the responsibility of the traverser to handle those cases correctly and
+produce the null resources. This document only describes their behavior.
+
+A null resource is easily instantiated using the container and the name of the
+resource:
+
+ >>> class Folder(object):
+ ... __parent__ = __name__ = None
+ ... child = None
+ ...
+ ... def __init__(self, name=''):
+ ... self.name = self.__name__ = name
+ ...
+ ... def __repr__(self):
+ ... return '<Folder %r>' %self.name
+ >>> folder = Folder()
+
+ >>> from z3c.rest import null
+ >>> resource = null.NullResource(folder, 'resource')
+
+ >>> from zope.app.http.interfaces import INullResource
+ >>> INullResource.providedBy(resource)
+ True
+
+Null resources are locations, so security is available:
+
+ >>> from zope.location.interfaces import ILocation
+ >>> ILocation.providedBy(resource)
+ True
+
+The container is also the parent:
+
+ >>> resource.container
+ <Folder ''>
+ >>> resource.__parent__
+ <Folder ''>
+
+The name of the resource is available at:
+
+ >>> resource.name
+ 'resource'
+ >>> resource.__name__
+ 'resource'
+
+There is a special implementation of "PUT" for null resources. It works by
+looking up a view called "NullPUT" for the container. This way, one null
+resource implementation can be used for all container implementations.
+
+ >>> import StringIO
+ >>> from z3c.rest import rest
+ >>> request = rest.RESTRequest(StringIO.StringIO(), {})
+
+ >>> nullPut = null.NullPUT(resource, request)
+ >>> nullPut.PUT()
+
+Since no view called "NullPUT" exists for our `Folder` class, we get a 501
+return status:
+
+ >>> request.response.getStatusString()
+ '501 Not Implemented'
+
+Let's now register a simple NullPUT view:
+
+ >>> class FolderAPI(rest.RESTView):
+ ...
+ ... def NullPUT(self, resource):
+ ... self.context.child = Folder(resource.name)
+ ... self.context.child.__parent__ = self.context
+ ... return self.context.child
+
+ >>> import zope.component
+ >>> from z3c.rest import interfaces
+ >>> zope.component.provideAdapter(
+ ... FolderAPI, (Folder, interfaces.IRESTRequest), name='NullPUT')
+
+Let's make sure our location structure is correctly setup, so that absolute
+URL will work:
+
+ >>> from zope.traversing.interfaces import IContainmentRoot
+ >>> import zope.interface
+ >>> zope.interface.alsoProvides(folder, IContainmentRoot)
+
+Now we are ready to PUT the new resource:
+
+ >>> request = rest.RESTRequest(
+ ... StringIO.StringIO(), {'SERVER_URL': 'http://localhost/'})
+
+ >>> nullPut = null.NullPUT(resource, request)
+ >>> nullPut.PUT()
+
+ >>> request.response.getStatusString()
+ '201 Created'
+ >>> request.response.getHeader('Location')
+ 'http://localhost/resource'
+
+ >>> folder.child
+ <Folder 'resource'>
Property changes on: z3c.rest/trunk/src/z3c/rest/null.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c/rest/rest.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/rest.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/rest.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,94 @@
+##############################################################################
+#
+# 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 cgi
+import zope.interface
+from zope.app.publication.interfaces import IPublicationRequestFactory
+from zope.app.publication.http import HTTPPublication
+from zope.publisher.http import HTTPRequest
+
+from z3c.rest import interfaces
+
+class RESTRequest(HTTPRequest):
+ zope.interface.implements(interfaces.IRESTRequest)
+
+ __slots__ = (
+ 'parameters', # Parameters sent via the query string.
+ )
+
+ def __init__(self, body_instream, environ, response=None):
+ self.parameters = {}
+ super(RESTRequest, self).__init__(body_instream, environ, response)
+
+ def processInputs(self):
+ 'See IPublisherRequest'
+ if 'QUERY_STRING' not in self._environ:
+ return
+ # Parse the query string into our parameters dictionary.
+ self.parameters = cgi.parse_qs(
+ self._environ['QUERY_STRING'], keep_blank_values=1)
+ # Since the parameter value is always a list (sigh), let's at least
+ # detect single values and store them.
+ for name, value in self.parameters.items():
+ if len(value) == 1:
+ self.parameters[name] = value[0]
+
+ def keys(self):
+ 'See Interface.Common.Mapping.IEnumerableMapping'
+ d = {}
+ d.update(self._environ)
+ d.update(self.parameters)
+ return d.keys()
+
+ def get(self, key, default=None):
+ 'See Interface.Common.Mapping.IReadMapping'
+ marker = object()
+ result = self.parameters.get(key, marker)
+ if result is not marker:
+ return result
+
+ return super(RESTRequest, self).get(key, default)
+
+
+class RESTView(object):
+ zope.interface.implements(interfaces.IRESTView)
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ @apply
+ def __parent__():
+ def get(self):
+ return getattr(self, '_parent', self.context)
+ def set(self, parent):
+ self._parent = parent
+ return property(get, set)
+
+
+class RESTPublicationRequestFactory(object):
+ zope.interface.implements(IPublicationRequestFactory)
+
+ def __init__(self, db):
+ """See zope.app.publication.interfaces.IPublicationRequestFactory"""
+ self.publication = HTTPPublication(db)
+
+ def __call__(self, input_stream, env, output_stream=None):
+ """See zope.app.publication.interfaces.IPublicationRequestFactory"""
+ request = RESTRequest(input_stream, env)
+ request.setPublication(self.publication)
+ return request
Property changes on: z3c.rest/trunk/src/z3c/rest/rest.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/rest.txt
===================================================================
--- z3c.rest/trunk/src/z3c/rest/rest.txt (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/rest.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,204 @@
+=================================
+Publisher Hooks for REST Requests
+=================================
+
+Reading this document requires -- to some extend -- that the reader is
+familiar with the basic steps of the publication process.
+
+
+The Publication Request Factory
+-------------------------------
+
+The Zope publication process starts when a WSGI server sends the request
+environment and response initialization callable to the Zope WSGI Publisher
+application _[1]. The WSGI publisher application is then responsible for
+processing the request in the publisher and stream out the result.
+
+In order to process a request in the publisher, we have to create a valid
+publisher request object. The WSGI publisher application uses a request
+factory for this purpose. This package implements this factory to ensure that
+a special REST request (based on HTTP Request) is created at all times.
+
+The request factory is instantiated using a ZODB database object:
+
+ >>> from ZODB.DB import DB
+ >>> from ZODB.DemoStorage import DemoStorage
+ >>> db = DB(DemoStorage())
+
+Let's now create the factory:
+
+ >>> from z3c.rest import rest
+ >>> RequestFactory = rest.RESTPublicationRequestFactory(db)
+
+When a request comes in from the server, the request is created as follows:
+
+ >>> import StringIO
+ >>> inStream = StringIO.StringIO('Input stream')
+
+ >>> env = {'HTTP_ACCEPT_LANGUAGE': 'en-US,en',
+ ... 'SERVER_URL': 'http://localhost:8080/'}
+
+ >>> request = RequestFactory(inStream, env)
+
+We now got a valid request that we can send through the publisher:
+
+ >>> request
+ <z3c.rest.rest.RESTRequest instance URL=http://localhost:8080>
+
+The request, however, is only responsible for representing the network request
+in the publisher and has no direct knowledge of the application. But the
+request connects to an application-specific -- in this case Zope 3 --
+component known as the publication.
+
+ >>> request.publication
+ <zope.app.publication.http.HTTPPublication object at ...>
+
+Since we do not need a special REST publication, we are simply reusing the
+more generic HTTP version. The publication will be the same for all
+requests. It also contains the reference to the database:
+
+ >>> request.publication.db
+ <ZODB.DB.DB object at ...>
+
+Unfortunately, it takes a lot more setup to send the request through the
+publisher successfully. The publication requires many other aspects of
+publishing to be available, including traversal, security, and a properly
+constructed database. However, we can still see a failure:
+
+ >>> from zope.publisher import publish
+ >>> 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>
+
+Let' unwind a bit. Originally, we started with the desire to create a
+Publisher WSGI Application instance that internally uses a REST request. All
+that you need to do is:
+
+ >>> from zope.app import wsgi
+ >>> app = wsgi.WSGIPublisherApplication(
+ ... db, rest.RESTPublicationRequestFactory)
+ >>> app
+ <zope.app.wsgi.WSGIPublisherApplication object at ...>
+
+When the WSGI server sends a request to the WSGI application, the following
+happens:
+
+ >>> status = None
+ >>> headers = None
+ >>> def start_response(s, h):
+ ... global status
+ ... global headers
+ ... status, headers = s, h
+
+ >>> wsgiEnv = {'wsgi.input': inStream}
+ >>> 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>
+
+
+.. [1]: ``zope.app.wsgi.WSGIPublisherApplication.__call__``
+
+
+The REST Request
+----------------
+
+For most parts, the REST request is identical to the HTTP request, so I won't
+go into too much detail about the HTTP request API.
+
+The REST request mainly extends the HTTP request in that it parses the query
+string of the URL into a set of parameters. This happens during
+``processInputs()``.
+
+If there is no query string, the paramaters mapping is empty:
+
+ >>> request = RequestFactory(
+ ... StringIO.StringIO(), {})
+ >>> request.processInputs()
+ >>> request.parameters
+ {}
+
+So let's now pass a few parameters:
+
+ >>> request = RequestFactory(
+ ... StringIO.StringIO(),
+ ... {'QUERY_STRING': 'format=html&action=delete&item=1&item=3'})
+ >>> request.processInputs()
+ >>> pprint(request.parameters)
+ {'action': 'delete',
+ 'format': 'html',
+ 'item': ['1', '3']}
+
+We also override some of the request's mapping methods, so that the parameters
+and environment values are available as part of the request:
+
+ >>> sorted(request.keys())
+ ['QUERY_STRING', 'action', 'format', 'item']
+
+ >>> request.get('QUERY_STRING')
+ 'format=html&action=delete&item=1&item=3'
+ >>> request.get('action')
+ 'delete'
+ >>> request.get('unknwon', 'default')
+ 'default'
+
+
+REST Views
+----------
+
+Unlike browser views, a REST view does *not* represent its own sub-resource
+(such as "index.html"). Instead it merely defines the behavior of the HTTP
+methods for a particular content component.
+
+Here is an example:
+
+ >>> class ObjectAPI(rest.RESTView):
+ ...
+ ... def GET(self):
+ ... return str(self.context)
+
+The ``RESTView`` base class provides a suitable constructor:
+
+ >>> class Object(object):
+ ... def __repr__(self):
+ ... return '<Object>'
+ >>> myobj = Object()
+
+ >>> request = RequestFactory(
+ ... StringIO.StringIO(), {'SERVER_URL': 'http://localhost:8080/myobj'})
+
+ >>> view = ObjectAPI(myobj, request)
+
+When the publisher traverses to `myobj`, it will look up a view based on the
+HTTP mehtod, such as "GET". It then also expects to find a method of that same
+name and calls it _[2].
+
+ >>> view.GET()
+ '<Object>'
+
+The REST View, like all other views, exposes its context and the request:
+
+ >>> view.context
+ <Object>
+ >>> view.request
+ <z3c.rest.rest.RESTRequest instance URL=http://localhost:8080/myobj>
+
+Also, a view must be located, so it has a parent as well:
+
+ >>> view.__parent__
+ <Object>
+
+You can set it to something else of course:
+
+ >>> view.__parent__ = 1
+ >>> view.__parent__
+ 1
+
+.. [2]: ``zope.app.publication.HTTPPublication.callObject``
Property changes on: z3c.rest/trunk/src/z3c/rest/rest.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c/rest/testing.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/testing.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/testing.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,100 @@
+##############################################################################
+#
+# 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 testing support.
+
+$Id$
+"""
+import sys
+import zope.interface
+from z3c.rest import client, rest, interfaces
+from zope.app.publication.http import HTTPPublication
+from zope.app.testing.functional import HTTPCaller
+
+
+class RESTCaller(HTTPCaller):
+ """An HTTP caller for REST functional page tests"""
+
+ def chooseRequestClass(self, method, path, environment):
+ """Always returns HTTPRequests regardless of methods and content"""
+ return rest.RESTRequest, HTTPPublication
+
+
+class PublisherConnection(object):
+
+ def __init__(self, server, port=None):
+ self._response = None
+ self.server = server
+ self.port = port
+
+ def request(self, method, path, body, headers):
+ # Extract the handle_error option header
+ if sys.version_info >= (2,5):
+ handleErrorsKey = 'X-Zope-Handle-Errors'
+ else:
+ handleErrorsKey = 'X-zope-handle-errors'
+ handleErrors = headers.get(handleErrorsKey, True)
+ if handleErrorsKey in headers:
+ del headers[handleErrorsKey]
+
+ # Construct the request body and call the publisher
+ body = body or ''
+ request = ["%s %s HTTP/1.1" % (method, path)]
+ for hdr, value in headers.items():
+ request.append("%s: %s" % (hdr, value))
+ request_string = "\n".join(request) + "\n\n" + body
+ self._response = RESTCaller()(
+ request_string, handle_errors=handleErrors)
+
+ def getresponse(self):
+ return PublisherResponse(self._response)
+
+ def close(self):
+ self._response = None
+
+
+class PublisherResponse(object):
+ """Adapter of Zope 3 response objects into httplib.HTTPResponse."""
+
+ def __init__(self, response):
+ self._response = response
+ self.status = response.getStatus()
+ self.reason = response._reason
+
+ def getheaders(self):
+ return self._response.getHeaders()
+
+ def read(self):
+ return self._response.consumeBody()
+
+
+class RESTClient(client.RESTClient):
+ zope.interface.implements(interfaces.IPublisherRESTClient)
+
+ connectionFactory = PublisherConnection
+
+ @apply
+ def handleErrors():
+ """See zope.testbrowser.interfaces.IBrowser"""
+ headerKey = 'X-zope-handle-errors'
+
+ def get(self):
+ return self.requestHeaders.get(headerKey, True)
+
+ def set(self, value):
+ current_value = get(self)
+ if current_value == value:
+ return
+ self.requestHeaders[headerKey] = value
+
+ return property(get, set)
Property changes on: z3c.rest/trunk/src/z3c/rest/testing.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/tests/__init__.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/tests/__init__.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/tests/__init__.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1 @@
+# Make a package
Property changes on: z3c.rest/trunk/src/z3c/rest/tests/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/tests/folder.pt
===================================================================
--- z3c.rest/trunk/src/z3c/rest/tests/folder.pt (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/tests/folder.pt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,14 @@
+<?xml version="1.0" ?>
+<folder xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+ <name tal:content="context/__name__">folder</name>
+ <title tal:content="context/zope:title"
+ tal:condition="not: request/notitle|nothing">Folder</title>
+ <items
+ tal:condition="not: request/noitems|nothing">
+ <item xlink:type="simple" xlink:href="" xlink:title=""
+ tal:repeat="item context/values"
+ tal:attributes="xlink:href item/@@absolute_url;
+ xlink:title item/__name__" />
+ </items>
+</folder>
Property changes on: z3c.rest/trunk/src/z3c/rest/tests/folder.pt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c/rest/tests/folder.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/tests/folder.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/tests/folder.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Test REST view for folders.
+
+$Id$
+"""
+import lxml.etree
+from z3c.rest import rest
+from zope.app.folder import folder
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+from zope.app.publication.http import MethodNotAllowed
+from zope.dublincore.interfaces import IZopeDublinCore
+
+class FolderAPI(rest.RESTView):
+ """A simple REST view for folders."""
+
+ template = ViewPageTemplateFile("folder.pt")
+
+ def __init__(self, context, request):
+ super(FolderAPI, self).__init__(context, request)
+ for param in ('noitems', 'notitle'):
+ if 'HTTP_DEMO_' + param.upper() in self.request:
+ self.request.parameters[param] = 1
+
+ def GET(self):
+ return self.template()
+
+ def POST(self):
+ tree = lxml.etree.parse(self.request.bodyStream)
+ title = tree.find('title').text
+ dc = IZopeDublinCore(self.context)
+ dc.title = unicode(title)
+
+ # For existing resources, PUT pretty much behaves like POST
+ PUT = POST
+
+ def NullPUT(self, nullResource):
+ name = nullResource.name
+ self.context[name] = folder.Folder()
+ return self.context[name]
+
+ def DELETE(self):
+ container = self.context.__parent__
+ if container is None:
+ raise MethodNotAllowed(self.context, self.request)
+ del container[self.context.__name__]
Property changes on: z3c.rest/trunk/src/z3c/rest/tests/folder.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/tests/folder.zcml
===================================================================
--- z3c.rest/trunk/src/z3c/rest/tests/folder.zcml (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/tests/folder.zcml 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,48 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+<view
+ for="zope.app.folder.interfaces.IFolder"
+ name="GET"
+ type="..interfaces.IRESTRequest"
+ factory=".folder.FolderAPI"
+ permission="zope.View"
+ allowed_attributes="GET"
+ />
+
+<view
+ for="zope.app.folder.interfaces.IFolder"
+ name="POST"
+ type="..interfaces.IRESTRequest"
+ factory=".folder.FolderAPI"
+ permission="zope.ManageContent"
+ allowed_attributes="POST"
+ />
+
+<view
+ for="zope.app.folder.interfaces.IFolder"
+ name="PUT"
+ type="..interfaces.IRESTRequest"
+ factory=".folder.FolderAPI"
+ permission="zope.ManageContent"
+ allowed_attributes="PUT"
+ />
+
+<view
+ name="NullPUT"
+ for="zope.app.folder.interfaces.IFolder"
+ type="..interfaces.IRESTRequest"
+ factory=".folder.FolderAPI"
+ permission="zope.ManageContent"
+ allowed_attributes="NullPUT"
+ />
+
+<view
+ for="zope.app.folder.interfaces.IFolder"
+ name="DELETE"
+ type="..interfaces.IRESTRequest"
+ factory=".folder.FolderAPI"
+ permission="zope.ManageContent"
+ allowed_attributes="DELETE"
+ />
+
+</configure>
Property changes on: z3c.rest/trunk/src/z3c/rest/tests/folder.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c/rest/tests/ftesting.zcml
===================================================================
--- z3c.rest/trunk/src/z3c/rest/tests/ftesting.zcml (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/tests/ftesting.zcml 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,6 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope">
+
+ <include package="z3c.rest" file="application.zcml" />
+
+</configure>
Property changes on: z3c.rest/trunk/src/z3c/rest/tests/ftesting.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c/rest/tests/test.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/tests/test.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/tests/test.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,79 @@
+##############################################################################
+#
+# Copyright (c) 2004 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 Tests
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import os
+import unittest
+import zope.component
+from zope.testing import doctest, doctestunit
+from zope.traversing.browser import absoluteurl
+from zope.traversing.interfaces import IContainmentRoot
+from zope.app.testing import functional, placelesssetup
+from z3c.rest import interfaces
+
+RESTLayer = functional.ZCMLLayer(
+ os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'),
+ __name__, 'RESTLayer', allow_teardown=True)
+
+def setUp(test):
+ placelesssetup.setUp(test)
+
+ # XXX: This really needs a REST equivalent w/o breadcrumbs.
+
+ zope.component.provideAdapter(
+ absoluteurl.AbsoluteURL,
+ (None, interfaces.IRESTRequest),
+ absoluteurl.IAbsoluteURL)
+
+ zope.component.provideAdapter(
+ absoluteurl.SiteAbsoluteURL,
+ (IContainmentRoot, interfaces.IRESTRequest),
+ absoluteurl.IAbsoluteURL,
+ 'absolute_url')
+
+ zope.component.provideAdapter(
+ absoluteurl.SiteAbsoluteURL,
+ (IContainmentRoot, interfaces.IRESTRequest),
+ absoluteurl.IAbsoluteURL)
+
+ zope.component.provideAdapter(
+ absoluteurl.AbsoluteURL,
+ (None, interfaces.IRESTRequest),
+ absoluteurl.IAbsoluteURL,
+ 'absolute_url')
+
+
+def test_suite():
+ client = functional.FunctionalDocFileSuite('../client.txt')
+ client.layer = RESTLayer
+ return unittest.TestSuite((
+ client,
+ doctest.DocFileSuite(
+ '../rest.txt',
+ globs={'pprint': doctestunit.pprint},
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ doctest.DocFileSuite(
+ '../null.txt',
+ setUp=setUp, tearDown=placelesssetup.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ doctest.DocFileSuite(
+ '../traverser.txt',
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ ))
Property changes on: z3c.rest/trunk/src/z3c/rest/tests/test.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/traverser.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/traverser.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/traverser.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Some basic traversers for REST
+
+$Id$
+"""
+import zope.interface
+from zope.publisher.interfaces.http import IHTTPPublisher
+from zope.app.container.interfaces import IItemContainer
+from zope.publisher.interfaces import NotFound
+from z3c.rest import interfaces, null
+
+class ItemTraverser(object):
+ zope.interface.implements(IHTTPPublisher)
+ zope.component.adapts(IItemContainer, interfaces.IRESTRequest)
+
+ def __init__(self, container, request):
+ self.context = container
+ self.request = request
+
+ def publishTraverse(self, request, name):
+ try:
+ return self.context[name]
+ except KeyError:
+ return self.nullResource(request, name)
+
+ def nullResource(self, request, name):
+ # we traversed to something that doesn't exist.
+
+ # The name must be the last name in the path, so the traversal
+ # name stack better be empty:
+ if request.getTraversalStack():
+ raise NotFound(self.context, name, request)
+
+ # This should only happen for a PUT:
+ if request.method != 'PUT':
+ raise NotFound(self.context, name, request)
+
+ return null.NullResource(self.context, name)
Property changes on: z3c.rest/trunk/src/z3c/rest/traverser.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/traverser.txt
===================================================================
--- z3c.rest/trunk/src/z3c/rest/traverser.txt (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/traverser.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,74 @@
+=========================
+REST Traverser Components
+=========================
+
+The traverser module contains several travreser implementations for common
+scenarios.
+
+
+Item Container Traverser
+------------------------
+
+The item mapping interface -- from which item container inherits -- is the
+most minimal mapping interface in Python. Thus, once traversing through this
+item container is implemented, it can be used by all other container
+interfaces and implementations.
+
+Let's start by creating a very simple item container implementation:
+
+ >>> import zope.interface
+ >>> from zope.app.container.interfaces import IItemContainer
+
+ >>> class SimpleContainer(dict):
+ ... zope.interface.implements(IItemContainer)
+ ... def __init__(self, name=''):
+ ... self.name = name
+ ... def __repr__(self):
+ ... return '<Container name=%s>' %self.name
+
+ >>> container = SimpleContainer()
+
+ >>> container['sub1'] = SimpleContainer('sub1')
+ >>> container['sub2'] = SimpleContainer('sub2')
+
+After creating a traverser instance,
+
+ >>> from z3c.rest import traverser
+
+ >>> import StringIO
+ >>> from z3c.rest import rest
+ >>> request = rest.RESTRequest(StringIO.StringIO(), {})
+
+
+ >>> containerTraverser = traverser.ItemTraverser(
+ ... container, request)
+
+we can traverse to a sub-object of that container:
+
+ >>> containerTraverser.publishTraverse(request, 'sub1')
+ <Container name=sub1>
+
+If no proper sub-item can be found, some interesting can happen. In a normal
+case, ``NotFound`` is raised:
+
+ >>> containerTraverser.publishTraverse(request, 'unknown')
+ Traceback (most recent call last):
+ ...
+ NotFound: Object: <Container name=>, name: 'unknown'
+
+However, if the request is a PUT request, we must generate a null resource:
+
+ >>> request.method = 'PUT'
+ >>> containerTraverser.publishTraverse(request, 'unknown')
+ <NullResource 'unknown'>
+
+However, a null resource is only created, if the current resource is the last
+one in the traversal stack:
+
+ >>> request.setTraversalStack(('sub11',))
+ >>> containerTraverser.publishTraverse(request, 'unknown')
+ Traceback (most recent call last):
+ ...
+ NotFound: Object: <Container name=>, name: 'unknown'
+
+And that's it.
Property changes on: z3c.rest/trunk/src/z3c/rest/traverser.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c/rest/twist.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/twist.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/twist.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# 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 server factories for Twisted
+
+Note: The file is not named twisted.py because of package import problems.
+
+$Id$
+"""
+from twisted.web2 import log, server, wsgi
+from twisted.web2.channel.http import HTTPFactory
+from zope.app.twisted import http
+from zope.app.twisted.server import ServerType
+from zope.app.wsgi import WSGIPublisherApplication
+from z3c.rest.rest import RESTPublicationRequestFactory
+
+def createRESTFactory(db):
+ resource = wsgi.WSGIResource(
+ WSGIPublisherApplication(db, RESTPublicationRequestFactory))
+ resource = log.LogWrapperResource(resource)
+ resource = http.Prebuffer(resource)
+
+ return HTTPFactory(server.Site(resource))
+
+rest = ServerType(createRESTFactory, 8081)
Property changes on: z3c.rest/trunk/src/z3c/rest/twist.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c/rest/zserver.py
===================================================================
--- z3c.rest/trunk/src/z3c/rest/zserver.py (rev 0)
+++ z3c.rest/trunk/src/z3c/rest/zserver.py 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# 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 server factories
+
+$Id$
+"""
+from zope.server.http.commonaccesslogger import CommonAccessLogger
+from zope.server.http import wsgihttpserver
+from zope.app.server.wsgi import ServerType
+from zope.app.wsgi import WSGIPublisherApplication
+from z3c.rest.rest import RESTPublicationRequestFactory
+
+rest = ServerType(wsgihttpserver.WSGIHTTPServer,
+ WSGIPublisherApplication,
+ CommonAccessLogger,
+ 8080, True,
+ RESTPublicationRequestFactory)
Property changes on: z3c.rest/trunk/src/z3c/rest/zserver.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.rest/trunk/src/z3c.rest.egg-info/PKG-INFO
===================================================================
--- z3c.rest/trunk/src/z3c.rest.egg-info/PKG-INFO (rev 0)
+++ z3c.rest/trunk/src/z3c.rest.egg-info/PKG-INFO 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,31 @@
+Metadata-Version: 1.0
+Name: z3c.rest
+Version: 1.5.1
+Summary: A REST Framework for Zope 3 Applications
+Home-page: http://cheeseshop.python.org/pypi/z3c.rest
+Author: Stephan Richter and the Zope Community
+Author-email: zope3-dev at zope.org
+License: ZPL 2.1
+Description: This package provides a framework to build REST APIs on top of Zope 3.
+
+
+ =======
+ CHANGES
+ =======
+
+ Version 0.1.0 (2007-12-??)
+ --------------------------
+
+ - Initial Release
+
+Keywords: zope3 form widget
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Zope Public License
+Classifier: Programming Language :: Python
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Topic :: Internet :: WWW/HTTP
+Classifier: Framework :: Zope3
Added: z3c.rest/trunk/src/z3c.rest.egg-info/SOURCES.txt
===================================================================
--- z3c.rest/trunk/src/z3c.rest.egg-info/SOURCES.txt (rev 0)
+++ z3c.rest/trunk/src/z3c.rest.egg-info/SOURCES.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,17 @@
+README.txt
+setup.py
+src/z3c/__init__.py
+src/z3c.rest.egg-info/PKG-INFO
+src/z3c.rest.egg-info/SOURCES.txt
+src/z3c.rest.egg-info/dependency_links.txt
+src/z3c.rest.egg-info/namespace_packages.txt
+src/z3c.rest.egg-info/not-zip-safe
+src/z3c.rest.egg-info/requires.txt
+src/z3c.rest.egg-info/top_level.txt
+src/z3c/rest/__init__.py
+src/z3c/rest/interfaces.py
+src/z3c/rest/rest.py
+src/z3c/rest/twist.py
+src/z3c/rest/zserver.py
+src/z3c/rest/tests/__init__.py
+src/z3c/rest/tests/folder.py
Property changes on: z3c.rest/trunk/src/z3c.rest.egg-info/SOURCES.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c.rest.egg-info/dependency_links.txt
===================================================================
--- z3c.rest/trunk/src/z3c.rest.egg-info/dependency_links.txt (rev 0)
+++ z3c.rest/trunk/src/z3c.rest.egg-info/dependency_links.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1 @@
+
Property changes on: z3c.rest/trunk/src/z3c.rest.egg-info/dependency_links.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c.rest.egg-info/namespace_packages.txt
===================================================================
--- z3c.rest/trunk/src/z3c.rest.egg-info/namespace_packages.txt (rev 0)
+++ z3c.rest/trunk/src/z3c.rest.egg-info/namespace_packages.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1 @@
+z3c
Property changes on: z3c.rest/trunk/src/z3c.rest.egg-info/namespace_packages.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c.rest.egg-info/not-zip-safe
===================================================================
--- z3c.rest/trunk/src/z3c.rest.egg-info/not-zip-safe (rev 0)
+++ z3c.rest/trunk/src/z3c.rest.egg-info/not-zip-safe 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1 @@
+
Added: z3c.rest/trunk/src/z3c.rest.egg-info/requires.txt
===================================================================
--- z3c.rest/trunk/src/z3c.rest.egg-info/requires.txt (rev 0)
+++ z3c.rest/trunk/src/z3c.rest.egg-info/requires.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1,24 @@
+lxml
+setuptools
+zope.publisher
+
+[test]
+z3c.coverage
+z3c.etestbrowser
+zope.app.testing
+
+[app]
+zope.app.appsetup
+zope.app.authentication
+zope.app.component
+zope.app.container
+zope.app.error
+zope.app.form
+zope.app.publisher
+zope.app.publication
+zope.app.security
+zope.app.securitypolicy
+zope.app.twisted
+zope.app.wsgi
+zope.app.zcmlfiles
+zope.contentprovider
\ No newline at end of file
Property changes on: z3c.rest/trunk/src/z3c.rest.egg-info/requires.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.rest/trunk/src/z3c.rest.egg-info/top_level.txt
===================================================================
--- z3c.rest/trunk/src/z3c.rest.egg-info/top_level.txt (rev 0)
+++ z3c.rest/trunk/src/z3c.rest.egg-info/top_level.txt 2007-11-30 11:40:12 UTC (rev 82045)
@@ -0,0 +1 @@
+z3c
Property changes on: z3c.rest/trunk/src/z3c.rest.egg-info/top_level.txt
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Checkins
mailing list