summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2013-03-10 16:59:20 -0700
committerAnas Nashif <anas.nashif@intel.com>2013-03-10 16:59:20 -0700
commit4dfc28666838e4e9049690a402199ac8a8ebf2a2 (patch)
tree77d46a9ba0b5eac46bc8201a7d7232a6d6c1f576
downloadpython-ZSI-4dfc28666838e4e9049690a402199ac8a8ebf2a2.tar.gz
python-ZSI-4dfc28666838e4e9049690a402199ac8a8ebf2a2.tar.bz2
python-ZSI-4dfc28666838e4e9049690a402199ac8a8ebf2a2.zip
Imported Upstream version 2.0.rc3upstream/2.0.rc3
-rw-r--r--.cvsignore5
-rw-r--r--CHANGES176
-rw-r--r--Copyright11
-rw-r--r--MANIFEST.in7
-rw-r--r--Makefile31
-rw-r--r--PKG-INFO10
-rw-r--r--README41
-rw-r--r--RELEASE163
-rw-r--r--ZSI.egg-info/PKG-INFO10
-rw-r--r--ZSI.egg-info/SOURCES.txt381
-rw-r--r--ZSI.egg-info/dependency_links.txt1
-rw-r--r--ZSI.egg-info/entry_points.txt4
-rw-r--r--ZSI.egg-info/requires.txt1
-rw-r--r--ZSI.egg-info/top_level.txt1
-rw-r--r--ZSI/.cvsignore2
-rw-r--r--ZSI/LBNLCopyright41
-rw-r--r--ZSI/ServiceContainer.py498
-rwxr-xr-xZSI/ServiceProxy.py305
-rw-r--r--ZSI/TC.py1807
-rw-r--r--ZSI/TCapache.py75
-rw-r--r--ZSI/TCcompound.py674
-rw-r--r--ZSI/TCnumbers.py184
-rw-r--r--ZSI/TCtimes.py314
-rw-r--r--ZSI/__init__.py433
-rw-r--r--ZSI/address.py331
-rw-r--r--ZSI/auth.py65
-rwxr-xr-xZSI/client.py541
-rw-r--r--ZSI/digest_auth.py105
-rw-r--r--ZSI/dispatch.py290
-rw-r--r--ZSI/fault.py262
-rw-r--r--ZSI/generate/__init__.py24
-rw-r--r--ZSI/generate/commands.py304
-rw-r--r--ZSI/generate/containers.py2869
-rw-r--r--ZSI/generate/pyclass.py285
-rw-r--r--ZSI/generate/utility.py159
-rwxr-xr-xZSI/generate/wsdl2dispatch.py531
-rw-r--r--ZSI/generate/wsdl2python.py536
-rw-r--r--ZSI/parse.py375
-rw-r--r--ZSI/resolvers.py147
-rw-r--r--ZSI/schema.py327
-rw-r--r--ZSI/twisted/WSresource.py355
-rw-r--r--ZSI/twisted/WSsecurity.py389
-rw-r--r--ZSI/twisted/__init__.py7
-rw-r--r--ZSI/twisted/client.py344
-rw-r--r--ZSI/typeinterpreter.py133
-rw-r--r--ZSI/version.py2
-rw-r--r--ZSI/writer.py190
-rw-r--r--ZSI/wstools/.cvsignore1
-rwxr-xr-xZSI/wstools/Namespaces.py194
-rwxr-xr-xZSI/wstools/TimeoutSocket.py179
-rw-r--r--ZSI/wstools/UserTuple.py99
-rwxr-xr-xZSI/wstools/Utility.py1376
-rwxr-xr-xZSI/wstools/WSDLTools.py1668
-rwxr-xr-xZSI/wstools/XMLSchema.py3081
-rw-r--r--ZSI/wstools/XMLname.py90
-rw-r--r--ZSI/wstools/ZPL61
-rw-r--r--ZSI/wstools/__init__.py9
-rwxr-xr-xZSI/wstools/c14n.py433
-rw-r--r--ZSI/wstools/license.txt44
-rw-r--r--ZSI/wstools/logging.py110
-rw-r--r--ZSI/wstools/test/.cvsignore1
-rw-r--r--ZSI/wstools/test/README52
-rw-r--r--ZSI/wstools/test/__init__.py5
-rw-r--r--ZSI/wstools/test/config.txt362
-rw-r--r--ZSI/wstools/test/schema.tar.gzbin0 -> 68874 bytes
-rw-r--r--ZSI/wstools/test/test_t1.py20
-rw-r--r--ZSI/wstools/test/test_wsdl.py160
-rw-r--r--ZSI/wstools/test/test_wstools.py37
-rw-r--r--ZSI/wstools/test/test_wstools_net.py20
-rw-r--r--ZSI/wstools/test/xmethods.tar.gzbin0 -> 15209 bytes
-rw-r--r--ZSI/zsi/ZSI/ServiceContainer.py498
-rwxr-xr-xZSI/zsi/ZSI/ServiceProxy.py305
-rw-r--r--ZSI/zsi/ZSI/TC.py1807
-rw-r--r--ZSI/zsi/ZSI/TCapache.py75
-rw-r--r--ZSI/zsi/ZSI/TCcompound.py674
-rw-r--r--ZSI/zsi/ZSI/TCnumbers.py184
-rw-r--r--ZSI/zsi/ZSI/TCtimes.py314
-rw-r--r--ZSI/zsi/ZSI/__init__.py433
-rw-r--r--ZSI/zsi/ZSI/address.py331
-rw-r--r--ZSI/zsi/ZSI/auth.py65
-rwxr-xr-xZSI/zsi/ZSI/client.py541
-rw-r--r--ZSI/zsi/ZSI/digest_auth.py105
-rw-r--r--ZSI/zsi/ZSI/dispatch.py290
-rw-r--r--ZSI/zsi/ZSI/fault.py262
-rw-r--r--ZSI/zsi/ZSI/generate/__init__.py24
-rw-r--r--ZSI/zsi/ZSI/generate/commands.py304
-rw-r--r--ZSI/zsi/ZSI/generate/containers.py2869
-rw-r--r--ZSI/zsi/ZSI/generate/pyclass.py285
-rw-r--r--ZSI/zsi/ZSI/generate/utility.py159
-rwxr-xr-xZSI/zsi/ZSI/generate/wsdl2dispatch.py531
-rw-r--r--ZSI/zsi/ZSI/generate/wsdl2python.py536
-rw-r--r--ZSI/zsi/ZSI/parse.py375
-rw-r--r--ZSI/zsi/ZSI/resolvers.py147
-rw-r--r--ZSI/zsi/ZSI/schema.py327
-rw-r--r--ZSI/zsi/ZSI/twisted/WSresource.py355
-rw-r--r--ZSI/zsi/ZSI/twisted/WSsecurity.py389
-rw-r--r--ZSI/zsi/ZSI/twisted/__init__.py7
-rw-r--r--ZSI/zsi/ZSI/twisted/client.py344
-rw-r--r--ZSI/zsi/ZSI/typeinterpreter.py133
-rw-r--r--ZSI/zsi/ZSI/version.py2
-rw-r--r--ZSI/zsi/ZSI/writer.py190
-rwxr-xr-xZSI/zsi/ZSI/wstools/Namespaces.py194
-rwxr-xr-xZSI/zsi/ZSI/wstools/TimeoutSocket.py179
-rw-r--r--ZSI/zsi/ZSI/wstools/UserTuple.py99
-rwxr-xr-xZSI/zsi/ZSI/wstools/Utility.py1376
-rwxr-xr-xZSI/zsi/ZSI/wstools/WSDLTools.py1668
-rwxr-xr-xZSI/zsi/ZSI/wstools/XMLSchema.py3081
-rw-r--r--ZSI/zsi/ZSI/wstools/XMLname.py90
-rw-r--r--ZSI/zsi/ZSI/wstools/__init__.py9
-rwxr-xr-xZSI/zsi/ZSI/wstools/c14n.py433
-rw-r--r--ZSI/zsi/ZSI/wstools/logging.py110
-rw-r--r--ZSI/zsi/ZSI/wstools/test/__init__.py5
-rw-r--r--ZSI/zsi/ZSI/wstools/test/test_t1.py20
-rw-r--r--ZSI/zsi/ZSI/wstools/test/test_wsdl.py160
-rw-r--r--ZSI/zsi/ZSI/wstools/test/test_wstools.py37
-rw-r--r--ZSI/zsi/ZSI/wstools/test/test_wstools_net.py20
-rw-r--r--ZSI/zsi/doc/examples/client/receive_response/complex/manual/ComplexTypes.py13
-rwxr-xr-xZSI/zsi/doc/examples/client/receive_response/complex/manual/client.py20
-rwxr-xr-xZSI/zsi/doc/examples/client/receive_response/complex/server/server.py21
-rwxr-xr-xZSI/zsi/doc/examples/client/receive_response/complex/wsdl2py/client.py21
-rwxr-xr-xZSI/zsi/doc/examples/client/receive_response/simple/ServiceProxy/client.py16
-rwxr-xr-xZSI/zsi/doc/examples/client/receive_response/simple/server/server.py15
-rwxr-xr-xZSI/zsi/doc/examples/client/receive_response/simple/wsdl2py/client.py23
-rw-r--r--ZSI/zsi/doc/examples/client/send_request/complex/manual/ComplexTypes.py13
-rwxr-xr-xZSI/zsi/doc/examples/client/send_request/complex/manual/client.py21
-rw-r--r--ZSI/zsi/doc/examples/client/send_request/complex/server/ComplexTypes.py13
-rwxr-xr-xZSI/zsi/doc/examples/client/send_request/complex/server/server.py17
-rwxr-xr-xZSI/zsi/doc/examples/client/send_request/complex/wsdl2py/client.py22
-rwxr-xr-xZSI/zsi/doc/examples/client/send_request/simple/Binding/client.py16
-rwxr-xr-xZSI/zsi/doc/examples/client/send_request/simple/ServiceProxy/client.py16
-rwxr-xr-xZSI/zsi/doc/examples/client/send_request/simple/server/server.py14
-rwxr-xr-xZSI/zsi/doc/examples/client/send_request/simple/wsdl2py/client.py23
-rw-r--r--ZSI/zsi/doc/examples/server/receive_request/complex/client/ComplexTypes.py13
-rwxr-xr-xZSI/zsi/doc/examples/server/receive_request/complex/client/client.py19
-rw-r--r--ZSI/zsi/doc/examples/server/receive_request/complex/manual/ComplexTypes.py13
-rwxr-xr-xZSI/zsi/doc/examples/server/receive_request/complex/manual/server.py13
-rwxr-xr-xZSI/zsi/doc/examples/server/receive_request/complex/wsdl2py/server.py13
-rwxr-xr-xZSI/zsi/doc/examples/server/receive_request/simple/CGI/cgi.py10
-rwxr-xr-xZSI/zsi/doc/examples/server/receive_request/simple/client/client.py17
-rw-r--r--ZSI/zsi/doc/examples/server/receive_request/simple/mod_python/MyHandler.py6
-rw-r--r--ZSI/zsi/doc/examples/server/receive_request/simple/mod_python/mod_python.py10
-rwxr-xr-xZSI/zsi/doc/examples/server/receive_request/simple/standalone/standalone.py15
-rwxr-xr-xZSI/zsi/doc/examples/server/send_response/complex/client/client.py21
-rw-r--r--ZSI/zsi/doc/examples/server/send_response/complex/manual/ComplexTypes.py14
-rwxr-xr-xZSI/zsi/doc/examples/server/send_response/complex/manual/server.py13
-rwxr-xr-xZSI/zsi/doc/examples/server/send_response/complex/wsdl2py/server.py21
-rwxr-xr-xZSI/zsi/doc/examples/server/send_response/simple/no_typecode/client.py17
-rwxr-xr-xZSI/zsi/doc/examples/server/send_response/simple/no_typecode/server.py11
-rwxr-xr-xZSI/zsi/doc/examples/server/send_response/simple/wsdl2py/client.py17
-rwxr-xr-xZSI/zsi/doc/examples/server/send_response/simple/wsdl2py/server.py13
-rwxr-xr-xZSI/zsi/interop/client.py102
-rwxr-xr-xZSI/zsi/interop/cpackets.py205
-rw-r--r--ZSI/zsi/interop/sclasses.py233
-rwxr-xr-xZSI/zsi/interop/server.py209
-rwxr-xr-xZSI/zsi/newver.py45
-rw-r--r--ZSI/zsi/samples/Echo/EchoClient.py17
-rw-r--r--ZSI/zsi/samples/Echo/EchoServer.py67
-rwxr-xr-xZSI/zsi/samples/poly.py121
-rwxr-xr-xZSI/zsi/setup.py68
-rwxr-xr-xZSI/zsi/test/cgicli.py75
-rw-r--r--ZSI/zsi/test/test_TCtimes.py143
-rw-r--r--ZSI/zsi/test/test_t1.py229
-rw-r--r--ZSI/zsi/test/test_t2.py104
-rw-r--r--ZSI/zsi/test/test_t3.py41
-rw-r--r--ZSI/zsi/test/test_t4.py62
-rw-r--r--ZSI/zsi/test/test_t5.py64
-rw-r--r--ZSI/zsi/test/test_t6.py154
-rw-r--r--ZSI/zsi/test/test_t7.py66
-rw-r--r--ZSI/zsi/test/test_t8.py163
-rw-r--r--ZSI/zsi/test/test_t9.py363
-rw-r--r--ZSI/zsi/test/test_zsi.py28
-rw-r--r--ZSI/zsi/test/test_zsi_net.py30
-rw-r--r--ZSI/zsi/test/tests_bad.py126
-rw-r--r--ZSI/zsi/test/tests_good.py88
-rw-r--r--ZSI/zsi/test/wsdl2py/ServiceTest.py418
-rwxr-xr-xZSI/zsi/test/wsdl2py/runTests.py100
-rwxr-xr-xZSI/zsi/test/wsdl2py/servers/EchoServer.py27
-rwxr-xr-xZSI/zsi/test/wsdl2py/servers/WhiteMesa.py53
-rwxr-xr-xZSI/zsi/test/wsdl2py/test_AWSECommerceService.py150
-rwxr-xr-xZSI/zsi/test/wsdl2py/test_AmazonS3.py82
-rw-r--r--ZSI/zsi/test/wsdl2py/test_Attributes.py178
-rw-r--r--ZSI/zsi/test/wsdl2py/test_Choice.py80
-rw-r--r--ZSI/zsi/test/wsdl2py/test_DerivedTypes.py236
-rw-r--r--ZSI/zsi/test/wsdl2py/test_Echo.py67
-rw-r--r--ZSI/zsi/test/wsdl2py/test_InfoBil.py57
-rw-r--r--ZSI/zsi/test/wsdl2py/test_MapPoint.py65
-rw-r--r--ZSI/zsi/test/wsdl2py/test_OpcDaGateway.py155
-rw-r--r--ZSI/zsi/test/wsdl2py/test_Sabre.py100
-rwxr-xr-xZSI/zsi/test/wsdl2py/test_TerraService.py244
-rwxr-xr-xZSI/zsi/test/wsdl2py/test_ThreatService.py68
-rwxr-xr-xZSI/zsi/test/wsdl2py/test_WhiteMesa.py77
-rwxr-xr-xZSI/zsi/test/wsdl2py/test_XMethodsQuery.py99
-rwxr-xr-xZSI/zsi/test/wsdl2py/test_ZipCodeResolver.py101
-rw-r--r--doc/.cvsignore6
-rw-r--r--doc/Makefile65
-rw-r--r--doc/app-dispatch-dict.tex23
-rw-r--r--doc/app-dispatch.tex231
-rw-r--r--doc/app-lowlevel.tex227
-rw-r--r--doc/app-pickler.tex212
-rw-r--r--doc/bibliography.tex5
-rw-r--r--doc/blank.pngbin0 -> 1031 bytes
-rw-r--r--doc/c01-intro.tex98
-rw-r--r--doc/c02-samples.tex275
-rw-r--r--doc/c02a-coverage.tex38
-rw-r--r--doc/c03-except.tex49
-rw-r--r--doc/c04-utils.tex128
-rw-r--r--doc/c05-parse.tex212
-rw-r--r--doc/c06-tc.tex783
-rw-r--r--doc/c07-writer.tex96
-rw-r--r--doc/c08-fault.tex135
-rw-r--r--doc/c09-resolve.tex103
-rw-r--r--doc/c10-dispatch.tex442
-rw-r--r--doc/client-hello-struct.tex13
-rw-r--r--doc/contents.pngbin0 -> 649 bytes
-rw-r--r--doc/dispatch-rpc-hello-array.tex21
-rw-r--r--doc/dispatch-rpc-hello-struct.tex22
-rw-r--r--doc/examples/client/receive_response/complex/binding.wsdl37
-rw-r--r--doc/examples/client/receive_response/complex/interface.wsdl31
-rw-r--r--doc/examples/client/receive_response/complex/manual/.cvsignore1
-rw-r--r--doc/examples/client/receive_response/complex/manual/ComplexTypes.py13
-rwxr-xr-xdoc/examples/client/receive_response/complex/manual/client.py20
-rw-r--r--doc/examples/client/receive_response/complex/server/.cvsignore1
-rwxr-xr-xdoc/examples/client/receive_response/complex/server/server.py21
-rw-r--r--doc/examples/client/receive_response/complex/types.xsd15
-rw-r--r--doc/examples/client/receive_response/complex/wsdl2py/.cvsignore1
-rwxr-xr-xdoc/examples/client/receive_response/complex/wsdl2py/client.py21
-rwxr-xr-xdoc/examples/client/receive_response/simple/ServiceProxy/client.py16
-rw-r--r--doc/examples/client/receive_response/simple/binding.wsdl38
-rw-r--r--doc/examples/client/receive_response/simple/interface.wsdl28
-rw-r--r--doc/examples/client/receive_response/simple/server/.cvsignore1
-rwxr-xr-xdoc/examples/client/receive_response/simple/server/server.py15
-rw-r--r--doc/examples/client/receive_response/simple/wsdl2py/.cvsignore1
-rw-r--r--doc/examples/client/receive_response/simple/wsdl2py/README3
-rwxr-xr-xdoc/examples/client/receive_response/simple/wsdl2py/client.py23
-rw-r--r--doc/examples/client/send_request/complex/binding.wsdl37
-rw-r--r--doc/examples/client/send_request/complex/interface.wsdl31
-rw-r--r--doc/examples/client/send_request/complex/manual/.cvsignore1
-rw-r--r--doc/examples/client/send_request/complex/manual/ComplexTypes.py13
-rwxr-xr-xdoc/examples/client/send_request/complex/manual/client.py21
-rw-r--r--doc/examples/client/send_request/complex/server/.cvsignore2
-rw-r--r--doc/examples/client/send_request/complex/server/ComplexTypes.py13
-rwxr-xr-xdoc/examples/client/send_request/complex/server/server.py17
-rw-r--r--doc/examples/client/send_request/complex/types.xsd15
-rw-r--r--doc/examples/client/send_request/complex/wsdl2py/.cvsignore1
-rwxr-xr-xdoc/examples/client/send_request/complex/wsdl2py/client.py22
-rwxr-xr-xdoc/examples/client/send_request/simple/Binding/client.py16
-rw-r--r--doc/examples/client/send_request/simple/README3
-rwxr-xr-xdoc/examples/client/send_request/simple/ServiceProxy/client.py16
-rw-r--r--doc/examples/client/send_request/simple/binding.wsdl38
-rw-r--r--doc/examples/client/send_request/simple/interface.wsdl28
-rw-r--r--doc/examples/client/send_request/simple/server/.cvsignore1
-rwxr-xr-xdoc/examples/client/send_request/simple/server/server.py14
-rw-r--r--doc/examples/client/send_request/simple/wsdl2py/.cvsignore1
-rw-r--r--doc/examples/client/send_request/simple/wsdl2py/README3
-rwxr-xr-xdoc/examples/client/send_request/simple/wsdl2py/client.py23
-rw-r--r--doc/examples/server/receive_request/complex/binding.wsdl37
-rw-r--r--doc/examples/server/receive_request/complex/client/.cvsignore1
-rw-r--r--doc/examples/server/receive_request/complex/client/ComplexTypes.py13
-rwxr-xr-xdoc/examples/server/receive_request/complex/client/client.py19
-rw-r--r--doc/examples/server/receive_request/complex/interface.wsdl31
-rw-r--r--doc/examples/server/receive_request/complex/manual/.cvsignore1
-rw-r--r--doc/examples/server/receive_request/complex/manual/ComplexTypes.py13
-rwxr-xr-xdoc/examples/server/receive_request/complex/manual/server.py13
-rw-r--r--doc/examples/server/receive_request/complex/types.xsd15
-rw-r--r--doc/examples/server/receive_request/complex/wsdl2py/.cvsignore1
-rwxr-xr-xdoc/examples/server/receive_request/complex/wsdl2py/server.py13
-rw-r--r--doc/examples/server/receive_request/simple/CGI/README3
-rwxr-xr-xdoc/examples/server/receive_request/simple/CGI/cgi.py10
-rw-r--r--doc/examples/server/receive_request/simple/binding.wsdl38
-rwxr-xr-xdoc/examples/server/receive_request/simple/client/client.py17
-rw-r--r--doc/examples/server/receive_request/simple/interface.wsdl28
-rw-r--r--doc/examples/server/receive_request/simple/mod_python/.cvsignore1
-rw-r--r--doc/examples/server/receive_request/simple/mod_python/MyHandler.py6
-rw-r--r--doc/examples/server/receive_request/simple/mod_python/README3
-rw-r--r--doc/examples/server/receive_request/simple/mod_python/mod_python.py10
-rw-r--r--doc/examples/server/receive_request/simple/standalone/.cvsignore1
-rw-r--r--doc/examples/server/receive_request/simple/standalone/README3
-rwxr-xr-xdoc/examples/server/receive_request/simple/standalone/standalone.py15
-rw-r--r--doc/examples/server/send_response/complex/binding.wsdl37
-rw-r--r--doc/examples/server/send_response/complex/client/.cvsignore1
-rwxr-xr-xdoc/examples/server/send_response/complex/client/client.py21
-rw-r--r--doc/examples/server/send_response/complex/interface.wsdl31
-rw-r--r--doc/examples/server/send_response/complex/manual/.cvsignore1
-rw-r--r--doc/examples/server/send_response/complex/manual/ComplexTypes.py14
-rwxr-xr-xdoc/examples/server/send_response/complex/manual/server.py13
-rw-r--r--doc/examples/server/send_response/complex/types.xsd15
-rw-r--r--doc/examples/server/send_response/complex/wsdl2py/.cvsignore1
-rwxr-xr-xdoc/examples/server/send_response/complex/wsdl2py/server.py21
-rw-r--r--doc/examples/server/send_response/simple/binding.wsdl38
-rw-r--r--doc/examples/server/send_response/simple/interface.wsdl28
-rwxr-xr-xdoc/examples/server/send_response/simple/no_typecode/client.py17
-rwxr-xr-xdoc/examples/server/send_response/simple/no_typecode/server.py11
-rw-r--r--doc/examples/server/send_response/simple/wsdl2py/.cvsignore1
-rw-r--r--doc/examples/server/send_response/simple/wsdl2py/README4
-rwxr-xr-xdoc/examples/server/send_response/simple/wsdl2py/client.py17
-rwxr-xr-xdoc/examples/server/send_response/simple/wsdl2py/server.py13
-rw-r--r--doc/guide.css243
-rw-r--r--doc/guide.html814
-rw-r--r--doc/guide.pdfbin0 -> 135004 bytes
-rw-r--r--doc/guide.ps874
-rw-r--r--doc/guide.tex93
-rw-r--r--doc/guide01-intro.tex27
-rwxr-xr-xdoc/guide02-wsdl2py.tex365
-rw-r--r--doc/index.pngbin0 -> 529 bytes
-rw-r--r--doc/modules.pngbin0 -> 598 bytes
-rw-r--r--doc/next.pngbin0 -> 511 bytes
-rw-r--r--doc/previous.pngbin0 -> 511 bytes
-rw-r--r--doc/pyfav.pngbin0 -> 240 bytes
-rw-r--r--doc/up.pngbin0 -> 577 bytes
-rw-r--r--doc/version.tex4
-rw-r--r--doc/zsi.css243
-rw-r--r--doc/zsi.html5024
-rw-r--r--doc/zsi.l2h~797
-rw-r--r--doc/zsi.pdfbin0 -> 294028 bytes
-rw-r--r--doc/zsi.ps3495
-rw-r--r--doc/zsi.tex110
-rw-r--r--doc/zsi.xsd90
-rw-r--r--interop/.cvsignore4
-rw-r--r--interop/InteropTest.wsdl314
-rw-r--r--interop/InteropTestB.wsdl173
-rw-r--r--interop/README29
-rwxr-xr-xinterop/client.py102
-rwxr-xr-xinterop/cpackets.py205
-rw-r--r--interop/echoHeaderBindings.wsdl47
-rw-r--r--interop/sclasses.py233
-rwxr-xr-xinterop/server.py209
-rwxr-xr-xinterop/start16
-rwxr-xr-xnewver.py45
-rw-r--r--samples/Echo/Echo.wsdl63
-rw-r--r--samples/Echo/EchoClient.py17
-rw-r--r--samples/Echo/EchoServer.py67
-rw-r--r--samples/Echo/README144
-rw-r--r--samples/Echo/buildms.bat3
-rw-r--r--samples/Echo/echo.cs20
-rw-r--r--samples/README3
-rwxr-xr-xsamples/poly.py121
-rw-r--r--scripts/wsdl2dispatch9
-rw-r--r--scripts/wsdl2py9
-rw-r--r--setup.cfg16
-rwxr-xr-xsetup.py68
-rw-r--r--test/.cvsignore1
-rw-r--r--test/README10
-rwxr-xr-xtest/cgicli.py75
-rw-r--r--test/test_TCtimes.py143
-rw-r--r--test/test_t1.py229
-rw-r--r--test/test_t2.py104
-rw-r--r--test/test_t3.py41
-rw-r--r--test/test_t4.py62
-rw-r--r--test/test_t5.py64
-rw-r--r--test/test_t6.py154
-rw-r--r--test/test_t7.py66
-rw-r--r--test/test_t8.py163
-rw-r--r--test/test_t9.py363
-rw-r--r--test/test_zsi.py28
-rw-r--r--test/test_zsi_net.py30
-rw-r--r--test/tests_bad.py126
-rw-r--r--test/tests_good.py88
-rw-r--r--test/wsdl2py/.cvsignore1
-rw-r--r--test/wsdl2py/README118
-rw-r--r--test/wsdl2py/ServiceTest.py418
-rw-r--r--test/wsdl2py/config.txt157
-rwxr-xr-xtest/wsdl2py/runTests.py100
-rwxr-xr-xtest/wsdl2py/servers/EchoServer.py27
-rwxr-xr-xtest/wsdl2py/servers/WhiteMesa.py53
-rwxr-xr-xtest/wsdl2py/test_AWSECommerceService.py150
-rwxr-xr-xtest/wsdl2py/test_AmazonS3.py82
-rw-r--r--test/wsdl2py/test_Attributes.py178
-rw-r--r--test/wsdl2py/test_Attributes.xsd43
-rw-r--r--test/wsdl2py/test_Choice.py80
-rw-r--r--test/wsdl2py/test_Choice.xsd25
-rw-r--r--test/wsdl2py/test_DerivedTypes.py236
-rw-r--r--test/wsdl2py/test_DerivedTypes.xsd55
-rw-r--r--test/wsdl2py/test_Echo.py67
-rw-r--r--test/wsdl2py/test_InfoBil.py57
-rw-r--r--test/wsdl2py/test_MapPoint.py65
-rw-r--r--test/wsdl2py/test_OpcDaGateway.py155
-rw-r--r--test/wsdl2py/test_Sabre.py100
-rwxr-xr-xtest/wsdl2py/test_TerraService.py244
-rwxr-xr-xtest/wsdl2py/test_ThreatService.py68
-rwxr-xr-xtest/wsdl2py/test_WhiteMesa.py77
-rwxr-xr-xtest/wsdl2py/test_XMethodsQuery.py99
-rwxr-xr-xtest/wsdl2py/test_ZipCodeResolver.py101
382 files changed, 70751 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 0000000..4c9c3cf
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,5 @@
+MANIFEST
+dist
+build
+*.pyc
+.project
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..d84f36f
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,176 @@
+Change for xxx released xxx:
+ - Make XMLSchema.py work in cases where threading isn't built in
+ - Add client-side Cookie Support (Jorgen Frojk Kjaersgaard)
+ - For cookies, getheaders() is Python 2.3; use getallmatchingheaders
+ - In SoapWriter, put nsdecls on body, not envelope
+ - Record facets (restrictions) in XMLSchema.py <vchen@datapower.com>
+ - Remove Send()'s kwargs out of _args list <efrain@bogotron.net>
+
+Change for 2.0.0rc3 released xxx:
+ - Updated ZSI developers guide
+ - Added ZSI wsdl2py users guide
+ - Added support for setuptools in setup script. If setuptools is installed
+ use it, else revert to distutils.
+ - Removed "requestclass" keyword argument to Binding.Send
+ - simplified and retooled Binding/NamedParamBinding and dispatch, added
+ "typesmodule" back into Binding. Now it's mirror image of dispatch.
+ - Microseconds to TCtime
+ - BUG [ 1525567 ] Amazon ECommerce Issues: local element declarations
+ overriding global element declarations with the same name within the
+ same namespace.
+ - new module "schema", contains "all" the code for dealing with global
+ Schema instance.
+
+Change for 2.0.0rc2 released 28-March-2006:
+ - Replace many/most id() with _get_idstr() to hide negative numbers
+ - Added ZSI.twisted package w/client and server, requires Python 2.4
+ - If Python >= 2.4, build/install ZSI.twisted package
+ - Add Typecode.typed to control xsi:type data system-wide
+ - Replace many/most id() with _get_idstr() to hide negative numbers
+
+Changes for 1.7 released 16-Feb-2005:
+ - Add support for jonpy (http://jonpy.sourceforge.net) FastCGI
+ courtesy of Guan Yang
+ - Avoid FutureWarning with Python 2.3.x
+ - Make sure generated ID values are legal IDs under Python 2.3
+ and newer
+ - Don't need _textunprotect (via Grahame Bowland)
+ - Fix ZSI.wstools.XMLname.toXMLname() so namespace prefix isn't lost
+
+Changes for 1.5, released 10-Mar-2004:
+ - TypeCode honoring for response parameters
+ - String adherence to wsdl for request/response params via ServiceProxy
+ - wsdl2py bug fixes
+ - Numerous bug fixes
+
+Changes for 1.4.1 released 11-Nov-2003:
+ - Make "docstyle" work for returning data, too (Alexis Marrero-Narvaez)
+ - TC.Struct.typed will output xsi:type parameter now.
+ - Numerous bug fixes
+
+Changes for 1.4, released 09-Sep-2003:
+ - Ouput XML prolog in SoapWriter
+ - Added nsdict parameter to dispatch.AsCGI and dispatch.AsServer
+ - Fixed bug where xmlns attribute was included in closing elements
+ - Added support for "wrapped" complexTypes in response messages
+ - TypeCode enabled classes now support parameters in the constructor
+ - Automatic parsing of ComplexTypes in response messages
+ - WSDL Parsing and operation invocation via ServiceProxy
+ - Tuple returned from dates and times (instead of a list)
+ - Arrays of ComplexTypes can now be deserialized
+ - User can specify which module contains complex type definitions
+ - Mod_Python support for dispatch to multiple functions within a module
+ - In client.py, try to get port from URL if available (Wichert Akkerman)
+ - Add ZSIException as parent for ZSI exception classes, FaultException
+ class, and raise that if we get unexpected fault in client (Wichert)
+ - Add auth_header keyword param to client Send method (Phillip Pearson)
+ - Added support for conversion between WSDL and Python classes
+
+Changes for 1.3, released xx-May-2002:
+ - Fixed parsing bug evidenced by dW article
+ - Style: use defaulted parameters not kw.get()
+ - Style: avoid __dict__ and use standard attribute fetch
+ - Fix dispatch to actually dispatch (Laroche Denis)
+ - Fix some typo's (thanks adalke)
+ - In TCCompound, include class name in exception, not just object name
+ - ZSI homepage is at pywebsvcs.sf.net now, not www.zolera.com (sigh...)
+ - Add nsdict parameter to dispatch's AsServer()
+
+Changes for 1.2, released 05-Mar-2002:
+ - Replace with X copyright; GNU compatible now
+ - newver writes date into version.tex
+ - Use "raise x(a)" always, never "raise x, a" (that's oldschool:)
+ - Don't delete tb in FaultFromException; add try/except for robustness
+ - Ignore -1 values for DST, etc., in Python time tuples
+ - Remove needless __init__ when just calling parent __init__
+ - Added 'undeclared' parameter to TC.Array
+ - Added 'repeatable' parameter to TC.TypeCode
+ - Add 'aname' parameter to TC.Typecode
+ - Move 'unique' keyword from TC.String up to TC.TypeCode
+ - Add 'wrapped' keyword to TC.XML
+ - Added _find_attr lambda
+ - Had path backwards in backtrace, add [1] to uniqify when needed
+ - Complain if extra elements in TC.Struct and hasextras=0
+ - Duration.lex_pattern was wrong (old schema) <grahamd@dscpl.com.au>
+ - Newline after output gDateTime, not a space
+ - Newline before Base64string
+ - Incorrect test in RegisterType <grahamd@dscpl.com.au>
+ - Add Boolean.serialize <grahamd@dscpl.com.au>
+ - Anchor all TCtime lex_pattern's <grahamd@dscpl.com.au>
+ - Allow class object (not just class name) in TC.seriallist
+ - TC.Void can serialize None (useful for TC.Any)
+ - RegisterType(TC.Void) <grahamd@dscpl.com.au>
+ - Fix consistency and interop in dispatching <grahamd@dscpl.com.au>
+ - Array interop fix <gisle@activestate.com>
+ - SOAPAction header needs quotes <gisle@activestate.com>
+ - Add Apache SOAPArray <gisle@activestate.com>
+ - Properly use 'ns' in client to set default namespace
+ - Convert client.py to use "new" HTTP objects
+ - Sign isn't optional on numeric timezones in dates <grahamd@dscpl.com.au>
+ - Document limits on date/time conversions.
+ - Add TC.XML.copyit and 'copyit' keyword to constructor
+ - Spelt EvaluateException wrong in dispatch.py <grahamd@dscpl.com.au>
+ - xsi:nil not xsi:null in TC.Void <grahamd@dscpl.com.au>
+ - Address issues with client/dispatch and None <grahamd@dscpl.com.au>
+ - Use formatted output, not strftime
+ - TC.Any defaults to optional if not set, for None <grahamd@dscpl.com.au>
+ - TC.Any tries to serialize its datatype as its tagname
+ - Add 'typed' support to TC.Any
+ - Allow keyword args in Fault.AsSOAP, passed to SoapWriter ctor
+ - SoapWriter's self.memo always uses id() (even for strings)
+ - Add SoapWriter.Forget()
+ - Add 'envelope' and 'encoding' keywords to SoapWriter
+ - Added 'mutable' parameter to TC.Struct
+ - TC.oname now properly defaults to TC.pname
+ - Remove "None" if it was second arg to dictionary get() method
+ - Add 'format' parameter to TC.Decimal and TC.Integer
+ - Fix TC.Decimal to work where float('INF') doesn't
+ - Fix TC.Decimal to handle NaN more portably <grahamd@dscpl.com.au>
+ - Add samples directory
+ - Binding() omits typing from the outermost RPC wrapper
+ - Fix HTML docs to include the ZSI schema
+ - Add readerclass parameter to client Binding
+ - Ignore <> around Content-ID in resolver; fix test to have it
+ - Docfix for Placer.typecode (paul@prescod.net)
+ - TC.Any will call pyobj's typecode if attr exists (paul@prescod.net)
+ - Binding replytype is optional; standards replytype not replyclass
+ - Document other tests to-do in interop/README
+ - Document ZSI typecode naming better.
+ - Add 'docstyle' to AsServer
+ - Handle HTTP 100 responses (httplib should...) (aspinei@internal.metawings.com)
+
+Changes for 1.1, released 14-Sep-2001:
+ - Works with PyXML0.6 now!
+ - Create this CHANGES file, include it in doc files
+ - Lots of editing and new material in the documentation
+ - Fix TC.String to handle empty strings
+ - Renamed NodeBacktrace to _backtrace
+ - Renamed HasValidEncoding to _valid_encoding
+ - Add 'textprotect' keyword argument to TC.String
+ - TC.Any() can now serialize dictionaries, lists, tuples
+ - TC.Any() can now parse arrays
+ - Add aslist to TC.Any()
+ - Add TC.Apache.Map typecode
+ - GetMyHeaderElements includes header with no actor attribute
+ - Fix formatting of error message when TC.Struct caught eval exception
+ - Fix TC.Struct for case when all kids are optional (could-be-zero-sized)
+ - Fix SimpleHREF calling sequence; boolean, Gregorian, duration now work
+ - Fix typo when generating backtrace on EvaluateException
+ - Exception backtraces are now in XPath syntax
+ - Fix zsi.xsd to conform to proper XSD-Rec style
+ - Fix zsi.xsd nits found by free IBM schema quality checker
+ - Added Z:BasicAuth to zsi.xsd
+ - CIDResolver now takes an optional "next" parameter, instead of creating
+ a NetworkResolver automatically; the "prefixes" parameter is gone
+ from the CIDResolver constructor
+ - Rename CIDResolver to MIMEResolver, and add Content-Location handling
+ - Changed resolver.seekable default from 1 to 0
+ - Resolvers raise EvaluateException, not TypeError
+ - Fix FindLocalHREF to search the serialization root, too
+ - Use and prefer Fault.AsSOAP, although Fault.AsSoap still exists
+ - Change FindLocalHREF to cache all id's as it finds them
+ - Add boolean, decimal, and hexbinary to interop server
+ - Add simple CGI dispatching
+ - Put version in setup.cfg and make newver use it for version.{py,tex}
+ - Add ZSI.Version() to retrieve version tuple
+ - Use len(_children(elt)) not elt.hasChildNodes()
diff --git a/Copyright b/Copyright
new file mode 100644
index 0000000..77d9bad
--- /dev/null
+++ b/Copyright
@@ -0,0 +1,11 @@
+Ugh. I hate paperwork. :)
+
+Most of ZSI is covered by an MIT/X Consortium copyright, as found
+in doc/zsi.tex.
+
+The wsdl2python tools are covered by an LBNL BSD-style license, as found
+in ZSI/LBNLCopyright.
+
+Most of the code in wstools is covered by the Zope Public License,
+which is a certified open source license that is GPL compatible.
+See ZSI/wstools/ZPL for details.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..4c46169
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,7 @@
+include CHANGES
+recursive-include ZSI *.py
+recursive-include scripts *
+recursive-include doc *
+recursive-include samples *
+recursive-include test *.py README
+recursive-include interop *.py README *.wsdl
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..95a8dc7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,31 @@
+#
+
+# Change this to point to your Python2 exectuable
+PYTHON = python
+
+# This should be the Python that generated RPMs depend on and install
+# into; this may be different from the Python used to build the RPM.
+RPM_TARGET_PYTHON=/usr/bin/python
+
+
+all: rpm
+
+ins:
+ soup $(PYTHON) setup.py install
+
+kit: doc rpm # make a kit
+
+rpm: ZSI/version.py
+ rm -f dist/*
+ $(PYTHON) setup.py bdist_rpm --python=$(RPM_TARGET_PYTHON)
+
+doc: doc/version.tex # build the docs
+ $(MAKE) -C doc
+
+ver: # update the build number
+ $(PYTHON) newver.py --incr
+
+.PHONY: all ins kit rpm doc ver
+
+ZSI/version.py doc/version.tex: setup.cfg
+ $(PYTHON) newver.py
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..8772ae1
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: ZSI
+Version: 2.0-rc3
+Summary: Zolera SOAP Infrastructure
+Home-page: http://pywebsvcs.sf.net/
+Author: Rich Salz, et al
+Author-email: pywebsvcs-talk@lists.sf.net
+License: Python
+Description: For additional information, please see http://pywebsvcs.sf.net/
+Platform: UNKNOWN
diff --git a/README b/README
new file mode 100644
index 0000000..1cb4130
--- /dev/null
+++ b/README
@@ -0,0 +1,41 @@
+==============================
+The Zolera SOAP Infrastructure
+==============================
+
+ZSI, the Zolera SOAP Infrastructure, is a pure-Python module that
+provides an implementation of SOAP messaging, as described in SOAP 1.1
+Specification (see http://www.w3.org/TR/soap). It can also be used to
+build applications using SOAP Messages with Attachments (see
+http://www.w3.org/TR/SOAP-attachments). ZSI is intended to make it
+easier to write web services in Python.
+
+In particular, ZSI parses and generates SOAP messages, and converts
+between native Python datatypes and SOAP syntax. Simple dispatch and
+invocation methods are supported. There are no known bugs. It's only
+known limitation is that it cannot handle multi-dimensional arrays.
+
+ZSI is built on top of DOM. It requires Python 2.3 or later, and PyXML
+0.8.3 or later. It is open source. We hope you find it useful.
+
+The ZSI.twisted package will only be built if you're using Python >= 2.4,
+and in order to use it you'll need twisted >= 2.0 and twistedWeb >= 0.5.0
+
+
+The documentation (in PDF and HTML) is accurate. We should probably
+restructure the document as a HOWTO. You probably can't usefully edit
+the docs without having the Python2.0 sources and some other utilities
+(TeX, pdfLaTeX, latex2html) on a Unix or Cygwin installation. If you
+want to format or revise the docs, see "Documentation Tools" in the
+file RELEASE.
+
+ /rich $alz
+ rsalz@datapower.com
+
+Things To Do
+------------
+
+Any volunteers?
+
+- Use isinstance() for types.
+
+- Update to SOAPv1.2.
diff --git a/RELEASE b/RELEASE
new file mode 100644
index 0000000..6708dc6
--- /dev/null
+++ b/RELEASE
@@ -0,0 +1,163 @@
+====================
+Making a ZSI Release
+====================
+
+The following instructions tell how to go about making a ZSI release.
+The Makefile automates many of the more mechanical aspects of the
+process.
+
+The documentation is built using the same documentation tools used for
+the standard Python documentation. See `Documentation Tools`_ for
+information on getting and setting up those tools.
+
+1. Announce your intent to make a release on the mailing list. This
+ gives folks a day or so to respond with info or last-minute
+ checkins.
+
+2. Edit setup.cfg to set the version number in the ``[version]``
+ section, if appropriate. If you change the version number, set
+ the ``release`` setting in the ``[bdist_rpm]`` section to 1.
+
+3. Make sure CHANGES is up to date, including the version number at
+ the top.
+
+4. Make sure the documentation is up to date. Try all of the examples,
+ and make sure they work. For a release candidate, the docs do not
+ have to be up to date, but we must at least mark the parts of the
+ docs that are no longer accurate. For a final release, the docs
+ *must* be up to date with the code.
+
+5. Commit all changes to SVN.
+
+6. Run all the tests; they must all pass. See `Running Tests`_ for
+ details.
+
+7. Tag the SVN repository. Release tags are of the form::
+
+ "v" MAJOR "_" MINOR [ "_" PATCHLEVEL ] [ modifier serial ]
+
+ where the optional segment containing PATCHLEVEL is included only
+ if PATCHLEVEL is non-zero. The modifier segment has only been
+ used for release candidates (so far), for which the modifier is
+ "RC" and the serial is the number of the candidate for this
+ particular software version ("1", "2", etc.).
+
+ You'll need to tag the ``wstools`` package separately, but using
+ the same tag.
+
+8. Export the tagged sources from CVS. You'll need to run the
+ commands::
+
+ cvs -d cvs.sourceforge.net:/cvsroot/pywebsvcs export -r TAG zsi
+ cd zsi/ZSI/
+ cvs -d cvs.sourceforge.net:/cvsroot/pywebsvcs export -r TAG wstools
+
+9. Build the release from the export. Switch to the export directory
+ (the one containing setup.py) and run **make kit**.
+
+10. Rename the PDF documentation. If the patchlevel from setug.cfg is
+ 0, copy doc/zsi.pdf to dist/zsi-$MAJOR.$MINOR.pdf, otherwise copy
+ it to dist/zsi-$MAJOR.$MINOR.$PATCHLEVEL.pdf. This will leave you
+ with all the files you need to upload in a single directory
+ (dist/).
+
+11. Using the constructed distribution (usually the .tar.gz version),
+ build and test the software; they must all pass. See `Running
+ Tests`_ for details.
+
+12. Use the SourceForge tools to create a release and upload the
+ files. If appropriate, hide obsolete releases. Hiding old
+ releases is normally only done if the older release contains
+ security bugs or other critical bugs that render it unusable.
+
+13. The web pages are in pywebsvcs.sourceforge.net, at the same level
+ as the zsi directory. Copy zsi.html from zsi/doc into there.
+ Update index.html to refer (and link) to the new release. Commit
+ those changes. The cron job will update it every couple of hours.
+
+14. Announce the release:
+
+ - Freshmeat. The details of how to do this have been lost to the
+ travesties of time, but there is a ZSI package there.
+
+ - SourceForge.net news: Create a news item in the project
+ announcing the release.
+
+ - Register the release with the Python Package Index (PyPI). This
+ can be done using **python setup.py register**.
+
+ - comp.lang.python.announce
+
+
+Documentation Tools
+-------------------
+
+The Python documentation tools include a script (**mkhowto**) that can
+be used on any Unix or Cygwin installation that provides all the
+required formatting tools. The recommended way to use them is to
+create a symbolic link to **mkhowto** from some directory on your
+PATH.
+
+The Python documentation tools can be acquired by any of these three
+methods:
+
+- Get a Python source release and unpack it. Create the symlink to
+ .../Python-VERSION/Doc/tools/mkhowto.
+
+- Get the LaTeX version of the documentation package and unpack it.
+ Create the symlink to .../Python-Doc-VERSION/tools/mkhowto.
+
+- Get them from CVS. If you have an existing Python checkout, you can
+ symlink to the .../Doc/tools/mkhowto script in that. If not, then
+ you only need to check out the Doc/ tree::
+
+ cvs -d :pserver:anonymous@cvs.sourceforge.net:/cvsroot/python \
+ checkout -d someplace python/dist/src/Doc
+
+ The symlink can then be made to .../someplace/tools/mkhowto.
+
+If you want to skip the symlink, you can specify the MKHOWTO variable
+when you run **make kit**::
+
+ make MKHOWTO=.../Doc/tools/mkhowto kit
+
+If you're working from unpacked Python sources or a complete Python
+checkout, you can just point to that instead of typing the full path
+to the **mkhowto** script::
+
+ make PYSRCDIR=...
+
+
+Running Tests
+-------------
+
+Before a release, all the tests must run to completion and pass. This
+entails running several scripts:
+
+- test/test_zsi.py
+
+- test/test_zsi_net.py
+
+- test/wsdl2py/runTests.py
+
+This is most easily done by setting the PYTHONPATH environment
+variable to point to the directory containing the version of the ZSI
+package that you want to test.
+
+Before testing, build the software::
+
+ python setup.py build
+
+Set PYTHONPATH to point to the built package::
+
+ PYTHONPATH=`pwd`/build/lib
+
+The first two scripts can easily be run from any directory; once your
+PYTHONPATH is set, just run the scripts::
+
+ python test/test_zsi.py
+
+ python test/test_zsi_net.py
+
+The third script depends on a configuration file; see the
+test/wsdl2py/README file for more information on running these tests.
diff --git a/ZSI.egg-info/PKG-INFO b/ZSI.egg-info/PKG-INFO
new file mode 100644
index 0000000..8772ae1
--- /dev/null
+++ b/ZSI.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: ZSI
+Version: 2.0-rc3
+Summary: Zolera SOAP Infrastructure
+Home-page: http://pywebsvcs.sf.net/
+Author: Rich Salz, et al
+Author-email: pywebsvcs-talk@lists.sf.net
+License: Python
+Description: For additional information, please see http://pywebsvcs.sf.net/
+Platform: UNKNOWN
diff --git a/ZSI.egg-info/SOURCES.txt b/ZSI.egg-info/SOURCES.txt
new file mode 100644
index 0000000..f3e5eea
--- /dev/null
+++ b/ZSI.egg-info/SOURCES.txt
@@ -0,0 +1,381 @@
+.cvsignore
+CHANGES
+Copyright
+MANIFEST.in
+Makefile
+README
+RELEASE
+newver.py
+setup.cfg
+setup.py
+ZSI/.cvsignore
+ZSI/LBNLCopyright
+ZSI/ServiceContainer.py
+ZSI/ServiceProxy.py
+ZSI/TC.py
+ZSI/TCapache.py
+ZSI/TCcompound.py
+ZSI/TCnumbers.py
+ZSI/TCtimes.py
+ZSI/__init__.py
+ZSI/address.py
+ZSI/auth.py
+ZSI/client.py
+ZSI/digest_auth.py
+ZSI/dispatch.py
+ZSI/fault.py
+ZSI/parse.py
+ZSI/resolvers.py
+ZSI/schema.py
+ZSI/typeinterpreter.py
+ZSI/version.py
+ZSI/writer.py
+ZSI.egg-info/PKG-INFO
+ZSI.egg-info/SOURCES.txt
+ZSI.egg-info/dependency_links.txt
+ZSI.egg-info/entry_points.txt
+ZSI.egg-info/requires.txt
+ZSI.egg-info/top_level.txt
+ZSI/generate/__init__.py
+ZSI/generate/commands.py
+ZSI/generate/containers.py
+ZSI/generate/pyclass.py
+ZSI/generate/utility.py
+ZSI/generate/wsdl2dispatch.py
+ZSI/generate/wsdl2python.py
+ZSI/twisted/WSresource.py
+ZSI/twisted/WSsecurity.py
+ZSI/twisted/__init__.py
+ZSI/twisted/client.py
+ZSI/wstools/.cvsignore
+ZSI/wstools/Namespaces.py
+ZSI/wstools/TimeoutSocket.py
+ZSI/wstools/UserTuple.py
+ZSI/wstools/Utility.py
+ZSI/wstools/WSDLTools.py
+ZSI/wstools/XMLSchema.py
+ZSI/wstools/XMLname.py
+ZSI/wstools/ZPL
+ZSI/wstools/__init__.py
+ZSI/wstools/c14n.py
+ZSI/wstools/license.txt
+ZSI/wstools/logging.py
+ZSI/wstools/test/.cvsignore
+ZSI/wstools/test/README
+ZSI/wstools/test/__init__.py
+ZSI/wstools/test/config.txt
+ZSI/wstools/test/schema.tar.gz
+ZSI/wstools/test/test_t1.py
+ZSI/wstools/test/test_wsdl.py
+ZSI/wstools/test/test_wstools.py
+ZSI/wstools/test/test_wstools_net.py
+ZSI/wstools/test/xmethods.tar.gz
+ZSI/zsi/newver.py
+ZSI/zsi/setup.py
+ZSI/zsi/ZSI/ServiceContainer.py
+ZSI/zsi/ZSI/ServiceProxy.py
+ZSI/zsi/ZSI/TC.py
+ZSI/zsi/ZSI/TCapache.py
+ZSI/zsi/ZSI/TCcompound.py
+ZSI/zsi/ZSI/TCnumbers.py
+ZSI/zsi/ZSI/TCtimes.py
+ZSI/zsi/ZSI/__init__.py
+ZSI/zsi/ZSI/address.py
+ZSI/zsi/ZSI/auth.py
+ZSI/zsi/ZSI/client.py
+ZSI/zsi/ZSI/digest_auth.py
+ZSI/zsi/ZSI/dispatch.py
+ZSI/zsi/ZSI/fault.py
+ZSI/zsi/ZSI/parse.py
+ZSI/zsi/ZSI/resolvers.py
+ZSI/zsi/ZSI/schema.py
+ZSI/zsi/ZSI/typeinterpreter.py
+ZSI/zsi/ZSI/version.py
+ZSI/zsi/ZSI/writer.py
+ZSI/zsi/ZSI/generate/__init__.py
+ZSI/zsi/ZSI/generate/commands.py
+ZSI/zsi/ZSI/generate/containers.py
+ZSI/zsi/ZSI/generate/pyclass.py
+ZSI/zsi/ZSI/generate/utility.py
+ZSI/zsi/ZSI/generate/wsdl2dispatch.py
+ZSI/zsi/ZSI/generate/wsdl2python.py
+ZSI/zsi/ZSI/twisted/WSresource.py
+ZSI/zsi/ZSI/twisted/WSsecurity.py
+ZSI/zsi/ZSI/twisted/__init__.py
+ZSI/zsi/ZSI/twisted/client.py
+ZSI/zsi/ZSI/wstools/Namespaces.py
+ZSI/zsi/ZSI/wstools/TimeoutSocket.py
+ZSI/zsi/ZSI/wstools/UserTuple.py
+ZSI/zsi/ZSI/wstools/Utility.py
+ZSI/zsi/ZSI/wstools/WSDLTools.py
+ZSI/zsi/ZSI/wstools/XMLSchema.py
+ZSI/zsi/ZSI/wstools/XMLname.py
+ZSI/zsi/ZSI/wstools/__init__.py
+ZSI/zsi/ZSI/wstools/c14n.py
+ZSI/zsi/ZSI/wstools/logging.py
+ZSI/zsi/ZSI/wstools/test/__init__.py
+ZSI/zsi/ZSI/wstools/test/test_t1.py
+ZSI/zsi/ZSI/wstools/test/test_wsdl.py
+ZSI/zsi/ZSI/wstools/test/test_wstools.py
+ZSI/zsi/ZSI/wstools/test/test_wstools_net.py
+ZSI/zsi/doc/examples/client/receive_response/complex/manual/ComplexTypes.py
+ZSI/zsi/doc/examples/client/receive_response/complex/manual/client.py
+ZSI/zsi/doc/examples/client/receive_response/complex/server/server.py
+ZSI/zsi/doc/examples/client/receive_response/complex/wsdl2py/client.py
+ZSI/zsi/doc/examples/client/receive_response/simple/ServiceProxy/client.py
+ZSI/zsi/doc/examples/client/receive_response/simple/server/server.py
+ZSI/zsi/doc/examples/client/receive_response/simple/wsdl2py/client.py
+ZSI/zsi/doc/examples/client/send_request/complex/manual/ComplexTypes.py
+ZSI/zsi/doc/examples/client/send_request/complex/manual/client.py
+ZSI/zsi/doc/examples/client/send_request/complex/server/ComplexTypes.py
+ZSI/zsi/doc/examples/client/send_request/complex/server/server.py
+ZSI/zsi/doc/examples/client/send_request/complex/wsdl2py/client.py
+ZSI/zsi/doc/examples/client/send_request/simple/Binding/client.py
+ZSI/zsi/doc/examples/client/send_request/simple/ServiceProxy/client.py
+ZSI/zsi/doc/examples/client/send_request/simple/server/server.py
+ZSI/zsi/doc/examples/client/send_request/simple/wsdl2py/client.py
+ZSI/zsi/doc/examples/server/receive_request/complex/client/ComplexTypes.py
+ZSI/zsi/doc/examples/server/receive_request/complex/client/client.py
+ZSI/zsi/doc/examples/server/receive_request/complex/manual/ComplexTypes.py
+ZSI/zsi/doc/examples/server/receive_request/complex/manual/server.py
+ZSI/zsi/doc/examples/server/receive_request/complex/wsdl2py/server.py
+ZSI/zsi/doc/examples/server/receive_request/simple/CGI/cgi.py
+ZSI/zsi/doc/examples/server/receive_request/simple/client/client.py
+ZSI/zsi/doc/examples/server/receive_request/simple/mod_python/MyHandler.py
+ZSI/zsi/doc/examples/server/receive_request/simple/mod_python/mod_python.py
+ZSI/zsi/doc/examples/server/receive_request/simple/standalone/standalone.py
+ZSI/zsi/doc/examples/server/send_response/complex/client/client.py
+ZSI/zsi/doc/examples/server/send_response/complex/manual/ComplexTypes.py
+ZSI/zsi/doc/examples/server/send_response/complex/manual/server.py
+ZSI/zsi/doc/examples/server/send_response/complex/wsdl2py/server.py
+ZSI/zsi/doc/examples/server/send_response/simple/no_typecode/client.py
+ZSI/zsi/doc/examples/server/send_response/simple/no_typecode/server.py
+ZSI/zsi/doc/examples/server/send_response/simple/wsdl2py/client.py
+ZSI/zsi/doc/examples/server/send_response/simple/wsdl2py/server.py
+ZSI/zsi/interop/client.py
+ZSI/zsi/interop/cpackets.py
+ZSI/zsi/interop/sclasses.py
+ZSI/zsi/interop/server.py
+ZSI/zsi/samples/poly.py
+ZSI/zsi/samples/Echo/EchoClient.py
+ZSI/zsi/samples/Echo/EchoServer.py
+ZSI/zsi/test/cgicli.py
+ZSI/zsi/test/test_TCtimes.py
+ZSI/zsi/test/test_t1.py
+ZSI/zsi/test/test_t2.py
+ZSI/zsi/test/test_t3.py
+ZSI/zsi/test/test_t4.py
+ZSI/zsi/test/test_t5.py
+ZSI/zsi/test/test_t6.py
+ZSI/zsi/test/test_t7.py
+ZSI/zsi/test/test_t8.py
+ZSI/zsi/test/test_t9.py
+ZSI/zsi/test/test_zsi.py
+ZSI/zsi/test/test_zsi_net.py
+ZSI/zsi/test/tests_bad.py
+ZSI/zsi/test/tests_good.py
+ZSI/zsi/test/wsdl2py/ServiceTest.py
+ZSI/zsi/test/wsdl2py/runTests.py
+ZSI/zsi/test/wsdl2py/test_AWSECommerceService.py
+ZSI/zsi/test/wsdl2py/test_AmazonS3.py
+ZSI/zsi/test/wsdl2py/test_Attributes.py
+ZSI/zsi/test/wsdl2py/test_Choice.py
+ZSI/zsi/test/wsdl2py/test_DerivedTypes.py
+ZSI/zsi/test/wsdl2py/test_Echo.py
+ZSI/zsi/test/wsdl2py/test_InfoBil.py
+ZSI/zsi/test/wsdl2py/test_MapPoint.py
+ZSI/zsi/test/wsdl2py/test_OpcDaGateway.py
+ZSI/zsi/test/wsdl2py/test_Sabre.py
+ZSI/zsi/test/wsdl2py/test_TerraService.py
+ZSI/zsi/test/wsdl2py/test_ThreatService.py
+ZSI/zsi/test/wsdl2py/test_WhiteMesa.py
+ZSI/zsi/test/wsdl2py/test_XMethodsQuery.py
+ZSI/zsi/test/wsdl2py/test_ZipCodeResolver.py
+ZSI/zsi/test/wsdl2py/servers/EchoServer.py
+ZSI/zsi/test/wsdl2py/servers/WhiteMesa.py
+doc/.cvsignore
+doc/Makefile
+doc/app-dispatch-dict.tex
+doc/app-dispatch.tex
+doc/app-lowlevel.tex
+doc/app-pickler.tex
+doc/bibliography.tex
+doc/blank.png
+doc/c01-intro.tex
+doc/c02-samples.tex
+doc/c02a-coverage.tex
+doc/c03-except.tex
+doc/c04-utils.tex
+doc/c05-parse.tex
+doc/c06-tc.tex
+doc/c07-writer.tex
+doc/c08-fault.tex
+doc/c09-resolve.tex
+doc/c10-dispatch.tex
+doc/client-hello-struct.tex
+doc/contents.png
+doc/dispatch-rpc-hello-array.tex
+doc/dispatch-rpc-hello-struct.tex
+doc/guide.css
+doc/guide.html
+doc/guide.pdf
+doc/guide.ps
+doc/guide.tex
+doc/guide01-intro.tex
+doc/guide02-wsdl2py.tex
+doc/index.png
+doc/modules.png
+doc/next.png
+doc/previous.png
+doc/pyfav.png
+doc/up.png
+doc/version.tex
+doc/zsi.css
+doc/zsi.html
+doc/zsi.l2h~
+doc/zsi.pdf
+doc/zsi.ps
+doc/zsi.tex
+doc/zsi.xsd
+doc/examples/client/receive_response/complex/binding.wsdl
+doc/examples/client/receive_response/complex/interface.wsdl
+doc/examples/client/receive_response/complex/types.xsd
+doc/examples/client/receive_response/complex/manual/.cvsignore
+doc/examples/client/receive_response/complex/manual/ComplexTypes.py
+doc/examples/client/receive_response/complex/manual/client.py
+doc/examples/client/receive_response/complex/server/.cvsignore
+doc/examples/client/receive_response/complex/server/server.py
+doc/examples/client/receive_response/complex/wsdl2py/.cvsignore
+doc/examples/client/receive_response/complex/wsdl2py/client.py
+doc/examples/client/receive_response/simple/binding.wsdl
+doc/examples/client/receive_response/simple/interface.wsdl
+doc/examples/client/receive_response/simple/ServiceProxy/client.py
+doc/examples/client/receive_response/simple/server/.cvsignore
+doc/examples/client/receive_response/simple/server/server.py
+doc/examples/client/receive_response/simple/wsdl2py/.cvsignore
+doc/examples/client/receive_response/simple/wsdl2py/README
+doc/examples/client/receive_response/simple/wsdl2py/client.py
+doc/examples/client/send_request/complex/binding.wsdl
+doc/examples/client/send_request/complex/interface.wsdl
+doc/examples/client/send_request/complex/types.xsd
+doc/examples/client/send_request/complex/manual/.cvsignore
+doc/examples/client/send_request/complex/manual/ComplexTypes.py
+doc/examples/client/send_request/complex/manual/client.py
+doc/examples/client/send_request/complex/server/.cvsignore
+doc/examples/client/send_request/complex/server/ComplexTypes.py
+doc/examples/client/send_request/complex/server/server.py
+doc/examples/client/send_request/complex/wsdl2py/.cvsignore
+doc/examples/client/send_request/complex/wsdl2py/client.py
+doc/examples/client/send_request/simple/README
+doc/examples/client/send_request/simple/binding.wsdl
+doc/examples/client/send_request/simple/interface.wsdl
+doc/examples/client/send_request/simple/Binding/client.py
+doc/examples/client/send_request/simple/ServiceProxy/client.py
+doc/examples/client/send_request/simple/server/.cvsignore
+doc/examples/client/send_request/simple/server/server.py
+doc/examples/client/send_request/simple/wsdl2py/.cvsignore
+doc/examples/client/send_request/simple/wsdl2py/README
+doc/examples/client/send_request/simple/wsdl2py/client.py
+doc/examples/server/receive_request/complex/binding.wsdl
+doc/examples/server/receive_request/complex/interface.wsdl
+doc/examples/server/receive_request/complex/types.xsd
+doc/examples/server/receive_request/complex/client/.cvsignore
+doc/examples/server/receive_request/complex/client/ComplexTypes.py
+doc/examples/server/receive_request/complex/client/client.py
+doc/examples/server/receive_request/complex/manual/.cvsignore
+doc/examples/server/receive_request/complex/manual/ComplexTypes.py
+doc/examples/server/receive_request/complex/manual/server.py
+doc/examples/server/receive_request/complex/wsdl2py/.cvsignore
+doc/examples/server/receive_request/complex/wsdl2py/server.py
+doc/examples/server/receive_request/simple/binding.wsdl
+doc/examples/server/receive_request/simple/interface.wsdl
+doc/examples/server/receive_request/simple/CGI/README
+doc/examples/server/receive_request/simple/CGI/cgi.py
+doc/examples/server/receive_request/simple/client/client.py
+doc/examples/server/receive_request/simple/mod_python/.cvsignore
+doc/examples/server/receive_request/simple/mod_python/MyHandler.py
+doc/examples/server/receive_request/simple/mod_python/README
+doc/examples/server/receive_request/simple/mod_python/mod_python.py
+doc/examples/server/receive_request/simple/standalone/.cvsignore
+doc/examples/server/receive_request/simple/standalone/README
+doc/examples/server/receive_request/simple/standalone/standalone.py
+doc/examples/server/send_response/complex/binding.wsdl
+doc/examples/server/send_response/complex/interface.wsdl
+doc/examples/server/send_response/complex/types.xsd
+doc/examples/server/send_response/complex/client/.cvsignore
+doc/examples/server/send_response/complex/client/client.py
+doc/examples/server/send_response/complex/manual/.cvsignore
+doc/examples/server/send_response/complex/manual/ComplexTypes.py
+doc/examples/server/send_response/complex/manual/server.py
+doc/examples/server/send_response/complex/wsdl2py/.cvsignore
+doc/examples/server/send_response/complex/wsdl2py/server.py
+doc/examples/server/send_response/simple/binding.wsdl
+doc/examples/server/send_response/simple/interface.wsdl
+doc/examples/server/send_response/simple/no_typecode/client.py
+doc/examples/server/send_response/simple/no_typecode/server.py
+doc/examples/server/send_response/simple/wsdl2py/.cvsignore
+doc/examples/server/send_response/simple/wsdl2py/README
+doc/examples/server/send_response/simple/wsdl2py/client.py
+doc/examples/server/send_response/simple/wsdl2py/server.py
+interop/.cvsignore
+interop/InteropTest.wsdl
+interop/InteropTestB.wsdl
+interop/README
+interop/client.py
+interop/cpackets.py
+interop/echoHeaderBindings.wsdl
+interop/sclasses.py
+interop/server.py
+interop/start
+samples/README
+samples/poly.py
+samples/Echo/Echo.wsdl
+samples/Echo/EchoClient.py
+samples/Echo/EchoServer.py
+samples/Echo/README
+samples/Echo/buildms.bat
+samples/Echo/echo.cs
+scripts/wsdl2dispatch
+scripts/wsdl2py
+test/.cvsignore
+test/README
+test/cgicli.py
+test/test_TCtimes.py
+test/test_t1.py
+test/test_t2.py
+test/test_t3.py
+test/test_t4.py
+test/test_t5.py
+test/test_t6.py
+test/test_t7.py
+test/test_t8.py
+test/test_t9.py
+test/test_zsi.py
+test/test_zsi_net.py
+test/tests_bad.py
+test/tests_good.py
+test/wsdl2py/.cvsignore
+test/wsdl2py/README
+test/wsdl2py/ServiceTest.py
+test/wsdl2py/config.txt
+test/wsdl2py/runTests.py
+test/wsdl2py/test_AWSECommerceService.py
+test/wsdl2py/test_AmazonS3.py
+test/wsdl2py/test_Attributes.py
+test/wsdl2py/test_Attributes.xsd
+test/wsdl2py/test_Choice.py
+test/wsdl2py/test_Choice.xsd
+test/wsdl2py/test_DerivedTypes.py
+test/wsdl2py/test_DerivedTypes.xsd
+test/wsdl2py/test_Echo.py
+test/wsdl2py/test_InfoBil.py
+test/wsdl2py/test_MapPoint.py
+test/wsdl2py/test_OpcDaGateway.py
+test/wsdl2py/test_Sabre.py
+test/wsdl2py/test_TerraService.py
+test/wsdl2py/test_ThreatService.py
+test/wsdl2py/test_WhiteMesa.py
+test/wsdl2py/test_XMethodsQuery.py
+test/wsdl2py/test_ZipCodeResolver.py
+test/wsdl2py/servers/EchoServer.py
+test/wsdl2py/servers/WhiteMesa.py
diff --git a/ZSI.egg-info/dependency_links.txt b/ZSI.egg-info/dependency_links.txt
new file mode 100644
index 0000000..904169a
--- /dev/null
+++ b/ZSI.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+http://sourceforge.net/project/showfiles.php?group_id=6473&package_id=6541&release_id=286213
diff --git a/ZSI.egg-info/entry_points.txt b/ZSI.egg-info/entry_points.txt
new file mode 100644
index 0000000..8fbc289
--- /dev/null
+++ b/ZSI.egg-info/entry_points.txt
@@ -0,0 +1,4 @@
+[console_scripts]
+wsdl2dispatch = ZSI.generate.commands:wsdl2dispatch
+wsdl2py = ZSI.generate.commands:wsdl2py
+
diff --git a/ZSI.egg-info/requires.txt b/ZSI.egg-info/requires.txt
new file mode 100644
index 0000000..772549e
--- /dev/null
+++ b/ZSI.egg-info/requires.txt
@@ -0,0 +1 @@
+PyXML >= 0.8.3 \ No newline at end of file
diff --git a/ZSI.egg-info/top_level.txt b/ZSI.egg-info/top_level.txt
new file mode 100644
index 0000000..745eb6c
--- /dev/null
+++ b/ZSI.egg-info/top_level.txt
@@ -0,0 +1 @@
+ZSI
diff --git a/ZSI/.cvsignore b/ZSI/.cvsignore
new file mode 100644
index 0000000..f1b640c
--- /dev/null
+++ b/ZSI/.cvsignore
@@ -0,0 +1,2 @@
+*.pyc
+version.py
diff --git a/ZSI/LBNLCopyright b/ZSI/LBNLCopyright
new file mode 100644
index 0000000..e887f13
--- /dev/null
+++ b/ZSI/LBNLCopyright
@@ -0,0 +1,41 @@
+Copyright (c) 2003, The Regents of the University of California,
+through Lawrence Berkeley National Laboratory (subject to receipt of
+any required approvals from the U.S. Dept. of Energy). All rights
+reserved. Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+(1) Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+(2) Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+(3) Neither the name of the University of California, Lawrence Berkeley
+National Laboratory, U.S. Dept. of Energy nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+You are under no obligation whatsoever to provide any bug fixes,
+patches, or upgrades to the features, functionality or performance of
+the source code ("Enhancements") to anyone; however, if you choose to
+make your Enhancements available either publicly, or directly to
+Lawrence Berkeley National Laboratory, without imposing a separate
+written license agreement for such Enhancements, then you hereby grant
+the following license: a non-exclusive, royalty-free perpetual license
+to install, use, modify, prepare derivative works, incorporate into
+other computer software, distribute, and sublicense such Enhancements
+or derivative works thereof, in binary and source code form.
+
+
diff --git a/ZSI/ServiceContainer.py b/ZSI/ServiceContainer.py
new file mode 100644
index 0000000..a12d803
--- /dev/null
+++ b/ZSI/ServiceContainer.py
@@ -0,0 +1,498 @@
+#! /usr/bin/env python
+'''Simple Service Container
+ -- use with wsdl2py generated modules.
+'''
+
+import urlparse, types, os, sys, cStringIO as StringIO, thread,re
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+from ZSI import ParseException, FaultFromException, FaultFromZSIException, Fault
+from ZSI import _copyright, _seqtypes, _get_element_nsuri_name, resolvers
+from ZSI import _get_idstr
+from ZSI.address import Address
+from ZSI.parse import ParsedSoap
+from ZSI.writer import SoapWriter
+from ZSI.dispatch import _ModPythonSendXML, _ModPythonSendFault, _CGISendXML, _CGISendFault
+from ZSI.dispatch import SOAPRequestHandler as BaseSOAPRequestHandler
+
+"""
+Functions:
+ _Dispatch
+ AsServer
+ GetSOAPContext
+
+Classes:
+ SOAPContext
+ NoSuchService
+ PostNotSpecified
+ SOAPActionNotSpecified
+ ServiceSOAPBinding
+ SimpleWSResource
+ SOAPRequestHandler
+ ServiceContainer
+"""
+class NoSuchService(Exception): pass
+class UnknownRequestException(Exception): pass
+class PostNotSpecified(Exception): pass
+class SOAPActionNotSpecified(Exception): pass
+class WSActionException(Exception): pass
+class WSActionNotSpecified(WSActionException): pass
+class NotAuthorized(Exception): pass
+class ServiceAlreadyPresent(Exception): pass
+
+
+class SOAPContext:
+ def __init__(self, container, xmldata, ps, connection, httpheaders,
+ soapaction):
+
+ self.container = container
+ self.xmldata = xmldata
+ self.parsedsoap = ps
+ self.connection = connection
+ self.httpheaders= httpheaders
+ self.soapaction = soapaction
+
+_contexts = dict()
+def GetSOAPContext():
+ global _contexts
+ return _contexts[thread.get_ident()]
+
+def _Dispatch(ps, server, SendResponse, SendFault, post, action, nsdict={}, **kw):
+ '''Send ParsedSoap instance to ServiceContainer, which dispatches to
+ appropriate service via post, and method via action. Response is a
+ self-describing pyobj, which is passed to a SoapWriter.
+
+ Call SendResponse or SendFault to send the reply back, appropriately.
+ server -- ServiceContainer instance
+
+ '''
+ localURL = 'http://%s:%d%s' %(server.server_name,server.server_port,post)
+ address = action
+ service = server.getNode(post)
+ isWSResource = False
+ if isinstance(service, SimpleWSResource):
+ isWSResource = True
+ service.setServiceURL(localURL)
+ address = Address()
+ try:
+ address.parse(ps)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+ if action and action != address.getAction():
+ e = WSActionException('SOAP Action("%s") must match WS-Action("%s") if specified.' \
+ %(action,address.getAction()))
+ return SendFault(FaultFromException(e, 0, None), **kw)
+ action = address.getAction()
+
+ if isinstance(service, ServiceInterface) is False:
+ e = NoSuchService('no service at POST(%s) in container: %s' %(post,server))
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+ if not service.authorize(None, post, action):
+ return SendFault(Fault(Fault.Server, "Not authorized"), code=401)
+ #try:
+ # raise NotAuthorized()
+ #except Exception, e:
+ #return SendFault(FaultFromException(e, 0, None), code=401, **kw)
+ ##return SendFault(FaultFromException(NotAuthorized(), 0, None), code=401, **kw)
+
+ try:
+ method = service.getOperation(ps, address)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+ try:
+ if isWSResource is True:
+ result = method(ps, address)
+ else:
+ result = method(ps)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+ # Verify if Signed
+ service.verify(ps)
+
+ # If No response just return.
+ if result is None:
+ return
+
+ sw = SoapWriter(nsdict=nsdict)
+ try:
+ sw.serialize(result)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+ if isWSResource is True:
+ action = service.getResponseAction(action)
+ addressRsp = Address(action=action)
+ try:
+ addressRsp.setResponseFromWSAddress(address, localURL)
+ addressRsp.serialize(sw)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+ # Create Signatures
+ service.sign(sw)
+
+ try:
+ soapdata = str(sw)
+ return SendResponse(soapdata, **kw)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+
+def AsServer(port=80, services=()):
+ '''port --
+ services -- list of service instances
+ '''
+ address = ('', port)
+ sc = ServiceContainer(address, services)
+ #for service in services:
+ # path = service.getPost()
+ # sc.setNode(service, path)
+ sc.serve_forever()
+
+
+class ServiceInterface:
+ '''Defines the interface for use with ServiceContainer Handlers.
+
+ class variables:
+ soapAction -- dictionary of soapAction keys, and operation name values.
+ These are specified in the WSDL soap bindings. There must be a
+ class method matching the operation name value. If WS-Action is
+ used the keys are WS-Action request values, according to the spec
+ if soapAction and WS-Action is specified they must be equal.
+
+ wsAction -- dictionary of operation name keys and WS-Action
+ response values. These values are specified by the portType.
+
+ root -- dictionary of root element keys, and operation name values.
+
+ '''
+ soapAction = {}
+ wsAction = {}
+ root = {}
+
+ def __init__(self, post):
+ self.post = post
+
+ def authorize(self, auth_info, post, action):
+ return 1
+
+ def __str__(self):
+ return '%s(%s) POST(%s)' %(self.__class__.__name__, _get_idstr(self), self.post)
+
+ def sign(self, sw):
+ return
+
+ def verify(self, ps):
+ return
+
+ def getPost(self):
+ return self.post
+
+ def getOperation(self, ps, action):
+ '''Returns a method of class.
+ action -- soapAction value
+ '''
+ opName = self.getOperationName(ps, action)
+ return getattr(self, opName)
+
+ def getOperationName(self, ps, action):
+ '''Returns operation name.
+ action -- soapAction value
+ '''
+ method = self.root.get(_get_element_nsuri_name(ps.body_root)) or \
+ self.soapAction.get(action)
+ if method is None:
+ raise UnknownRequestException, \
+ 'failed to map request to a method: action(%s), root%s' %(action,_get_element_nsuri_name(ps.body_root))
+ return method
+
+
+class ServiceSOAPBinding(ServiceInterface):
+ '''Binding defines the set of wsdl:binding operations, it takes as input a
+ ParsedSoap instance and parses it into a pyobj. It returns a response pyobj.
+ '''
+ def __init__(self, post):
+ ServiceInterface.__init__(self, post)
+
+ def __call___(self, action, ps):
+ return self.getOperation(ps, action)(ps)
+
+
+class SimpleWSResource(ServiceSOAPBinding):
+ '''Simple WSRF service, performs method resolutions based
+ on WS-Action values rather than SOAP Action.
+
+ class variables:
+ encoding
+ wsAction -- Must override to set output Action values.
+ soapAction -- Must override to set input Action values.
+ '''
+ encoding = "UTF-8"
+
+ def __init__(self, post=None):
+ '''
+ post -- POST value
+ '''
+ assert isinstance(self.soapAction, dict), "soapAction must be a dict"
+ assert isinstance(self.wsAction, dict), "wsAction must be a dict"
+ ServiceSOAPBinding.__init__(self, post)
+
+ def __call___(self, action, ps, address):
+ return self.getOperation(ps, action)(ps, address)
+
+ def getServiceURL(self):
+ return self._url
+
+ def setServiceURL(self, url):
+ self._url = url
+
+ def getOperation(self, ps, address):
+ '''Returns a method of class.
+ address -- ws-address
+ '''
+ action = address.getAction()
+ opName = self.getOperationName(ps, action)
+ return getattr(self, opName)
+
+ def getResponseAction(self, ps, action):
+ '''Returns response WS-Action if available
+ action -- request WS-Action value.
+ '''
+ opName = self.getOperationName(ps, action)
+ if self.wsAction.has_key(opName) is False:
+ raise WSActionNotSpecified, 'wsAction dictionary missing key(%s)' %opName
+ return self.wsAction[opName]
+
+ def do_POST(self):
+ '''The POST command. This is called by HTTPServer, not twisted.
+ action -- SOAPAction(HTTP header) or wsa:Action(SOAP:Header)
+ '''
+ global _contexts
+
+ soapAction = self.headers.getheader('SOAPAction')
+ post = self.path
+ if not post:
+ raise PostNotSpecified, 'HTTP POST not specified in request'
+ if soapAction:
+ soapAction = soapAction.strip('\'"')
+ post = post.strip('\'"')
+ try:
+ ct = self.headers['content-type']
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, self.rfile)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve, readerclass=DomletteReader)
+ else:
+ length = int(self.headers['content-length'])
+ ps = ParsedSoap(self.rfile.read(length), readerclass=DomletteReader)
+ except ParseException, e:
+ self.send_fault(FaultFromZSIException(e))
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ self.send_fault(FaultFromException(e, 1, sys.exc_info()[2]))
+ else:
+ # Keep track of calls
+ thread_id = thread.get_ident()
+ _contexts[thread_id] = SOAPContext(self.server, xml, ps,
+ self.connection,
+ self.headers, soapAction)
+
+ try:
+ _Dispatch(ps, self.server, self.send_xml, self.send_fault,
+ post=post, action=soapAction)
+ except Exception, e:
+ self.send_fault(FaultFromException(e, 0, sys.exc_info()[2]))
+
+ # Clean up after the call
+ if _contexts.has_key(thread_id):
+ del _contexts[thread_id]
+
+
+class SOAPRequestHandler(BaseSOAPRequestHandler):
+ '''SOAP handler.
+ '''
+ def do_POST(self):
+ '''The POST command.
+ action -- SOAPAction(HTTP header) or wsa:Action(SOAP:Header)
+ '''
+ soapAction = self.headers.getheader('SOAPAction')
+ post = self.path
+ if not post:
+ raise PostNotSpecified, 'HTTP POST not specified in request'
+ if soapAction:
+ soapAction = soapAction.strip('\'"')
+ post = post.strip('\'"')
+ try:
+ ct = self.headers['content-type']
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, self.rfile)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ length = int(self.headers['content-length'])
+ xml = self.rfile.read(length)
+ ps = ParsedSoap(xml)
+ except ParseException, e:
+ self.send_fault(FaultFromZSIException(e))
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ self.send_fault(FaultFromException(e, 1, sys.exc_info()[2]))
+ else:
+ # Keep track of calls
+ thread_id = thread.get_ident()
+ _contexts[thread_id] = SOAPContext(self.server, xml, ps,
+ self.connection,
+ self.headers, soapAction)
+
+ try:
+ _Dispatch(ps, self.server, self.send_xml, self.send_fault,
+ post=post, action=soapAction)
+ except Exception, e:
+ self.send_fault(FaultFromException(e, 0, sys.exc_info()[2]))
+
+ # Clean up after the call
+ if _contexts.has_key(thread_id):
+ del _contexts[thread_id]
+
+ def do_GET(self):
+ '''The GET command.
+ '''
+ if self.path.lower().endswith("?wsdl"):
+ service_path = self.path[:-5]
+ service = self.server.getNode(service_path)
+ if hasattr(service, "_wsdl"):
+ wsdl = service._wsdl
+ # update the soap:location tag in the wsdl to the actual server
+ # location
+ # - default to 'http' as protocol, or use server-specified protocol
+ proto = 'http'
+ if hasattr(self.server,'proto'):
+ proto = self.server.proto
+ serviceUrl = '%s://%s:%d%s' % (proto,
+ self.server.server_name,
+ self.server.server_port,
+ service_path)
+ soapAddress = '<soap:address location="%s"/>' % serviceUrl
+ wsdlre = re.compile('\<soap:address[^\>]*>',re.IGNORECASE)
+ wsdl = re.sub(wsdlre,soapAddress,wsdl)
+ self.send_xml(wsdl)
+ else:
+ self.send_error(404, "WSDL not available for that service [%s]." % self.path)
+ else:
+ self.send_error(404, "Service not found [%s]." % self.path)
+
+class ServiceContainer(HTTPServer):
+ '''HTTPServer that stores service instances according
+ to POST values. An action value is instance specific,
+ and specifies an operation (function) of an instance.
+ '''
+ class NodeTree:
+ '''Simple dictionary implementation of a node tree
+ '''
+ def __init__(self):
+ self.__dict = {}
+
+ def __str__(self):
+ return str(self.__dict)
+
+ def listNodes(self):
+ print self.__dict.keys()
+
+ def getNode(self, url):
+ path = urlparse.urlsplit(url)[2]
+ if path.startswith("/"):
+ path = path[1:]
+
+ if self.__dict.has_key(path):
+ return self.__dict[path]
+ else:
+ raise NoSuchService, 'No service(%s) in ServiceContainer' %path
+
+ def setNode(self, service, url):
+ path = urlparse.urlsplit(url)[2]
+ if path.startswith("/"):
+ path = path[1:]
+
+ if not isinstance(service, ServiceSOAPBinding):
+ raise TypeError, 'A Service must implement class ServiceSOAPBinding'
+ if self.__dict.has_key(path):
+ raise ServiceAlreadyPresent, 'Service(%s) already in ServiceContainer' % path
+ else:
+ self.__dict[path] = service
+
+ def removeNode(self, url):
+ path = urlparse.urlsplit(url)[2]
+ if path.startswith("/"):
+ path = path[1:]
+
+ if self.__dict.has_key(path):
+ node = self.__dict[path]
+ del self.__dict[path]
+ return node
+ else:
+ raise NoSuchService, 'No service(%s) in ServiceContainer' %path
+
+ def __init__(self, server_address, services=[], RequestHandlerClass=SOAPRequestHandler):
+ '''server_address --
+ RequestHandlerClass --
+ '''
+ HTTPServer.__init__(self, server_address, RequestHandlerClass)
+ self._nodes = self.NodeTree()
+ map(lambda s: self.setNode(s), services)
+
+ def __str__(self):
+ return '%s(%s) nodes( %s )' %(self.__class__, _get_idstr(self), str(self._nodes))
+
+ def __call__(self, ps, post, action, address=None):
+ '''ps -- ParsedSoap representing the request
+ post -- HTTP POST --> instance
+ action -- Soap Action header --> method
+ address -- Address instance representing WS-Address
+ '''
+ method = self.getCallBack(ps, post, action)
+ if isinstance(method.im_self, SimpleWSResource):
+ return method(ps, address)
+ return method(ps)
+
+
+ def setNode(self, service, url=None):
+ if url is None:
+ url = service.getPost()
+ self._nodes.setNode(service, url)
+
+ def getNode(self, url):
+ return self._nodes.getNode(url)
+
+ def removeNode(self, url):
+ self._nodes.removeNode(url)
+
+
+class SimpleWSResource(ServiceSOAPBinding):
+
+ def getNode(self, post):
+ '''post -- POST HTTP value
+ '''
+ return self._nodes.getNode(post)
+
+ def setNode(self, service, post):
+ '''service -- service instance
+ post -- POST HTTP value
+ '''
+ self._nodes.setNode(service, post)
+
+ def getCallBack(self, ps, post, action):
+ '''post -- POST HTTP value
+ action -- SOAP Action value
+ '''
+ node = self.getNode(post)
+ if node is None:
+ raise NoSuchFunction
+ if node.authorize(None, post, action):
+ return node.getOperation(ps, action)
+ else:
+ raise NotAuthorized, "Authorization failed for method %s" % action
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/ServiceProxy.py b/ZSI/ServiceProxy.py
new file mode 100755
index 0000000..44b89be
--- /dev/null
+++ b/ZSI/ServiceProxy.py
@@ -0,0 +1,305 @@
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+
+from ZSI.wstools.Utility import SplitQName
+from ZSI.wstools.Namespaces import WSDL
+from ZSI import *
+from ZSI.client import *
+from ZSI.TC import Any
+from ZSI.typeinterpreter import BaseTypeInterpreter
+import wstools
+from wstools.Utility import DOM
+from urlparse import urlparse
+import weakref
+
+class ServiceProxy:
+ """A ServiceProxy provides a convenient way to call a remote web
+ service that is described with WSDL. The proxy exposes methods
+ that reflect the methods of the remote web service."""
+
+ def __init__(self, wsdl, service=None, port=None, tracefile=None,
+ nsdict=None, transdict=None):
+ """
+ Parameters:
+ wsdl -- WSDLTools.WSDL instance or URL of WSDL.
+ service -- service name or index
+ port -- port name or index
+ tracefile --
+ nsdict -- key prefix to namespace mappings for serialization
+ in SOAP Envelope.
+ transdict -- arguments to pass into HTTPConnection constructor.
+ """
+ self._tracefile = tracefile
+ self._nsdict = nsdict or {}
+ self._transdict = transdict
+ self._wsdl = wsdl
+ if isinstance(wsdl, basestring) is True:
+ self._wsdl = wstools.WSDLTools.WSDLReader().loadFromURL(wsdl)
+
+ assert isinstance(self._wsdl, wstools.WSDLTools.WSDL), 'expecting a WSDL instance'
+ self._service = self._wsdl.services[service or 0]
+ self.__doc__ = self._service.documentation
+ self._port = self._service.ports[port or 0]
+ self._name = self._service.name
+ self._methods = {}
+
+ binding = self._port.getBinding()
+ portType = binding.getPortType()
+ for port in self._service.ports:
+ for item in port.getPortType().operations:
+ callinfo = wstools.WSDLTools.callInfoFromWSDL(port, item.name)
+ method = MethodProxy(self, callinfo)
+ setattr(self, item.name, method)
+ self._methods.setdefault(item.name, []).append(method)
+
+ def _call(self, name, *args, **kwargs):
+ """Call the named remote web service method."""
+ if len(args) and len(kwargs):
+ raise TypeError(
+ 'Use positional or keyword argument only.'
+ )
+
+ callinfo = getattr(self, name).callinfo
+
+ # go through the list of defined methods, and look for the one with
+ # the same number of arguments as what was passed. this is a weak
+ # check that should probably be improved in the future to check the
+ # types of the arguments to allow for polymorphism
+ for method in self._methods[name]:
+ if len(method.callinfo.inparams) == len(kwargs):
+ callinfo = method.callinfo
+
+ soapAction = callinfo.soapAction
+ url = callinfo.location
+ (protocol, host, uri, query, fragment, identifier) = urlparse(url)
+ port = None
+ if host.find(':') >= 0:
+ host, port = host.split(':')
+
+ if protocol == 'http':
+ transport = httplib.HTTPConnection
+ elif protocol == 'https':
+ transport = httplib.HTTPSConnection
+ else:
+ raise RuntimeError, 'Unknown protocol %s' %protocol
+
+ binding = Binding(host=host, tracefile=self._tracefile,
+ transport=transport,transdict=self._transdict,
+ port=port, url=url, nsdict=self._nsdict,
+ soapaction=soapAction,)
+
+ request, response = self._getTypeCodes(callinfo)
+ if len(kwargs): args = kwargs
+ if request is None:
+ request = Any(oname=name)
+ binding.Send(url=url, opname=None, obj=args,
+ nsdict=self._nsdict, soapaction=soapAction, requesttypecode=request)
+ return binding.Receive(replytype=response)
+
+
+ def _getTypeCodes(self, callinfo):
+ """Returns typecodes representing input and output messages, if request and/or
+ response fails to be generated return None for either or both.
+
+ callinfo -- WSDLTools.SOAPCallInfo instance describing an operation.
+ """
+ prefix = None
+ self._resetPrefixDict()
+ if callinfo.use == 'encoded':
+ prefix = self._getPrefix(callinfo.namespace)
+ try:
+ requestTC = self._getTypeCode(parameters=callinfo.getInParameters(), literal=(callinfo.use=='literal'))
+ except EvaluateException, ex:
+ print "DEBUG: Request Failed to generate --", ex
+ requestTC = None
+
+ self._resetPrefixDict()
+ try:
+ replyTC = self._getTypeCode(parameters=callinfo.getOutParameters(), literal=(callinfo.use=='literal'))
+ except EvaluateException, ex:
+ print "DEBUG: Response Failed to generate --", ex
+ replyTC = None
+
+ request = response = None
+ if callinfo.style == 'rpc':
+ if requestTC: request = TC.Struct(pyclass=None, ofwhat=requestTC, pname=callinfo.methodName)
+ if replyTC: response = TC.Struct(pyclass=None, ofwhat=replyTC, pname='%sResponse' %callinfo.methodName)
+ else:
+ if requestTC: request = requestTC[0]
+ if replyTC: response = replyTC[0]
+
+ #THIS IS FOR RPC/ENCODED, DOC/ENCODED Wrapper
+ if request and prefix and callinfo.use == 'encoded':
+ request.oname = '%(prefix)s:%(name)s xmlns:%(prefix)s="%(namespaceURI)s"' \
+ %{'prefix':prefix, 'name':request.aname, 'namespaceURI':callinfo.namespace}
+
+ return request, response
+
+ def _getTypeCode(self, parameters, literal=False):
+ """Returns typecodes representing a parameter set
+ parameters -- list of WSDLTools.ParameterInfo instances representing
+ the parts of a WSDL Message.
+ """
+ ofwhat = []
+ for part in parameters:
+ namespaceURI,localName = part.type
+
+ if part.element_type:
+ #global element
+ element = self._wsdl.types[namespaceURI].elements[localName]
+ tc = self._getElement(element, literal=literal, local=False, namespaceURI=namespaceURI)
+ else:
+ #local element
+ name = part.name
+ typeClass = self._getTypeClass(namespaceURI, localName)
+ if not typeClass:
+ tp = self._wsdl.types[namespaceURI].types[localName]
+ tc = self._getType(tp, name, literal, local=True, namespaceURI=namespaceURI)
+ else:
+ tc = typeClass(name)
+ ofwhat.append(tc)
+ return ofwhat
+
+ def _globalElement(self, typeCode, namespaceURI, literal):
+ """namespaces typecodes representing global elements with
+ literal encoding.
+ typeCode -- typecode representing an element.
+ namespaceURI -- namespace
+ literal -- True/False
+ """
+ if literal:
+ typeCode.oname = '%(prefix)s:%(name)s xmlns:%(prefix)s="%(namespaceURI)s"' \
+ %{'prefix':self._getPrefix(namespaceURI), 'name':typeCode.oname, 'namespaceURI':namespaceURI}
+
+ def _getPrefix(self, namespaceURI):
+ """Retrieves a prefix/namespace mapping.
+ namespaceURI -- namespace
+ """
+ prefixDict = self._getPrefixDict()
+ if prefixDict.has_key(namespaceURI):
+ prefix = prefixDict[namespaceURI]
+ else:
+ prefix = 'ns1'
+ while prefix in prefixDict.values():
+ prefix = 'ns%d' %int(prefix[-1]) + 1
+ prefixDict[namespaceURI] = prefix
+ return prefix
+
+ def _getPrefixDict(self):
+ """Used to hide the actual prefix dictionary.
+ """
+ if not hasattr(self, '_prefixDict'):
+ self.__prefixDict = {}
+ return self.__prefixDict
+
+ def _resetPrefixDict(self):
+ """Clears the prefix dictionary, this needs to be done
+ before creating a new typecode for a message
+ (ie. before, and after creating a new message typecode)
+ """
+ self._getPrefixDict().clear()
+
+ def _getElement(self, element, literal=False, local=False, namespaceURI=None):
+ """Returns a typecode instance representing the passed in element.
+ element -- XMLSchema.ElementDeclaration instance
+ literal -- literal encoding?
+ local -- is locally defined?
+ namespaceURI -- namespace
+ """
+ if not element.isElement():
+ raise TypeError, 'Expecting an ElementDeclaration'
+
+ tc = None
+ elementName = element.getAttribute('name')
+ tp = element.getTypeDefinition('type')
+
+ typeObj = None
+ if not (tp or element.content):
+ nsuriType,localName = element.getAttribute('type')
+ typeClass = self._getTypeClass(nsuriType,localName)
+
+ typeObj = typeClass(elementName)
+ elif not tp:
+ tp = element.content
+
+ if not typeObj:
+ typeObj = self._getType(tp, elementName, literal, local, namespaceURI)
+
+ minOccurs = int(element.getAttribute('minOccurs'))
+ typeObj.optional = not minOccurs
+ typeObj.minOccurs = minOccurs
+
+ maxOccurs = element.getAttribute('maxOccurs')
+ typeObj.repeatable = (maxOccurs == 'unbounded') or (int(maxOccurs) > 1)
+
+ return typeObj
+
+ def _getType(self, tp, name, literal, local, namespaceURI):
+ """Returns a typecode instance representing the passed in type and name.
+ tp -- XMLSchema.TypeDefinition instance
+ name -- element name
+ literal -- literal encoding?
+ local -- is locally defined?
+ namespaceURI -- namespace
+ """
+ ofwhat = []
+ if not (tp.isDefinition() and tp.isComplex()):
+ raise EvaluateException, 'only supporting complexType definition'
+ elif tp.content.isComplex():
+ if hasattr(tp.content, 'derivation') and tp.content.derivation.isRestriction():
+ derived = tp.content.derivation
+ typeClass = self._getTypeClass(*derived.getAttribute('base'))
+ if typeClass == TC.Array:
+ attrs = derived.attr_content[0].attributes[WSDL.BASE]
+ prefix, localName = SplitQName(attrs['arrayType'])
+ nsuri = derived.attr_content[0].getXMLNS(prefix=prefix)
+ localName = localName.split('[')[0]
+ simpleTypeClass = self._getTypeClass(namespaceURI=nsuri, localName=localName)
+ if simpleTypeClass:
+ ofwhat = simpleTypeClass()
+ else:
+ tp = self._wsdl.types[nsuri].types[localName]
+ ofwhat = self._getType(tp=tp, name=None, literal=literal, local=True, namespaceURI=nsuri)
+ else:
+ raise EvaluateException, 'only support soapenc:Array restrictions'
+ return typeClass(atype=name, ofwhat=ofwhat, pname=name, childNames='item')
+ else:
+ raise EvaluateException, 'complexContent only supported for soapenc:Array derivations'
+ elif tp.content.isModelGroup():
+ modelGroup = tp.content
+ for item in modelGroup.content:
+ ofwhat.append(self._getElement(item, literal=literal, local=True))
+
+ tc = TC.Struct(pyclass=None, ofwhat=ofwhat, pname=name)
+ if not local:
+ self._globalElement(tc, namespaceURI=namespaceURI, literal=literal)
+ return tc
+
+ raise EvaluateException, 'only supporting complexType w/ model group, or soapenc:Array restriction'
+
+ def _getTypeClass(self, namespaceURI, localName):
+ """Returns a typecode class representing the type we are looking for.
+ localName -- name of the type we are looking for.
+ namespaceURI -- defining XMLSchema targetNamespace.
+ """
+ bti = BaseTypeInterpreter()
+ simpleTypeClass = bti.get_typeclass(localName, namespaceURI)
+ return simpleTypeClass
+
+
+class MethodProxy:
+ """ """
+ def __init__(self, parent, callinfo):
+ self.__name__ = callinfo.methodName
+ self.__doc__ = callinfo.documentation
+ self.callinfo = callinfo
+ self.parent = weakref.ref(parent)
+
+ def __call__(self, *args, **kwargs):
+ return self.parent()._call(self.__name__, *args, **kwargs)
diff --git a/ZSI/TC.py b/ZSI/TC.py
new file mode 100644
index 0000000..cb93931
--- /dev/null
+++ b/ZSI/TC.py
@@ -0,0 +1,1807 @@
+#! /usr/bin/env python
+# $Header$
+'''General typecodes.
+'''
+
+from ZSI import _copyright, _children, _child_elements, \
+ _floattypes, _stringtypes, _seqtypes, _find_attr, _find_attrNS, _find_attrNodeNS, \
+ _find_arraytype, _find_default_namespace, _find_href, _find_encstyle, \
+ _resolve_prefix, _find_xsi_attr, _find_type, \
+ _find_xmlns_prefix, _get_element_nsuri_name, _get_idstr, \
+ _Node, EvaluateException, \
+ _valid_encoding, ParseException
+
+from ZSI.wstools.Namespaces import SCHEMA, SOAP
+from ZSI.wstools.Utility import SplitQName
+from ZSI.wstools.c14n import Canonicalize
+from ZSI.wstools.logging import getLogger as _GetLogger
+
+import re, types, time, copy
+
+from base64 import decodestring as b64decode, encodestring as b64encode
+from urllib import unquote as urldecode, quote as urlencode
+from binascii import unhexlify as hexdecode, hexlify as hexencode
+
+
+_is_xsd_or_soap_ns = lambda ns: ns in [
+ SCHEMA.XSD3, SOAP.ENC, SCHEMA.XSD1, SCHEMA.XSD2, ]
+_find_nil = lambda E: _find_xsi_attr(E, "null") or _find_xsi_attr(E, "nil")
+
+def _get_xsitype(pyclass):
+ '''returns the xsi:type as a tuple, coupled with ZSI.schema
+ '''
+ if hasattr(pyclass,'type') and type(pyclass.type) in _seqtypes:
+ return pyclass.type
+ elif hasattr(pyclass,'type') and hasattr(pyclass, 'schema'):
+ return (pyclass.schema, pyclass.type)
+
+ return (None,None)
+
+
+# value returned when xsi:nil="true"
+Nilled = None
+UNBOUNDED = 'unbounded'
+
+
+class TypeCode:
+ '''The parent class for all parseable SOAP types.
+ Class data:
+ typechecks -- do init-time type checking if non-zero
+ Class data subclasses may define:
+ tag -- global element declaration
+ type -- global type definition
+ parselist -- list of valid SOAP types for this class, as
+ (uri,name) tuples, where a uri of None means "all the XML
+ Schema namespaces"
+ errorlist -- parselist in a human-readable form; will be
+ generated if/when needed
+ seriallist -- list of Python types or user-defined classes
+ that this typecode can serialize.
+ logger -- logger instance for this class.
+ '''
+ tag = None
+ type = (None,None)
+ typechecks = True
+ attribute_typecode_dict = None
+ logger = _GetLogger('ZSI.TC.TypeCode')
+
+ def __init__(self, pname=None, aname=None, minOccurs=1,
+ maxOccurs=1, nillable=False, typed=True, unique=True,
+ pyclass=None, attrs_aname='_attrs', **kw):
+ '''Baseclass initialization.
+ Instance data (and usually keyword arg)
+ pname -- the parameter name (localname).
+ nspname -- the namespace for the parameter;
+ None to ignore the namespace
+ typed -- output xsi:type attribute
+ unique -- data item is not aliased (no href/id needed)
+ minOccurs -- min occurances
+ maxOccurs -- max occurances
+ nillable -- is item nillable?
+ attrs_aname -- This is variable name to dictionary of attributes
+ encoded -- encoded namespaceURI (specify if use is encoded)
+ '''
+ if type(pname) in _seqtypes:
+ self.nspname, self.pname = pname
+ else:
+ self.nspname, self.pname = None, pname
+
+ if self.pname:
+ self.pname = str(self.pname).split(':')[-1]
+
+ self.aname = aname or self.pname
+ self.minOccurs = minOccurs
+ self.maxOccurs = maxOccurs
+ self.nillable = nillable
+ self.typed = typed
+ self.unique = unique
+ self.attrs_aname = attrs_aname
+ self.pyclass = pyclass
+
+ # Need this stuff for rpc/encoded.
+ encoded = kw.get('encoded')
+ if encoded is not None:
+ self.nspname = kw['encoded']
+
+ def parse(self, elt, ps):
+ '''
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ raise EvaluateException("Unimplemented evaluation", ps.Backtrace(elt))
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ '''
+ Parameters:
+ elt -- the current DOMWrapper element
+ sw -- soapWriter object
+ pyobj -- python object to serialize
+
+ '''
+ raise EvaluateException("Unimplemented evaluation", sw.Backtrace(elt))
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ Parameters:
+ text -- text content
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ raise EvaluateException("Unimplemented evaluation", ps.Backtrace(elt))
+
+ def serialize_as_nil(self, elt):
+ '''
+ Parameters:
+ elt -- the current DOMWrapper element
+ '''
+ elt.setAttributeNS(SCHEMA.XSI3, 'nil', '1')
+
+ def SimpleHREF(self, elt, ps, tag):
+ '''Simple HREF for non-string and non-struct and non-array.
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ tag --
+ '''
+ if len(_children(elt)): return elt
+ href = _find_href(elt)
+ if not href:
+ if self.minOccurs is 0: return None
+ raise EvaluateException('Required' + tag + ' missing',
+ ps.Backtrace(elt))
+ return ps.FindLocalHREF(href, elt, 0)
+
+ def get_parse_and_errorlist(self):
+ """Get the parselist and human-readable version, errorlist is returned,
+ because it is used in error messages.
+ """
+ d = self.__class__.__dict__
+ parselist = d.get('parselist')
+ errorlist = d.get('errorlist')
+ if parselist and not errorlist:
+ errorlist = []
+ for t in parselist:
+ if t[1] not in errorlist: errorlist.append(t[1])
+ errorlist = ' or '.join(errorlist)
+ d['errorlist'] = errorlist
+ return (parselist, errorlist)
+
+ def checkname(self, elt, ps):
+ '''See if the name and type of the "elt" element is what we're
+ looking for. Return the element's type.
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+
+ parselist,errorlist = self.get_parse_and_errorlist()
+ ns, name = _get_element_nsuri_name(elt)
+ if ns == SOAP.ENC:
+ # Element is in SOAP namespace, so the name is a type.
+ if parselist and \
+ (None, name) not in parselist and (ns, name) not in parselist:
+ raise EvaluateException(
+ 'Element mismatch (got %s wanted %s) (SOAP encoding namespace)' % \
+ (name, errorlist), ps.Backtrace(elt))
+ return (ns, name)
+
+ # Not a type, check name matches.
+ if self.nspname and ns != self.nspname:
+ raise EvaluateException('Element NS mismatch (got %s wanted %s)' % \
+ (ns, self.nspname), ps.Backtrace(elt))
+
+ if self.pname and name != self.pname:
+ raise EvaluateException('Element Name mismatch (got %s wanted %s)' % \
+ (name, self.pname), ps.Backtrace(elt))
+ return self.checktype(elt, ps)
+
+ def checktype(self, elt, ps):
+ '''See if the type of the "elt" element is what we're looking for.
+ Return the element's type.
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ typeName = _find_type(elt)
+ if typeName is None or typeName == "":
+ return (None,None)
+
+ # Parse the QNAME.
+ prefix,typeName = SplitQName(typeName)
+ uri = ps.GetElementNSdict(elt).get(prefix)
+ if uri is None:
+ raise EvaluateException('Malformed type attribute (bad NS)',
+ ps.Backtrace(elt))
+
+ #typeName = list[1]
+ parselist,errorlist = self.get_parse_and_errorlist()
+ if not parselist or \
+ (uri,typeName) in parselist or \
+ (_is_xsd_or_soap_ns(uri) and (None,typeName) in parselist):
+ return (uri,typeName)
+ raise EvaluateException(
+ 'Type mismatch (%s namespace) (got %s wanted %s)' % \
+ (uri, typeName, errorlist), ps.Backtrace(elt))
+
+ def name_match(self, elt):
+ '''Simple boolean test to see if we match the element name.
+ Parameters:
+ elt -- the DOM element being parsed
+ '''
+ return self.pname == elt.localName and \
+ self.nspname in [None, elt.namespaceURI]
+
+ def nilled(self, elt, ps):
+ '''Is the element NIL, and is that okay?
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ if _find_nil(elt) not in [ "true", "1"]: return False
+ if self.nillable is False:
+ raise EvaluateException('Non-nillable element is NIL',
+ ps.Backtrace(elt))
+ return True
+
+ def simple_value(self, elt, ps, mixed=False):
+ '''Get the value of the simple content of this element.
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ mixed -- ignore element content, optional text node
+ '''
+ if not _valid_encoding(elt):
+ raise EvaluateException('Invalid encoding', ps.Backtrace(elt))
+ c = _children(elt)
+ if mixed is False:
+ if len(c) == 0:
+ raise EvaluateException('Value missing', ps.Backtrace(elt))
+ for c_elt in c:
+ if c_elt.nodeType == _Node.ELEMENT_NODE:
+ raise EvaluateException('Sub-elements in value',
+ ps.Backtrace(c_elt))
+
+ # It *seems* to be consensus that ignoring comments and
+ # concatenating the text nodes is the right thing to do.
+ return ''.join([E.nodeValue for E in c
+ if E.nodeType
+ in [ _Node.TEXT_NODE, _Node.CDATA_SECTION_NODE ]])
+
+ def parse_attributes(self, elt, ps):
+ '''find all attributes specified in the attribute_typecode_dict in
+ current element tag, if an attribute is found set it in the
+ self.attributes dictionary. Default to putting in String.
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ if self.attribute_typecode_dict is None:
+ return
+
+ attributes = {}
+ for attr,what in self.attribute_typecode_dict.items():
+ namespaceURI,localName = None,attr
+ if type(attr) in _seqtypes:
+ namespaceURI,localName = attr
+ value = _find_attrNodeNS(elt, namespaceURI, localName)
+ self.logger.debug("Parsed Attribute (%s,%s) -- %s",
+ namespaceURI, localName, value)
+
+ # For Now just set it w/o any type interpretation.
+ if value is None: continue
+ attributes[attr] = what.text_to_data(value, elt, ps)
+
+ return attributes
+
+ def set_attributes(self, el, pyobj):
+ '''Instance data attributes contains a dictionary
+ of keys (namespaceURI,localName) and attribute values.
+ These values can be self-describing (typecode), or use
+ attribute_typecode_dict to determine serialization.
+ Paramters:
+ el -- MessageInterface representing the element
+ pyobj --
+ '''
+ if not hasattr(pyobj, self.attrs_aname):
+ return
+
+ if not isinstance(getattr(pyobj, self.attrs_aname), dict):
+ raise TypeError,\
+ 'pyobj.%s must be a dictionary of names and values'\
+ % self.attrs_aname
+
+ for attr, value in getattr(pyobj, self.attrs_aname).items():
+ namespaceURI,localName = None, attr
+ if type(attr) in _seqtypes:
+ namespaceURI, localName = attr
+
+ what = None
+ if getattr(self, 'attribute_typecode_dict', None) is not None:
+ what = self.attribute_typecode_dict.get(attr)
+ if what is None and namespaceURI is None:
+ what = self.attribute_typecode_dict.get(localName)
+
+ # allow derived type
+ if hasattr(value, 'typecode') and not isinstance(what, AnyType):
+ if what is not None and not isinstance(value.typecode, what):
+ raise EvaluateException, \
+ 'self-describing attribute must subclass %s'\
+ %what.__class__
+
+ what = value.typecode
+
+ self.logger.debug("attribute create -- %s", value)
+ if isinstance(what, QName):
+ what.set_prefix(el, value)
+
+ #format the data
+ if what is None:
+ value = str(value)
+ else:
+ value = what.get_formatted_content(value)
+
+ el.setAttributeNS(namespaceURI, localName, value)
+
+ def set_attribute_xsi_type(self, el, **kw):
+ '''if typed, set the xsi:type attribute
+ Paramters:
+ el -- MessageInterface representing the element
+ '''
+ if kw.get('typed', self.typed):
+ namespaceURI,typeName = kw.get('type', _get_xsitype(self))
+ if namespaceURI and typeName:
+ self.logger.debug("attribute: (%s, %s)", namespaceURI, typeName)
+ el.setAttributeType(namespaceURI, typeName)
+
+ def set_attribute_href(self, el, objid):
+ '''set href attribute
+ Paramters:
+ el -- MessageInterface representing the element
+ objid -- ID type, unique id
+ '''
+ el.setAttributeNS(None, 'href', "#%s" %objid)
+
+ def set_attribute_id(self, el, objid):
+ '''set id attribute
+ Paramters:
+ el -- MessageInterface representing the element
+ objid -- ID type, unique id
+ '''
+ if self.unique is False:
+ el.setAttributeNS(None, 'id', "%s" %objid)
+
+ def get_name(self, name, objid):
+ '''
+ Paramters:
+ name -- element tag
+ objid -- ID type, unique id
+ '''
+ if type(name) is tuple:
+ return name
+
+ ns = self.nspname
+ n = name or self.pname or ('E' + objid)
+ return ns,n
+
+ def has_attributes(self):
+ '''Return True if Attributes are declared outside
+ the scope of SOAP ('root', 'id', 'href'), and some
+ attributes automatically handled (xmlns, xsi:type).
+ '''
+ if self.attribute_typecode_dict is None: return False
+ return len(self.attribute_typecode_dict) > 0
+
+
+class SimpleType(TypeCode):
+ '''SimpleType -- consist exclusively of a tag, attributes, and a value
+ class attributes:
+ empty_content -- value representing an empty element.
+ '''
+ empty_content = None
+ logger = _GetLogger('ZSI.TC.SimpleType')
+
+ def parse(self, elt, ps):
+ self.checkname(elt, ps)
+ if len(_children(elt)) == 0:
+ href = _find_href(elt)
+ if not href:
+ if self.nilled(elt, ps) is False:
+ # No content, no HREF, not NIL: empty string
+ return self.text_to_data(self.empty_content, elt, ps)
+
+ # No content, no HREF, and is NIL...
+ if self.nillable is True:
+ return Nilled
+ raise EvaluateException('Requiredstring missing',
+ ps.Backtrace(elt))
+
+ if href[0] != '#':
+ return ps.ResolveHREF(href, self)
+
+ elt = ps.FindLocalHREF(href, elt)
+ self.checktype(elt, ps)
+ if self.nilled(elt, ps): return Nilled
+ if len(_children(elt)) == 0:
+ v = self.empty_content
+ else:
+ v = self.simple_value(elt, ps)
+ else:
+ v = self.simple_value(elt, ps)
+
+ pyobj = self.text_to_data(v, elt, ps)
+
+ # parse all attributes contained in attribute_typecode_dict
+ # (user-defined attributes), the values (if not None) will
+ # be keyed in self.attributes dictionary.
+ if self.attribute_typecode_dict is not None:
+ attributes = self.parse_attributes(elt, ps)
+ if attributes:
+ setattr(pyobj, self.attrs_aname, attributes)
+
+ return pyobj
+
+ def get_formatted_content(self, pyobj):
+ raise NotImplementedError, 'method get_formatted_content is not implemented'
+
+ def serialize_text_node(self, elt, sw, pyobj):
+ '''Serialize without an element node.
+ '''
+ textNode = None
+ if pyobj is not None:
+ text = self.get_formatted_content(pyobj)
+ if type(text) not in _stringtypes:
+ raise TypeError, 'pyobj must be a formatted string'
+
+ textNode = elt.createAppendTextNode(text)
+
+ return textNode
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ '''Handles the start and end tags, and attributes. callout
+ to get_formatted_content to get the textNode value.
+ Parameters:
+ elt -- ElementProxy/DOM element
+ sw -- SoapWriter instance
+ pyobj -- processed content
+
+ KeyWord Parameters:
+ name -- substitute name, (nspname,name) or name
+ orig --
+
+ '''
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+
+ # nillable
+ el = elt.createAppendElement(ns, n)
+ if self.nillable is True and pyobj is Nilled:
+ self.serialize_as_nil(el)
+ return None
+
+ # other attributes
+ self.set_attributes(el, pyobj)
+
+ # soap href attribute
+ unique = self.unique or kw.get('unique', False)
+ if unique is False and sw.Known(orig or pyobj):
+ self.set_attribute_href(el, objid)
+ return None
+
+ # xsi:type attribute
+ if kw.get('typed', self.typed) is True:
+ self.set_attribute_xsi_type(el, **kw)
+
+ # soap id attribute
+ if self.unique is False:
+ self.set_attribute_id(el, objid)
+
+ #Content, <empty tag/>c
+ self.serialize_text_node(el, sw, pyobj)
+
+ return el
+
+
+class Any(TypeCode):
+ '''When the type isn't defined in the schema, but must be specified
+ in the incoming operation.
+ parsemap -- a type to class mapping (updated by descendants), for
+ parsing
+ serialmap -- same, for (outgoing) serialization
+ '''
+ logger = _GetLogger('ZSI.TC.Any')
+ parsemap, serialmap = {}, {}
+
+ def __init__(self, pname=None, aslist=False, minOccurs=0, **kw):
+ TypeCode.__init__(self, pname, minOccurs=minOccurs, **kw)
+ self.aslist = aslist
+ self.kwargs = {'aslist':aslist}
+ self.kwargs.update(kw)
+ self.unique = False
+
+ # input arg v should be a list of tuples (name, value).
+ def listify(self, v):
+ if self.aslist: return [ k for j,k in v ]
+ return dict(v)
+
+ def parse_into_dict_or_list(self, elt, ps):
+ c = _child_elements(elt)
+ count = len(c)
+ v = []
+ if count == 0:
+ href = _find_href(elt)
+ if not href: return v
+ elt = ps.FindLocalHREF(href, elt)
+ self.checktype(elt, ps)
+ c = _child_elements(elt)
+ count = len(c)
+ if count == 0: return self.listify(v)
+ if self.nilled(elt, ps): return Nilled
+
+ for c_elt in c:
+ v.append((str(c_elt.localName), self.__class__(**self.kwargs).parse(c_elt, ps)))
+
+ return self.listify(v)
+
+ def parse(self, elt, ps):
+ (ns,type) = self.checkname(elt, ps)
+ if not type and self.nilled(elt, ps): return Nilled
+ if len(_children(elt)) == 0:
+ href = _find_href(elt)
+ if not href:
+ if self.minOccurs < 1:
+ if _is_xsd_or_soap_ns(ns):
+ parser = Any.parsemap.get((None,type))
+ if parser: return parser.parse(elt, ps)
+ if ((ns,type) == (SOAP.ENC,'Array') or
+ (_find_arraytype(elt) or '').endswith('[0]')):
+ return []
+ return None
+ raise EvaluateException('Required Any missing',
+ ps.Backtrace(elt))
+ elt = ps.FindLocalHREF(href, elt)
+ (ns,type) = self.checktype(elt, ps)
+ if not type and elt.namespaceURI == SOAP.ENC:
+ ns,type = SOAP.ENC, elt.localName
+ if not type or (ns,type) == (SOAP.ENC,'Array'):
+ if self.aslist or _find_arraytype(elt):
+ return [ self.__class__(**self.kwargs).parse(e, ps)
+ for e in _child_elements(elt) ]
+ if len(_child_elements(elt)) == 0:
+ raise EvaluateException("Any cannot parse untyped element",
+ ps.Backtrace(elt))
+ return self.parse_into_dict_or_list(elt, ps)
+ parser = Any.parsemap.get((ns,type))
+ if not parser and _is_xsd_or_soap_ns(ns):
+ parser = Any.parsemap.get((None,type))
+ if not parser:
+ raise EvaluateException('''Any can't parse element''',
+ ps.Backtrace(elt))
+ return parser.parse(elt, ps)
+
+ def get_formatted_content(self, pyobj):
+ tc = type(pyobj)
+ if tc == types.InstanceType:
+ tc = pyobj.__class__
+ if hasattr(pyobj, 'typecode'):
+ #serializer = pyobj.typecode.serialmap.get(tc)
+ serializer = pyobj.typecode
+ else:
+ serializer = Any.serialmap.get(tc)
+ if not serializer:
+ tc = (types.ClassType, pyobj.__class__.__name__)
+ serializer = Any.serialmap.get(tc)
+ else:
+ serializer = Any.serialmap.get(tc)
+ if not serializer and isinstance(pyobj, time.struct_time):
+ from ZSI.TCtimes import gDateTime
+ serializer = gDateTime()
+ if serializer:
+ return serializer.get_formatted_content(pyobj)
+ raise EvaluateException, 'Failed to find serializer for pyobj %s' %pyobj
+
+ def serialize(self, elt, sw, pyobj, name=None, **kw):
+ if hasattr(pyobj, 'typecode') and pyobj.typecode is not self:
+ pyobj.typecode.serialize(elt, sw, pyobj, **kw)
+ return
+
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ kw.setdefault('typed', self.typed)
+ tc = type(pyobj)
+ self.logger.debug('Any serialize -- %s', tc)
+ if tc in _seqtypes:
+ if self.aslist:
+ array = elt.createAppendElement(ns, n)
+ array.setAttributeType(SOAP.ENC, "Array")
+ array.setAttributeNS(self.nspname, 'SOAP-ENC:arrayType',
+ "xsd:anyType[" + str(len(pyobj)) + "]" )
+ for o in pyobj:
+ #TODO maybe this should take **self.kwargs...
+ serializer = getattr(o, 'typecode', self.__class__()) # also used by _AnyLax()
+ serializer.serialize(array, sw, o, name='element', **kw)
+ else:
+ struct = elt.createAppendElement(ns, n)
+ for o in pyobj:
+ #TODO maybe this should take **self.kwargs...
+ serializer = getattr(o, 'typecode', self.__class__()) # also used by _AnyLax()
+ serializer.serialize(struct, sw, o, **kw)
+ return
+
+ kw['name'] = (ns,n)
+ if tc == types.DictType:
+ el = elt.createAppendElement(ns, n)
+ parentNspname = self.nspname # temporarily clear nspname for dict elements
+ self.nspname = None
+ for o,m in pyobj.items():
+ if type(o) != types.StringType and type(o) != types.UnicodeType:
+ raise Exception, 'Dictionary implementation requires keys to be of type string (or unicode).' %pyobj
+ kw['name'] = o
+ kw.setdefault('typed', True)
+ self.serialize(el, sw, m, **kw)
+ # restore nspname
+ self.nspname = parentNspname
+ return
+
+ if tc == types.InstanceType:
+ tc = pyobj.__class__
+ if hasattr(pyobj, 'typecode'):
+ #serializer = pyobj.typecode.serialmap.get(tc)
+ serializer = pyobj.typecode
+ else:
+ serializer = Any.serialmap.get(tc)
+ if not serializer:
+ tc = (types.ClassType, pyobj.__class__.__name__)
+ serializer = Any.serialmap.get(tc)
+ else:
+ serializer = Any.serialmap.get(tc)
+ if not serializer and isinstance(pyobj, time.struct_time):
+ from ZSI.TCtimes import gDateTime
+ serializer = gDateTime()
+
+ if not serializer:
+ # Last-chance; serialize instances as dictionary
+ if pyobj is None:
+ self.serialize_as_nil(elt.createAppendElement(ns, n))
+ elif type(pyobj) != types.InstanceType:
+ raise EvaluateException('''Any can't serialize ''' + \
+ repr(pyobj))
+ else:
+ self.serialize(elt, sw, pyobj.__dict__, **kw)
+ else:
+ # Try to make the element name self-describing
+ tag = getattr(serializer, 'tag', None)
+ if self.pname is not None:
+ #serializer.nspname = self.nspname
+ #serializer.pname = self.pname
+ if "typed" not in kw:
+ kw['typed'] = False
+ elif tag:
+ if tag.find(':') == -1: tag = 'SOAP-ENC:' + tag
+ kw['name'] = tag
+ kw['typed'] = False
+
+ serializer.unique = self.unique
+ serializer.serialize(elt, sw, pyobj, **kw)
+ # Reset TypeCode
+ #serializer.nspname = None
+ #serializer.pname = None
+
+
+class String(SimpleType):
+ '''A string type.
+ '''
+ empty_content = ''
+ parselist = [ (None,'string') ]
+ seriallist = [ types.StringType, types.UnicodeType ]
+ type = (SCHEMA.XSD3, 'string')
+ logger = _GetLogger('ZSI.TC.String')
+
+ def __init__(self, pname=None, strip=True, **kw):
+ TypeCode.__init__(self, pname, **kw)
+ if kw.has_key('resolver'): self.resolver = kw['resolver']
+ self.strip = strip
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ if self.strip: text = text.strip()
+ if self.pyclass is not None:
+ return self.pyclass(text)
+ return text
+
+ def get_formatted_content(self, pyobj):
+ if type(pyobj) not in _stringtypes:
+ pyobj = str(pyobj)
+ if type(pyobj) == types.UnicodeType: pyobj = pyobj.encode('utf-8')
+ return pyobj
+
+
+class URI(String):
+ '''A URI.
+ '''
+ parselist = [ (None,'anyURI'),(SCHEMA.XSD3, 'anyURI')]
+ type = (SCHEMA.XSD3, 'anyURI')
+ logger = _GetLogger('ZSI.TC.URI')
+
+ def text_to_data(self, text, elt, ps):
+ '''text --> typecode specific data.
+ '''
+ val = String.text_to_data(self, text, elt, ps)
+ return urldecode(val)
+
+ def get_formatted_content(self, pyobj):
+ '''typecode data --> text
+ '''
+ pyobj = String.get_formatted_content(self, pyobj)
+ return urlencode(pyobj)
+
+
+
+class QName(String):
+ '''A QName type
+ '''
+ parselist = [ (None,'QName') ]
+ type = (SCHEMA.XSD3, 'QName')
+ logger = _GetLogger('ZSI.TC.QName')
+
+ def __init__(self, pname=None, strip=1, **kw):
+ String.__init__(self, pname, strip, **kw)
+ self.prefix = None
+
+ def get_formatted_content(self, pyobj):
+ value = pyobj
+ if isinstance(pyobj, tuple):
+ namespaceURI,localName = pyobj
+ if self.prefix is not None:
+ value = "%s:%s" %(self.prefix,localName)
+ return String.get_formatted_content(self, value)
+
+ def set_prefix(self, elt, pyobj):
+ '''use this method to set the prefix of the QName,
+ method looks in DOM to find prefix or set new prefix.
+ This method must be called before get_formatted_content.
+ '''
+ if isinstance(pyobj, tuple):
+ namespaceURI,localName = pyobj
+ self.prefix = elt.getPrefix(namespaceURI)
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ prefix,localName = SplitQName(text)
+ nsdict = ps.GetElementNSdict(elt)
+ try:
+ namespaceURI = nsdict[prefix]
+ except KeyError, ex:
+ raise EvaluateException('cannot resolve prefix(%s)'%prefix,
+ ps.Backtrace(elt))
+
+ v = (namespaceURI,localName)
+ if self.pyclass is not None:
+ return self.pyclass(v)
+ return v
+
+ def serialize_text_node(self, elt, sw, pyobj):
+ '''Serialize without an element node.
+ '''
+ self.set_prefix(elt, pyobj)
+ return String.serialize_text_node(self, elt, sw, pyobj)
+
+
+class Token(String):
+ '''an xsd:token type
+ '''
+ parselist = [ (None, 'token') ]
+ type = (SCHEMA.XSD3, 'token')
+ logger = _GetLogger('ZSI.TC.Token')
+
+
+class Base64String(String):
+ '''A Base64 encoded string.
+ '''
+ parselist = [ (None,'base64Binary'), (SOAP.ENC, 'base64') ]
+ type = (SOAP.ENC, 'base64')
+ logger = _GetLogger('ZSI.TC.Base64String')
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ val = b64decode(text.replace(' ', '').replace('\n','').replace('\r',''))
+ if self.pyclass is not None:
+ return self.pyclass(val)
+ return val
+
+ def get_formatted_content(self, pyobj):
+ pyobj = '\n' + b64encode(pyobj)
+ return String.get_formatted_content(self, pyobj)
+
+
+class Base64Binary(String):
+ parselist = [ (None,'base64Binary'), ]
+ type = (SCHEMA.XSD3, 'base64Binary')
+ logger = _GetLogger('ZSI.TC.Base64Binary')
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ val = b64decode(text)
+ if self.pyclass is not None:
+ return self.pyclass(val)
+ return val
+
+ def get_formatted_content(self, pyobj):
+ pyobj = b64encode(pyobj).strip()
+ return pyobj
+
+
+class HexBinaryString(String):
+ '''Hex-encoded binary (yuk).
+ '''
+ parselist = [ (None,'hexBinary') ]
+ type = (SCHEMA.XSD3, 'hexBinary')
+ logger = _GetLogger('ZSI.TC.HexBinaryString')
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ val = hexdecode(text)
+ if self.pyclass is not None:
+ return self.pyclass(val)
+ return val
+
+ def get_formatted_content(self, pyobj):
+ pyobj = hexencode(pyobj).upper()
+ return String.get_formatted_content(self, pyobj)
+
+
+class XMLString(String):
+ '''A string that represents an XML document
+ '''
+ logger = _GetLogger('ZSI.TC.XMLString')
+
+ def __init__(self, pname=None, readerclass=None, **kw):
+ String.__init__(self, pname, **kw)
+ self.readerclass = readerclass
+
+ def parse(self, elt, ps):
+ if not self.readerclass:
+ from xml.dom.ext.reader import PyExpat
+ self.readerclass = PyExpat.Reader
+ v = String.parse(self, elt, ps)
+ return self.readerclass().fromString(v)
+
+ def get_formatted_content(self, pyobj):
+ pyobj = Canonicalize(pyobj)
+ return String.get_formatted_content(self, pyobj)
+
+
+class Enumeration(String):
+ '''A string type, limited to a set of choices.
+ '''
+ logger = _GetLogger('ZSI.TC.Enumeration')
+
+ def __init__(self, choices, pname=None, **kw):
+ String.__init__(self, pname, **kw)
+ t = type(choices)
+ if t in _seqtypes:
+ self.choices = tuple(choices)
+ elif TypeCode.typechecks:
+ raise TypeError(
+ 'Enumeration choices must be list or sequence, not ' + str(t))
+ if TypeCode.typechecks:
+ for c in self.choices:
+ if type(c) not in _stringtypes:
+ raise TypeError(
+ 'Enumeration choice ' + str(c) + ' is not a string')
+
+ def parse(self, elt, ps):
+ val = String.parse(self, elt, ps)
+ if val not in self.choices:
+ raise EvaluateException('Value not in enumeration list',
+ ps.Backtrace(elt))
+ return val
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ if pyobj not in self.choices:
+ raise EvaluateException('Value not in enumeration list',
+ ps.Backtrace(elt))
+ String.serialize(self, elt, sw, pyobj, name=name, orig=orig, **kw)
+
+
+
+# This is outside the Integer class purely for code esthetics.
+_ignored = []
+
+class Integer(SimpleType):
+ '''Common handling for all integers.
+ '''
+
+ ranges = {
+ 'unsignedByte': (0, 255),
+ 'unsignedShort': (0, 65535),
+ 'unsignedInt': (0, 4294967295L),
+ 'unsignedLong': (0, 18446744073709551615L),
+
+ 'byte': (-128, 127),
+ 'short': (-32768, 32767),
+ 'int': (-2147483648L, 2147483647),
+ 'long': (-9223372036854775808L, 9223372036854775807L),
+
+ 'negativeInteger': (_ignored, -1),
+ 'nonPositiveInteger': (_ignored, 0),
+ 'nonNegativeInteger': (0, _ignored),
+ 'positiveInteger': (1, _ignored),
+
+ 'integer': (_ignored, _ignored)
+ }
+ parselist = [ (None,k) for k in ranges.keys() ]
+ seriallist = [ types.IntType, types.LongType ]
+ logger = _GetLogger('ZSI.TC.Integer')
+
+ def __init__(self, pname=None, format='%d', **kw):
+ TypeCode.__init__(self, pname, **kw)
+ self.format = format
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ if self.pyclass is not None:
+ v = self.pyclass(text)
+ else:
+ try:
+ v = int(text)
+ except:
+ try:
+ v = long(text)
+ except:
+ raise EvaluateException('Unparseable integer',
+ ps.Backtrace(elt))
+ return v
+
+ def parse(self, elt, ps):
+ (ns,type) = self.checkname(elt, ps)
+ if self.nilled(elt, ps): return Nilled
+ elt = self.SimpleHREF(elt, ps, 'integer')
+ if not elt: return None
+
+ if type is None:
+ type = self.type[1]
+ elif self.type[1] is not None and type != self.type[1]:
+ raise EvaluateException('Integer type mismatch; ' \
+ 'got %s wanted %s' % (type,self.type[1]), ps.Backtrace(elt))
+
+ v = self.simple_value(elt, ps)
+ v = self.text_to_data(v, elt, ps)
+
+ (rmin, rmax) = Integer.ranges.get(type, (_ignored, _ignored))
+ if rmin != _ignored and v < rmin:
+ raise EvaluateException('Underflow, less than ' + repr(rmin),
+ ps.Backtrace(elt))
+ if rmax != _ignored and v > rmax:
+ raise EvaluateException('Overflow, greater than ' + repr(rmax),
+ ps.Backtrace(elt))
+ return v
+
+ def get_formatted_content(self, pyobj):
+ return self.format %pyobj
+
+
+
+# See credits, below.
+def _make_inf():
+ x = 2.0
+ x2 = x * x
+ i = 0
+ while i < 100 and x != x2:
+ x = x2
+ x2 = x * x
+ i = i + 1
+ if x != x2:
+ raise ValueError("This machine's floats go on forever!")
+ return x
+
+# This is outside the Decimal class purely for code esthetics.
+_magicnums = { }
+try:
+ _magicnums['INF'] = float('INF')
+ _magicnums['-INF'] = float('-INF')
+except:
+ _magicnums['INF'] = _make_inf()
+ _magicnums['-INF'] = -_magicnums['INF']
+
+# The following comment and code was written by Tim Peters in
+# article <001401be92d2$09dcb800$5fa02299@tim> in comp.lang.python,
+# also available at the following URL:
+# http://groups.google.com/groups?selm=001401be92d2%2409dcb800%245fa02299%40tim
+# Thanks, Tim!
+
+# NaN-testing.
+#
+# The usual method (x != x) doesn't work.
+# Python forces all comparisons thru a 3-outcome cmp protocol; unordered
+# isn't a possible outcome. The float cmp outcome is essentially defined
+# by this C expression (combining some cross-module implementation
+# details, and where px and py are pointers to C double):
+# px == py ? 0 : *px < *py ? -1 : *px > *py ? 1 : 0
+# Comparing x to itself thus always yields 0 by the first clause, and so
+# x != x is never true.
+# If px and py point to distinct NaN objects, a strange thing happens:
+# 1. On scrupulous 754 implementations, *px < *py returns false, and so
+# does *px > *py. Python therefore returns 0, i.e. "equal"!
+# 2. On Pentium HW, an unordered outcome sets an otherwise-impossible
+# combination of condition codes, including both the "less than" and
+# "equal to" flags. Microsoft C generates naive code that accepts
+# the "less than" flag at face value, and so the *px < *py clause
+# returns true, and Python returns -1, i.e. "not equal".
+# So with a proper C 754 implementation Python returns the wrong result,
+# and under MS's improper 754 implementation Python yields the right
+# result -- both by accident. It's unclear who should be shot <wink>.
+#
+# Anyway, the point of all that was to convince you it's tricky getting
+# the right answer in a portable way!
+def isnan(x):
+ """x -> true iff x is a NaN."""
+ # multiply by 1.0 to create a distinct object (x < x *always*
+ # false in Python, due to object identity forcing equality)
+ if x * 1.0 < x:
+ # it's a NaN and this is MS C on a Pentium
+ return 1
+ # Else it's non-NaN, or NaN on a non-MS+Pentium combo.
+ # If it's non-NaN, then x == 1.0 and x == 2.0 can't both be true,
+ # so we return false. If it is NaN, then assuming a good 754 C
+ # implementation Python maps both unordered outcomes to true.
+ return 1.0 == x and x == 2.0
+
+
+class Decimal(SimpleType):
+ '''Parent class for floating-point numbers.
+ '''
+
+ parselist = [ (None,'decimal'), (None,'float'), (None,'double') ]
+ seriallist = _floattypes
+ type = None
+ ranges = {
+ 'float': ( 7.0064923216240861E-46,
+ -3.4028234663852886E+38, 3.4028234663852886E+38 ),
+ 'double': ( 2.4703282292062327E-324,
+ -1.7976931348623158E+308, 1.7976931348623157E+308),
+ }
+ zeropat = re.compile('[1-9]')
+ logger = _GetLogger('ZSI.TC.Decimal')
+
+ def __init__(self, pname=None, format='%f', **kw):
+ TypeCode.__init__(self, pname, **kw)
+ self.format = format
+
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ v = text
+ if self.pyclass is not None:
+ return self.pyclass(v)
+
+ m = _magicnums.get(v)
+ if m: return m
+
+ try:
+ return float(v)
+ except:
+ raise EvaluateException('Unparseable floating point number',
+ ps.Backtrace(elt))
+
+ def parse(self, elt, ps):
+ (ns,type) = self.checkname(elt, ps)
+ elt = self.SimpleHREF(elt, ps, 'floating-point')
+ if not elt: return None
+ tag = getattr(self.__class__, 'type')
+ if tag:
+ if type is None:
+ type = tag
+ elif tag != (ns,type):
+ raise EvaluateException('Floating point type mismatch; ' \
+ 'got (%s,%s) wanted %s' % (ns,type,tag), ps.Backtrace(elt))
+ # Special value?
+ if self.nilled(elt, ps): return Nilled
+ v = self.simple_value(elt, ps)
+ try:
+ fp = self.text_to_data(v, elt, ps)
+ except EvaluateException, ex:
+ ex.args.append(ps.Backtrace(elt))
+ raise ex
+
+ m = _magicnums.get(v)
+ if m:
+ return m
+
+ if str(fp).lower() in [ 'inf', '-inf', 'nan', '-nan' ]:
+ raise EvaluateException('Floating point number parsed as "' + \
+ str(fp) + '"', ps.Backtrace(elt))
+ if fp == 0 and Decimal.zeropat.search(v):
+ raise EvaluateException('Floating point number parsed as zero',
+ ps.Backtrace(elt))
+ (rtiny, rneg, rpos) = Decimal.ranges.get(type, (None, None, None))
+ if rneg and fp < 0 and fp < rneg:
+ raise EvaluateException('Negative underflow', ps.Backtrace(elt))
+ if rtiny and fp > 0 and fp < rtiny:
+ raise EvaluateException('Positive underflow', ps.Backtrace(elt))
+ if rpos and fp > 0 and fp > rpos:
+ raise EvaluateException('Overflow', ps.Backtrace(elt))
+ return fp
+
+ def get_formatted_content(self, pyobj):
+ if pyobj == _magicnums['INF']:
+ return 'INF'
+ elif pyobj == _magicnums['-INF']:
+ return '-INF'
+ elif isnan(pyobj):
+ return 'NaN'
+ else:
+ return self.format %pyobj
+
+
+class Boolean(SimpleType):
+ '''A boolean.
+ '''
+
+ parselist = [ (None,'boolean') ]
+ seriallist = [ bool ]
+ type = (SCHEMA.XSD3, 'boolean')
+ logger = _GetLogger('ZSI.TC.Boolean')
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ v = text
+ if v == 'false':
+ if self.pyclass is None:
+ return False
+ return self.pyclass(False)
+
+ if v == 'true':
+ if self.pyclass is None:
+ return True
+ return self.pyclass(True)
+
+ try:
+ v = int(v)
+ except:
+ try:
+ v = long(v)
+ except:
+ raise EvaluateException('Unparseable boolean',
+ ps.Backtrace(elt))
+
+ if v:
+ if self.pyclass is None:
+ return True
+ return self.pyclass(True)
+
+ if self.pyclass is None:
+ return False
+ return self.pyclass(False)
+
+ def parse(self, elt, ps):
+ self.checkname(elt, ps)
+ elt = self.SimpleHREF(elt, ps, 'boolean')
+ if not elt: return None
+ if self.nilled(elt, ps): return Nilled
+
+ v = self.simple_value(elt, ps).lower()
+ return self.text_to_data(v, elt, ps)
+
+ def get_formatted_content(self, pyobj):
+ if pyobj: return 'true'
+ return 'false'
+
+
+#XXX NOT FIXED YET
+class XML(TypeCode):
+ '''Opaque XML which shouldn't be parsed.
+ comments -- preserve comments
+ inline -- don't href/id when serializing
+ resolver -- object to resolve href's
+ wrapped -- put a wrapper element around it
+ '''
+
+ # Clone returned data?
+ copyit = 0
+ logger = _GetLogger('ZSI.TC.XML')
+
+ def __init__(self, pname=None, comments=0, inline=0, wrapped=True, **kw):
+ TypeCode.__init__(self, pname, **kw)
+ self.comments = comments
+ self.inline = inline
+ if kw.has_key('resolver'): self.resolver = kw['resolver']
+ self.wrapped = wrapped
+ self.copyit = kw.get('copyit', XML.copyit)
+
+ def parse(self, elt, ps):
+ if self.wrapped is False:
+ return elt
+ c = _child_elements(elt)
+ if not c:
+ href = _find_href(elt)
+ if not href:
+ if self.minOccurs == 0: return None
+ raise EvaluateException('Embedded XML document missing',
+ ps.Backtrace(elt))
+ if href[0] != '#':
+ return ps.ResolveHREF(href, self)
+ elt = ps.FindLocalHREF(href, elt)
+ c = _child_elements(elt)
+ if _find_encstyle(elt) != "":
+ #raise EvaluateException('Embedded XML has unknown encodingStyle',
+ # ps.Backtrace(elt)
+ pass
+ if len(c) != 1:
+ raise EvaluateException('Embedded XML has more than one child',
+ ps.Backtrace(elt))
+ if self.copyit: return c[0].cloneNode(1)
+ return c[0]
+
+ def serialize(self, elt, sw, pyobj, name=None, unsuppressedPrefixes=[], **kw):
+ if self.wrapped is False:
+ Canonicalize(pyobj, sw, unsuppressedPrefixes=unsuppressedPrefixes,
+ comments=self.comments)
+ return
+
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ xmlelt = elt.createAppendElement(ns, n)
+
+ if type(pyobj) in _stringtypes:
+ self.set_attributes(xmlelt, pyobj)
+ self.set_attribute_href(xmlelt, objid)
+ elif kw.get('inline', self.inline):
+ self.cb(xmlelt, sw, pyobj, unsuppressedPrefixes)
+ else:
+ self.set_attributes(xmlelt, pyobj)
+ self.set_attribute_href(xmlelt, objid)
+ sw.AddCallback(self.cb, pyobj, unsuppressedPrefixes)
+
+ def cb(self, elt, sw, pyobj, unsuppressedPrefixes=[]):
+ if sw.Known(pyobj):
+ return
+
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ xmlelt = elt.createAppendElement(ns, n)
+ self.set_attribute_id(xmlelt, objid)
+ xmlelt.setAttributeNS(SOAP.ENC, 'encodingStyle', '""')
+ Canonicalize(pyobj, sw, unsuppressedPrefixes=unsuppressedPrefixes,
+ comments=self.comments)
+
+
+class AnyType(TypeCode):
+ """XML Schema xsi:anyType type definition wildCard.
+ class variables:
+ all -- specifies use of all namespaces.
+ other -- specifies use of other namespaces
+ type --
+ """
+ all = '#all'
+ other = '#other'
+ type = (SCHEMA.XSD3, 'anyType')
+ logger = _GetLogger('ZSI.TC.AnyType')
+
+ def __init__(self, pname=None, namespaces=['#all'],
+ minOccurs=1, maxOccurs=1, strip=1, **kw):
+ TypeCode.__init__(self, pname=pname, minOccurs=minOccurs,
+ maxOccurs=maxOccurs, **kw)
+ self.namespaces = namespaces
+
+ def get_formatted_content(self, pyobj):
+ # TODO: not sure this makes sense,
+ # parse side will be clueless, but oh well..
+ what = getattr(pyobj, 'typecode', Any())
+ return what.get_formatted_content(pyobj)
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data. Used only with
+ attributes so will not know anything about this content so
+ why guess?
+ Parameters:
+ text -- text content
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ return text
+
+ def serialize(self, elt, sw, pyobj, **kw):
+ nsuri,typeName = _get_xsitype(pyobj)
+ if self.all not in self.namespaces and nsuri not in self.namespaces:
+ raise EvaluateException(
+ '<anyType> unsupported use of namespaces "%s"' %self.namespaces)
+
+ what = getattr(pyobj, 'typecode', None)
+ if what is None:
+ # TODO: resolve this, "strict" processing but no
+ # concrete schema makes little sense.
+ #what = _AnyStrict(pname=(self.nspname,self.pname))
+ what = Any(pname=(self.nspname,self.pname), unique=True,
+ aslist=False)
+ kw['typed'] = True
+ what.serialize(elt, sw, pyobj, **kw)
+ return
+
+ # Namespace if element AnyType was namespaced.
+ what.serialize(elt, sw, pyobj,
+ name=(self.nspname or what.nspname, self.pname or what.pname), **kw)
+
+ def parse(self, elt, ps):
+ #element name must be declared ..
+ nspname,pname = _get_element_nsuri_name(elt)
+ if nspname != self.nspname or pname != self.pname:
+ raise EvaluateException('<anyType> instance is (%s,%s) found (%s,%s)' %(
+ self.nspname,self.pname,nspname,pname), ps.Backtrace(elt))
+
+ #locate xsi:type
+ prefix, typeName = SplitQName(_find_type(elt))
+ namespaceURI = _resolve_prefix(elt, prefix)
+ pyclass = GTD(namespaceURI, typeName)
+ if not pyclass:
+ if _is_xsd_or_soap_ns(namespaceURI):
+ pyclass = _AnyStrict
+ elif (str(namespaceURI).lower()==str(Apache.Map.type[0]).lower())\
+ and (str(typeName).lower() ==str(Apache.Map.type[1]).lower()):
+ pyclass = Apache.Map
+ else:
+ # Unknown type, so parse into a dictionary
+ pyobj = Any().parse_into_dict_or_list(elt, ps)
+ return pyobj
+
+ what = pyclass(pname=(self.nspname,self.pname))
+ pyobj = what.parse(elt, ps)
+ return pyobj
+
+
+class AnyElement(AnyType):
+ """XML Schema xsi:any element declaration wildCard.
+ class variables:
+ tag -- global element declaration
+ """
+ tag = (SCHEMA.XSD3, 'any')
+ logger = _GetLogger('ZSI.TC.AnyElement')
+
+ def __init__(self, namespaces=['#all'],pname=None,
+ minOccurs=1, maxOccurs=1, strip=1, processContents='strict',
+ **kw):
+
+ if processContents not in ('lax', 'skip', 'strict'):
+ raise ValueError('processContents(%s) must be lax, skip, or strict')
+
+ self.processContents = processContents
+ AnyType.__init__(self, namespaces=namespaces,pname=pname,
+ minOccurs=minOccurs, maxOccurs=maxOccurs, strip=strip, **kw)
+
+ def serialize(self, elt, sw, pyobj, **kw):
+ '''Must provice typecode to AnyElement for serialization, else
+ try to use TC.Any to serialize instance which will serialize
+ based on the data type of pyobj w/o reference to XML schema
+ instance.
+ '''
+ if isinstance(pyobj, TypeCode):
+ raise TypeError, 'pyobj is a typecode instance.'
+
+ what = getattr(pyobj, 'typecode', None)
+ if what is not None and type(pyobj) is types.InstanceType:
+ tc = pyobj.__class__
+ what = Any.serialmap.get(tc)
+ if not what:
+ tc = (types.ClassType, pyobj.__class__.__name__)
+ what = Any.serialmap.get(tc)
+
+ # failed to find a registered type for class
+ if what is None:
+ #TODO: seems incomplete. what about facets.
+ if self.processContents == 'strict':
+ what = _AnyStrict(pname=(self.nspname,self.pname))
+ else:
+ what = _AnyLax(pname=(self.nspname,self.pname))
+
+ self.logger.debug('serialize with %s', what.__class__.__name__)
+ what.serialize(elt, sw, pyobj, **kw)
+
+ def parse(self, elt, ps):
+ '''
+ processContents -- 'lax' | 'skip' | 'strict', 'strict'
+ 1) if 'skip' check namespaces, and return the DOM node.
+ 2) if 'lax' look for declaration, or definition. If
+ not found return DOM node.
+ 3) if 'strict' get declaration, or raise.
+ '''
+ skip = self.processContents == 'skip'
+ nspname,pname = _get_element_nsuri_name(elt)
+ what = GED(nspname, pname)
+ if not skip and what is not None:
+ pyobj = what.parse(elt, ps)
+ try:
+ pyobj.typecode = what
+ except AttributeError, ex:
+ # Assume this means builtin type.
+ pyobj = WrapImmutable(pyobj, what)
+ return pyobj
+
+ # Allow use of "<any>" element declarations w/ local
+ # element declarations
+ prefix, typeName = SplitQName(_find_type(elt))
+ if not skip and typeName:
+ namespaceURI = _resolve_prefix(elt, prefix or 'xmlns')
+ # First look thru user defined namespaces, if don't find
+ # look for 'primitives'.
+ pyclass = GTD(namespaceURI, typeName) or Any
+ what = pyclass(pname=(nspname,pname))
+ pyobj = what.parse(elt, ps)
+ try:
+ pyobj.typecode = what
+ except AttributeError, ex:
+ # Assume this means builtin type.
+ pyobj = WrapImmutable(pyobj, what)
+
+ what.typed = True
+ return pyobj
+
+ if skip:
+ what = XML(pname=(nspname,pname), wrapped=False)
+ elif self.processContents == 'lax':
+ what = _AnyLax(pname=(nspname,pname))
+ else:
+ what = _AnyStrict(pname=(nspname,pname))
+
+ try:
+ pyobj = what.parse(elt, ps)
+ except EvaluateException, ex:
+ self.logger.error("Give up, parse (%s,%s) as a String",
+ what.nspname, what.pname)
+ what = String(pname=(nspname,pname), typed=False)
+ pyobj = WrapImmutable(what.parse(elt, ps), what)
+
+ return pyobj
+
+
+class Union(SimpleType):
+ '''simpleType Union
+
+ class variables:
+ memberTypes -- list [(namespace,name),] tuples, each representing a type defintion.
+ '''
+ memberTypes = None
+ logger = _GetLogger('ZSI.TC.Union')
+
+ def __init__(self, pname=None, minOccurs=1, maxOccurs=1, **kw):
+ SimpleType.__init__(self, pname=pname, minOccurs=minOccurs, maxOccurs=maxOccurs, **kw)
+ self.memberTypeCodes = []
+
+ def setMemberTypeCodes(self):
+ if len(self.memberTypeCodes) > 0:
+ return
+ if self.__class__.memberTypes is None:
+ raise EvaluateException, 'uninitialized class variable memberTypes [(namespace,name),]'
+ for nsuri,name in self.__class__.memberTypes:
+ tcclass = GTD(nsuri,name)
+ if tcclass is None:
+ tc = Any.parsemap.get((nsuri,name))
+ typecode = tc.__class__(pname=(self.nspname,self.pname))
+ else:
+ typecode = tcclass(pname=(self.nspname,self.pname))
+
+ if typecode is None:
+ raise EvaluateException, \
+ 'Typecode class for Union memberType (%s,%s) is missing' %(nsuri,name)
+ if isinstance(typecode, Struct):
+ raise EvaluateException, \
+ 'Illegal: Union memberType (%s,%s) is complexType' %(nsuri,name)
+ self.memberTypeCodes.append(typecode)
+
+ def parse(self, elt, ps, **kw):
+ '''attempt to parse sequentially. No way to know ahead of time
+ what this instance represents. Must be simple type so it can
+ not have attributes nor children, so this isn't too bad.
+ '''
+ self.setMemberTypeCodes()
+ (nsuri,typeName) = self.checkname(elt, ps)
+
+ #if (nsuri,typeName) not in self.memberTypes:
+ # raise EvaluateException(
+ # 'Union Type mismatch got (%s,%s) not in %s' % \
+ # (nsuri, typeName, self.memberTypes), ps.Backtrace(elt))
+
+ for indx in range(len(self.memberTypeCodes)):
+ typecode = self.memberTypeCodes[indx]
+ try:
+ pyobj = typecode.parse(elt, ps)
+ except ParseException, ex:
+ continue
+ except Exception, ex:
+ continue
+
+ if indx > 0:
+ self.memberTypeCodes.remove(typecode)
+ self.memberTypeCodes.insert(0, typecode)
+ break
+
+ else:
+ raise
+
+ return pyobj
+
+ def get_formatted_content(self, pyobj, **kw):
+ self.setMemberTypeCodes()
+ for indx in range(len(self.memberTypeCodes)):
+ typecode = self.memberTypeCodes[indx]
+ try:
+ content = typecode.get_formatted_content(copy.copy(pyobj))
+ break
+ except ParseException, ex:
+ pass
+
+ if indx > 0:
+ self.memberTypeCodes.remove(typecode)
+ self.memberTypeCodes.insert(0, typecode)
+
+ else:
+ raise
+
+ return content
+
+
+class List(SimpleType):
+ '''simpleType List
+ Class data:
+ itemType -- sequence (namespaceURI,name) or a TypeCode instance
+ representing the type definition
+ '''
+ itemType = None
+ logger = _GetLogger('ZSI.TC.List')
+
+ def __init__(self, pname=None, itemType=None, **kw):
+ '''Currently need to require maxOccurs=1, so list
+ is interpreted as a single unit of data.
+ '''
+ assert kw.get('maxOccurs',1) == 1, \
+ 'Currently only supporting SimpleType Lists with maxOccurs=1'
+
+ SimpleType.__init__(self, pname=pname, **kw)
+ self.itemType = itemType or self.itemType
+ self.itemTypeCode = self.itemType
+
+ itemTypeCode = None
+ if type(self.itemTypeCode) in _seqtypes:
+ namespaceURI,name = self.itemTypeCode
+ try:
+ itemTypeCode = GTD(*self.itemType)(None)
+ except:
+ if _is_xsd_or_soap_ns(namespaceURI) is False:
+ raise
+ for pyclass in TYPES:
+ if pyclass.type == self.itemTypeCode:
+ itemTypeCode = pyclass(None)
+ break
+ elif pyclass.type[1] == name:
+ itemTypeCode = pyclass(None)
+
+ if itemTypeCode is None:
+ raise EvaluateException('Filed to locate %s' %self.itemTypeCode)
+
+ if hasattr(itemTypeCode, 'text_to_data') is False:
+ raise EvaluateException('TypeCode class %s missing text_to_data method' %itemTypeCode)
+
+ self.itemTypeCode = itemTypeCode
+
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data. items in
+ list are space separated.
+ '''
+ v = []
+ for item in text.split(' '):
+ v.append(self.itemTypeCode.text_to_data(item, elt, ps))
+
+ if self.pyclass is not None:
+ return self.pyclass(v)
+ return v
+
+ def parse(self, elt, ps):
+ '''elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ self.checkname(elt, ps)
+ if len(_children(elt)) == 0:
+ href = _find_href(elt)
+ if not href:
+ if self.nilled(elt, ps) is False:
+ # No content, no HREF, not NIL: empty string
+ return ""
+ # No content, no HREF, and is NIL...
+ if self.nillable is True:
+ return Nilled
+ raise EvaluateException('Required string missing',
+ ps.Backtrace(elt))
+ if href[0] != '#':
+ return ps.ResolveHREF(href, self)
+ elt = ps.FindLocalHREF(href, elt)
+ self.checktype(elt, ps)
+
+ if self.nilled(elt, ps): return Nilled
+ if len(_children(elt)) == 0: return ''
+
+ v = self.simple_value(elt, ps)
+ return self.text_to_data(v, elt, ps)
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ '''elt -- the current DOMWrapper element
+ sw -- soapWriter object
+ pyobj -- python object to serialize
+ '''
+ if type(pyobj) not in _seqtypes:
+ raise EvaluateException, 'expecting a list'
+
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ el = elt.createAppendElement(ns, n)
+ if self.nillable is True and pyobj is None:
+ self.serialize_as_nil(el)
+ return None
+
+ tc = self.itemTypeCode
+ s = StringIO()
+ for item in pyobj:
+ s.write(tc.get_formatted_content(item))
+ s.write(' ')
+
+ el.createAppendTextNode(textNode)
+
+
+class _AnyStrict(Any):
+ ''' Handles an unspecified types when using a concrete schemas and
+ processContents = "strict".
+ '''
+ #WARNING: unstable
+ logger = _GetLogger('ZSI.TC._AnyStrict')
+
+ def __init__(self, pname=None, aslist=False, **kw):
+ TypeCode.__init__(self, pname=pname, **kw)
+ self.aslist = aslist
+ self.unique = True
+
+ def serialize(self, elt, sw, pyobj, name=None, **kw):
+ if not (type(pyobj) is dict and not self.aslist):
+ Any.serialize(self, elt=elt,sw=sw,pyobj=pyobj,name=name, **kw)
+
+ raise EvaluateException(
+ 'Serializing dictionaries not implemented when processContents=\"strict\".' +
+ 'Try as a list or use processContents=\"lax\".'
+ )
+
+
+class _AnyLax(Any):
+ ''' Handles unspecified types when using a concrete schemas and
+ processContents = "lax".
+ '''
+ logger = _GetLogger('ZSI.TC._AnyLax')
+
+ def __init__(self, pname=None, aslist=False, **kw):
+ TypeCode.__init__(self, pname=pname, **kw)
+ self.aslist = aslist
+ self.unique = True
+
+ def parse_into_dict_or_list(self, elt, ps):
+ c = _child_elements(elt)
+ count = len(c)
+ v = []
+ if count == 0:
+ href = _find_href(elt)
+ if not href: return {}
+ elt = ps.FindLocalHREF(href, elt)
+ self.checktype(elt, ps)
+ c = _child_elements(elt)
+ count = len(c)
+ if count == 0: return self.listify([])
+ if self.nilled(elt, ps): return Nilled
+
+ # group consecutive elements with the same name together
+ # We treat consecutive elements with the same name as lists.
+ groupedElements = [] # tuples of (name, elementList)
+ previousName = ""
+ currentElementList = None
+ for ce in _child_elements(elt):
+ name = ce.localName
+ if (name != previousName): # new name, so new group
+ if currentElementList != None: # store previous group if there is one
+ groupedElements.append( (previousName, currentElementList) )
+ currentElementList = list()
+ currentElementList.append(ce) # append to list
+ previousName = name
+ # add the last group if necessary
+ if currentElementList != None: # store previous group if there is one
+ groupedElements.append( (previousName, currentElementList) )
+
+ # parse the groups of names
+ if len(groupedElements) < 1: # should return earlier
+ return None
+ # return a list if there is one name and multiple data
+ elif (len(groupedElements) == 1) and (len(groupedElements[0][0]) > 1):
+ self.aslist = False
+ # else return a dictionary
+
+ for name,eltList in groupedElements:
+ lst = []
+ for elt in eltList:
+ #aslist = self.aslist
+ lst.append( self.parse(elt, ps) )
+ #self.aslist = aslist # restore the aslist setting
+ if len(lst) > 1: # consecutive elements with the same name means a list
+ v.append( (name, lst) )
+ elif len(lst) == 1:
+ v.append( (name, lst[0]) )
+
+ return self.listify(v)
+
+ def checkname(self, elt, ps):
+ '''See if the name and type of the "elt" element is what we're
+ looking for. Return the element's type.
+ Since this is _AnyLax, it's ok if names don't resolve.
+ '''
+
+ parselist,errorlist = self.get_parse_and_errorlist()
+ ns, name = _get_element_nsuri_name(elt)
+ if ns == SOAP.ENC:
+ if parselist and \
+ (None, name) not in parselist and (ns, name) not in parselist:
+ raise EvaluateException(
+ 'Element mismatch (got %s wanted %s) (SOAP encoding namespace)' % \
+ (name, errorlist), ps.Backtrace(elt))
+ return (ns, name)
+
+ # Not a type, check name matches.
+ if self.nspname and ns != self.nspname:
+ raise EvaluateException('Element NS mismatch (got %s wanted %s)' % \
+ (ns, self.nspname), ps.Backtrace(elt))
+
+ return self.checktype(elt, ps)
+
+
+def RegisterType(C, clobber=0, *args, **keywords):
+ instance = apply(C, args, keywords)
+ for t in C.__dict__.get('parselist', []):
+ prev = Any.parsemap.get(t)
+ if prev:
+ if prev.__class__ == C: continue
+ if not clobber:
+ raise TypeError(
+ str(C) + ' duplicating parse registration for ' + str(t))
+ Any.parsemap[t] = instance
+ for t in C.__dict__.get('seriallist', []):
+ ti = type(t)
+ if ti in [ types.TypeType, types.ClassType]:
+ key = t
+ elif ti in _stringtypes:
+ key = (types.ClassType, t)
+ else:
+ raise TypeError(str(t) + ' is not a class name')
+ prev = Any.serialmap.get(key)
+ if prev:
+ if prev.__class__ == C: continue
+ if not clobber:
+ raise TypeError(
+ str(C) + ' duplicating serial registration for ' + str(t))
+ Any.serialmap[key] = instance
+
+
+from TCnumbers import *
+from TCtimes import *
+from schema import GTD, GED, WrapImmutable
+from TCcompound import *
+from TCapache import *
+
+# aliases backwards compatiblity
+_get_type_definition, _get_global_element_declaration, Wrap = GTD, GED, WrapImmutable
+
+f = lambda x: type(x) == types.ClassType and issubclass(x, TypeCode) and getattr(x, 'type', None) is not None
+TYPES = filter(f, map(lambda y:eval(y),dir()))
+
+
+if __name__ == '__main__': print _copyright
+
diff --git a/ZSI/TCapache.py b/ZSI/TCapache.py
new file mode 100644
index 0000000..fe39a00
--- /dev/null
+++ b/ZSI/TCapache.py
@@ -0,0 +1,75 @@
+#! /usr/bin/env python
+# $Header$
+'''Apache typecodes.
+'''
+
+from ZSI import _copyright, _child_elements, _get_idstr
+from ZSI.TC import TypeCode, Struct as _Struct, Any as _Any
+
+class Apache:
+ NS = "http://xml.apache.org/xml-soap"
+
+class _Map(TypeCode):
+ '''Apache's "Map" type.
+ '''
+ parselist = [ (Apache.NS, 'Map') ]
+
+ def __init__(self, pname=None, aslist=0, **kw):
+ TypeCode.__init__(self, pname, **kw)
+ self.aslist = aslist
+ self.tc = _Struct(None, [ _Any('key'), _Any('value') ], inline=1)
+
+ def parse(self, elt, ps):
+ self.checkname(elt, ps)
+ if self.nilled(elt, ps): return None
+ p = self.tc.parse
+ if self.aslist:
+ v = []
+ for c in _child_elements(elt):
+ d = p(c, ps)
+ v.append((d['key'], d['value']))
+ else:
+ v = {}
+ for c in _child_elements(elt):
+ d = p(c, ps)
+ v[d['key']] = d['value']
+ return v
+
+ def serialize(self, elt, sw, pyobj, name=None, **kw):
+ objid = _get_idstr(pyobj)
+ n = name or self.pname or ('E' + objid)
+
+ # nillable
+ el = elt.createAppendElement(self.nspname, n)
+ if self.nillable is True and pyobj is None:
+ self.serialize_as_nil(el)
+ return None
+
+ # other attributes
+ self.set_attributes(el, pyobj)
+
+ # soap href attribute
+ unique = self.unique or kw.get('unique', False)
+ if unique is False and sw.Known(orig or pyobj):
+ self.set_attribute_href(el, objid)
+ return None
+
+ # xsi:type attribute
+ if kw.get('typed', self.typed) is True:
+ self.set_attribute_xsi_type(el, **kw)
+
+ # soap id attribute
+ if self.unique is False:
+ self.set_attribute_id(el, objid)
+
+ if self.aslist:
+ for k,v in pyobj:
+ self.tc.serialize(el, sw, {'key': k, 'value': v}, name='item')
+ else:
+ for k,v in pyobj.items():
+ self.tc.serialize(el, sw, {'key': k, 'value': v}, name='item')
+
+
+Apache.Map = _Map
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/TCcompound.py b/ZSI/TCcompound.py
new file mode 100644
index 0000000..905d090
--- /dev/null
+++ b/ZSI/TCcompound.py
@@ -0,0 +1,674 @@
+#! /usr/bin/env python
+# $Header$
+'''Compound typecodes.
+'''
+
+from ZSI import _copyright, _children, _child_elements, \
+ _inttypes, _stringtypes, _seqtypes, _find_arraytype, _find_href, \
+ _find_type, _find_xmlns_prefix, _get_idstr, EvaluateException, \
+ ParseException
+
+from TC import _get_element_nsuri_name, \
+ _get_xsitype, TypeCode, Any, AnyElement, AnyType, \
+ Nilled, UNBOUNDED
+
+from schema import ElementDeclaration, TypeDefinition, \
+ _get_substitute_element, _get_type_definition
+
+from ZSI.wstools.Namespaces import SCHEMA, SOAP
+from ZSI.wstools.Utility import SplitQName
+from ZSI.wstools.logging import getLogger as _GetLogger
+import re, types
+
+_find_arrayoffset = lambda E: E.getAttributeNS(SOAP.ENC, "offset")
+_find_arrayposition = lambda E: E.getAttributeNS(SOAP.ENC, "position")
+
+_offset_pat = re.compile(r'\[[0-9]+\]')
+_position_pat = _offset_pat
+
+def _check_typecode_list(ofwhat, tcname):
+ '''Check a list of typecodes for compliance with Struct
+ requirements.'''
+ for o in ofwhat:
+ if callable(o): #skip if _Mirage
+ continue
+ if not isinstance(o, TypeCode):
+ raise TypeError(
+ tcname + ' ofwhat outside the TypeCode hierarchy, ' +
+ str(o.__class__))
+ if o.pname is None and not isinstance(o, AnyElement):
+ raise TypeError(tcname + ' element ' + str(o) + ' has no name')
+
+
+def _get_type_or_substitute(typecode, pyobj, sw, elt):
+ '''return typecode or substitute type for wildcard or
+ derived type. For serialization only.
+ '''
+ sub = getattr(pyobj, 'typecode', typecode)
+ if sub is typecode or sub is None:
+ return typecode
+
+ # Element WildCard
+ if isinstance(typecode, AnyElement):
+ return sub
+
+ # Global Element Declaration
+ if isinstance(sub, ElementDeclaration):
+ if (typecode.nspname,typecode.pname) == (sub.nspname,sub.pname):
+ raise TypeError(\
+ 'bad usage, failed to serialize element reference (%s, %s), in: %s' %
+ (typecode.nspname, typecode.pname, sw.Backtrace(elt),))
+
+ raise TypeError(\
+ 'failed to serialize (%s, %s) illegal sub GED (%s,%s): %s' %
+ (typecode.nspname, typecode.pname, sub.nspname, sub.pname,
+ sw.Backtrace(elt),))
+
+ # Local Element
+ if not isinstance(typecode, AnyType) and not isinstance(sub, typecode.__class__):
+ raise TypeError(\
+ 'failed to serialize substitute %s for %s, not derivation: %s' %
+ (sub, typecode, sw.Backtrace(elt),))
+
+ sub.nspname = typecode.nspname
+ sub.pname = typecode.pname
+ sub.aname = typecode.aname
+ sub.minOccurs = 1
+ sub.maxOccurs = 1
+ return sub
+
+
+def _get_any_instances(ofwhat, d):
+ '''Run thru list ofwhat.anames and find unmatched keys in value
+ dictionary d. Assume these are element wildcard instances.
+ '''
+ any_keys = []
+ anames = map(lambda what: what.aname, ofwhat)
+ for aname,pyobj in d.items():
+ if isinstance(pyobj, AnyType) or aname in anames or pyobj is None:
+ continue
+ any_keys.append(aname)
+ return any_keys
+
+
+
+
+class ComplexType(TypeCode):
+ '''Represents an element of complexType, potentially containing other
+ elements.
+ '''
+ logger = _GetLogger('ZSI.TCcompound.ComplexType')
+
+ def __init__(self, pyclass, ofwhat, pname=None, inorder=False, inline=False,
+ mutable=True, mixed=False, mixed_aname='_text', **kw):
+ '''pyclass -- the Python class to hold the fields
+ ofwhat -- a list of fields to be in the complexType
+ inorder -- fields must be in exact order or not
+ inline -- don't href/id when serializing
+ mutable -- object could change between multiple serializations
+ type -- the (URI,localname) of the datatype
+ mixed -- mixed content model? True/False
+ mixed_aname -- if mixed is True, specify text content here. Default _text
+ '''
+ TypeCode.__init__(self, pname, pyclass=pyclass, **kw)
+ self.inorder = inorder
+ self.inline = inline
+ self.mutable = mutable
+ self.mixed = mixed
+ self.mixed_aname = None
+ if mixed is True:
+ self.mixed_aname = mixed_aname
+
+ if self.mutable is True: self.inline = True
+ self.type = kw.get('type') or _get_xsitype(self)
+ t = type(ofwhat)
+ if t not in _seqtypes:
+ raise TypeError(
+ 'Struct ofwhat must be list or sequence, not ' + str(t))
+ self.ofwhat = tuple(ofwhat)
+ if TypeCode.typechecks:
+ # XXX Not sure how to determine if new-style class..
+ if self.pyclass is not None and \
+ type(self.pyclass) is not types.ClassType and not isinstance(self.pyclass, object):
+ raise TypeError('pyclass must be None or an old-style/new-style class, not ' +
+ str(type(self.pyclass)))
+ _check_typecode_list(self.ofwhat, 'ComplexType')
+
+ def parse(self, elt, ps):
+ debug = self.logger.debugOn()
+ debug and self.logger.debug('parse')
+
+ xtype = self.checkname(elt, ps)
+ if self.type and xtype not in [ self.type, (None,None) ]:
+ if not isinstance(self, TypeDefinition):
+ raise EvaluateException(\
+ 'ComplexType for %s has wrong type(%s), looking for %s' %
+ (self.pname, self.checktype(elt,ps), self.type),
+ ps.Backtrace(elt))
+ else:
+ #TODO: mabye change MRO to handle this
+ debug and self.logger.debug('delegate to substitute type')
+ what = TypeDefinition.getSubstituteType(self, elt, ps)
+ return what.parse(elt, ps)
+
+ href = _find_href(elt)
+ if href:
+ if _children(elt):
+ raise EvaluateException('Struct has content and HREF',
+ ps.Backtrace(elt))
+ elt = ps.FindLocalHREF(href, elt)
+ c = _child_elements(elt)
+ count = len(c)
+ if self.nilled(elt, ps): return Nilled
+
+ # Create the object.
+ v = {}
+
+ # parse all attributes contained in attribute_typecode_dict (user-defined attributes),
+ # the values (if not None) will be keyed in self.attributes dictionary.
+ attributes = self.parse_attributes(elt, ps)
+ if attributes:
+ v[self.attrs_aname] = attributes
+
+ #MIXED
+ if self.mixed is True:
+ v[self.mixed_aname] = self.simple_value(elt,ps, mixed=True)
+
+ # Clone list of kids (we null it out as we process)
+ c, crange = c[:], range(len(c))
+ # Loop over all items we're expecting
+
+ if debug:
+ self.logger.debug("ofwhat: %s",str(self.ofwhat))
+
+ any = None
+ for i,what in [ (i, self.ofwhat[i]) for i in range(len(self.ofwhat)) ]:
+
+ # retrieve typecode if it is hidden
+ if callable(what): what = what()
+
+ # Loop over all available kids
+ if debug:
+ self.logger.debug("what: (%s,%s)", what.nspname, what.pname)
+
+ for j,c_elt in [ (j, c[j]) for j in crange if c[j] ]:
+ if debug:
+ self.logger.debug("child node: (%s,%s)", c_elt.namespaceURI,
+ c_elt.tagName)
+ if what.name_match(c_elt):
+ # Parse value, and mark this one done.
+ try:
+ value = what.parse(c_elt, ps)
+ except EvaluateException, e:
+ #what = _get_substitute_element(c_elt, what)
+ #value = what.parse(c_elt, ps)
+ raise
+ if what.maxOccurs > 1:
+ if v.has_key(what.aname):
+ v[what.aname].append(value)
+ else:
+ v[what.aname] = [value]
+ c[j] = None
+ continue
+ else:
+ v[what.aname] = value
+ c[j] = None
+ break
+ else:
+ if debug:
+ self.logger.debug("no element (%s,%s)",
+ what.nspname, what.pname)
+
+ # No match; if it was supposed to be here, that's an error.
+ if self.inorder is True and i == j:
+ raise EvaluateException('Out of order complexType',
+ ps.Backtrace(c_elt))
+ else:
+ # only supporting 1 <any> declaration in content.
+ if isinstance(what,AnyElement):
+ any = what
+ elif hasattr(what, 'default'):
+ v[what.aname] = what.default
+ elif what.minOccurs > 0 and not v.has_key(what.aname):
+ raise EvaluateException('Element "' + what.aname + \
+ '" missing from complexType', ps.Backtrace(elt))
+
+ # Look for wildcards and unprocessed children
+ # XXX Stick all this stuff in "any", hope for no collisions
+ if any is not None:
+ occurs = 0
+ v[any.aname] = []
+ for j,c_elt in [ (j, c[j]) for j in crange if c[j] ]:
+ value = any.parse(c_elt, ps)
+ if any.maxOccurs == UNBOUNDED or any.maxOccurs > 1:
+ v[any.aname].append(value)
+ else:
+ v[any.aname] = value
+
+ occurs += 1
+
+ # No such thing as nillable <any>
+ if any.maxOccurs == 1 and occurs == 0:
+ v[any.aname] = None
+ elif occurs < any.minOccurs or (any.maxOccurs!=UNBOUNDED and any.maxOccurs<occurs):
+ raise EvaluateException('occurances of <any> elements(#%d) bound by (%d,%s)' %(
+ occurs, any.minOccurs,str(any.maxOccurs)), ps.Backtrace(elt))
+
+ if not self.pyclass:
+ return v
+
+ # type definition must be informed of element tag (nspname,pname),
+ # element declaration is initialized with a tag.
+ try:
+ pyobj = self.pyclass()
+ except Exception, e:
+ raise TypeError("Constructing element (%s,%s) with pyclass(%s), %s" \
+ %(self.nspname, self.pname, self.pyclass.__name__, str(e)))
+ for key in v.keys():
+ setattr(pyobj, key, v[key])
+ return pyobj
+
+ def serialize(self, elt, sw, pyobj, inline=False, name=None, **kw):
+ if inline or self.inline:
+ self.cb(elt, sw, pyobj, name=name, **kw)
+ else:
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ el = elt.createAppendElement(ns, n)
+ el.setAttributeNS(None, 'href', "#%s" %objid)
+ sw.AddCallback(self.cb, elt, sw, pyobj)
+
+ def cb(self, elt, sw, pyobj, name=None, **kw):
+ debug = self.logger.debugOn()
+ if debug:
+ self.logger.debug("cb: %s" %str(self.ofwhat))
+
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ if pyobj is None:
+ if self.nillable is True:
+ elem = elt.createAppendElement(ns, n)
+ self.serialize_as_nil(elem)
+ return
+ raise EvaluateException, 'element(%s,%s) is not nillable(%s)' %(
+ self.nspname,self.pname,self.nillable)
+
+ if self.mutable is False and sw.Known(pyobj):
+ return
+
+ if debug:
+ self.logger.debug("element: (%s, %s)", str(ns), n)
+
+ if n is not None:
+ elem = elt.createAppendElement(ns, n)
+ self.set_attributes(elem, pyobj)
+ if kw.get('typed', self.typed) is True:
+ self.set_attribute_xsi_type(elem)
+
+ #MIXED For now just stick it in front.
+ if self.mixed is True and self.mixed_aname is not None:
+ if hasattr(pyobj, self.mixed_aname):
+ textContent = getattr(pyobj, self.mixed_aname)
+ if hasattr(textContent, 'typecode'):
+ textContent.typecode.serialize_text_node(elem, sw, textContent)
+ elif type(textContent) in _stringtypes:
+ if debug:
+ self.logger.debug("mixed text content:\n\t%s",
+ textContent)
+ elem.createAppendTextNode(textContent)
+ else:
+ raise EvaluateException('mixed test content in element (%s,%s) must be a string type' %(
+ self.nspname,self.pname), sw.Backtrace(elt))
+ else:
+ if debug:
+ self.logger.debug("mixed NO text content in %s",
+ self.mixed_aname)
+ else:
+ #For information items w/o tagNames
+ # ie. model groups,SOAP-ENC:Header
+ elem = elt
+
+ if self.inline:
+ pass
+ elif not self.inline and self.unique:
+ raise EvaluateException('Not inline, but unique makes no sense. No href/id.',
+ sw.Backtrace(elt))
+ elif n is not None:
+ self.set_attribute_id(elem, objid)
+
+ if self.pyclass and type(self.pyclass) is type:
+ f = lambda attr: getattr(pyobj, attr, None)
+ elif self.pyclass:
+ d = pyobj.__dict__
+ f = lambda attr: d.get(attr)
+ else:
+ d = pyobj
+ f = lambda attr: pyobj.get(attr)
+ if TypeCode.typechecks and type(d) != types.DictType:
+ raise TypeError("Classless struct didn't get dictionary")
+
+ indx, lenofwhat = 0, len(self.ofwhat)
+ if debug:
+ self.logger.debug('element declaration (%s,%s)', self.nspname,
+ self.pname)
+ if self.type:
+ self.logger.debug('xsi:type definition (%s,%s)', self.type[0],
+ self.type[1])
+ else:
+ self.logger.warning('NO xsi:type')
+
+ while indx < lenofwhat:
+ occurs = 0
+ what = self.ofwhat[indx]
+
+ # retrieve typecode if hidden
+ if callable(what): what = what()
+
+ if debug:
+ self.logger.debug('serialize what -- %s',
+ what.__class__.__name__)
+
+ # No way to order <any> instances, so just grab any unmatched
+ # anames and serialize them. Only support one <any> in all content.
+ # Must be self-describing instances
+
+ # Regular handling of declared elements
+ aname = what.aname
+ v = f(aname)
+ indx += 1
+ if what.minOccurs == 0 and v is None:
+ continue
+
+ # Default to typecode, if self-describing instance, and check
+ # to make sure it is derived from what.
+ whatTC = what
+ if whatTC.maxOccurs > 1 and v is not None:
+ if type(v) not in _seqtypes:
+ raise EvaluateException('pyobj (%s,%s), aname "%s": maxOccurs %s, expecting a %s' %(
+ self.nspname,self.pname,what.aname,whatTC.maxOccurs,_seqtypes),
+ sw.Backtrace(elt))
+
+ for v2 in v:
+ occurs += 1
+ if occurs > whatTC.maxOccurs:
+ raise EvaluateException('occurances (%d) exceeded maxOccurs(%d) for <%s>' %(
+ occurs, whatTC.maxOccurs, what.pname),
+ sw.Backtrace(elt))
+
+ what = _get_type_or_substitute(whatTC, v2, sw, elt)
+ if debug and what is not whatTC:
+ self.logger.debug('substitute derived type: %s' %
+ what.__class__)
+
+ what.serialize(elem, sw, v2, **kw)
+# try:
+# what.serialize(elem, sw, v2, **kw)
+# except Exception, e:
+# raise EvaluateException('Serializing %s.%s, %s %s' %
+# (n, whatTC.aname or '?', e.__class__.__name__, str(e)))
+
+ if occurs < whatTC.minOccurs:
+ raise EvaluateException(\
+ 'occurances(%d) less than minOccurs(%d) for <%s>' %
+ (occurs, whatTC.minOccurs, what.pname), sw.Backtrace(elt))
+
+ continue
+
+ if v is not None or what.nillable is True:
+ what = _get_type_or_substitute(whatTC, v, sw, elt)
+ if debug and what is not whatTC:
+ self.logger.debug('substitute derived type: %s' %
+ what.__class__)
+ what.serialize(elem, sw, v, **kw)
+# try:
+# what.serialize(elem, sw, v, **kw)
+# except (ParseException, EvaluateException), e:
+# raise
+# except Exception, e:
+# raise EvaluateException('Serializing %s.%s, %s %s' %
+# (n, whatTC.aname or '?', e.__class__.__name__, str(e)),
+# sw.Backtrace(elt))
+ continue
+
+ raise EvaluateException('Got None for nillable(%s), minOccurs(%d) element (%s,%s), %s' %
+ (what.nillable, what.minOccurs, what.nspname, what.pname, elem),
+ sw.Backtrace(elt))
+
+
+ def setDerivedTypeContents(self, extensions=None, restrictions=None):
+ """For derived types set appropriate parameter and
+ """
+ if extensions:
+ ofwhat = list(self.ofwhat)
+ if type(extensions) in _seqtypes:
+ ofwhat += list(extensions)
+ else:
+ ofwhat.append(extensions)
+ elif restrictions:
+ if type(restrictions) in _seqtypes:
+ ofwhat = restrictions
+ else:
+ ofwhat = (restrictions,)
+ else:
+ return
+ self.ofwhat = tuple(ofwhat)
+ self.lenofwhat = len(self.ofwhat)
+
+
+class Struct(ComplexType):
+ '''Struct is a complex type for accessors identified by name.
+ Constraint: No element may have the same name as any other,
+ nor may any element have a maxOccurs > 1.
+
+ <xs:group name="Struct" >
+ <xs:sequence>
+ <xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ </xs:sequence>
+ </xs:group>
+
+ <xs:complexType name="Struct" >
+ <xs:group ref="tns:Struct" minOccurs="0" />
+ <xs:attributeGroup ref="tns:commonAttributes"/>
+ </xs:complexType>
+ '''
+ logger = _GetLogger('ZSI.TCcompound.Struct')
+
+ def __init__(self, pyclass, ofwhat, pname=None, inorder=False, inline=False,
+ mutable=True, **kw):
+ '''pyclass -- the Python class to hold the fields
+ ofwhat -- a list of fields to be in the struct
+ inorder -- fields must be in exact order or not
+ inline -- don't href/id when serializing
+ mutable -- object could change between multiple serializations
+ '''
+ ComplexType.__init__(self, pyclass, ofwhat, pname=pname,
+ inorder=inorder, inline=inline, mutable=mutable,
+ **kw
+ )
+
+ # Check Constraints
+ whats = map(lambda what: (what.nspname,what.pname), self.ofwhat)
+ for idx in range(len(self.ofwhat)):
+ what = self.ofwhat[idx]
+ key = (what.nspname,what.pname)
+ if not isinstance(what, AnyElement) and what.maxOccurs > 1:
+ raise TypeError,\
+ 'Constraint: no element can have a maxOccurs>1'
+ if key in whats[idx+1:]:
+ raise TypeError,\
+ 'Constraint: No element may have the same name as any other'
+
+
+class Array(TypeCode):
+ '''An array.
+ atype -- arrayType, (namespace,ncname)
+ mutable -- object could change between multiple serializations
+ undeclared -- do not serialize/parse arrayType attribute.
+ '''
+ logger = _GetLogger('ZSI.TCcompound.Array')
+
+ def __init__(self, atype, ofwhat, pname=None, dimensions=1, fill=None,
+ sparse=False, mutable=False, size=None, nooffset=0, undeclared=False,
+ childnames=None, **kw):
+ TypeCode.__init__(self, pname, **kw)
+ self.dimensions = dimensions
+ self.atype = atype
+ if undeclared is False and self.atype[1].endswith(']') is False:
+ self.atype = (self.atype[0], '%s[]' %self.atype[1])
+ # Support multiple dimensions
+ if self.dimensions != 1:
+ raise TypeError("Only single-dimensioned arrays supported")
+ self.fill = fill
+ self.sparse = sparse
+ #if self.sparse: ofwhat.minOccurs = 0
+ self.mutable = mutable
+ self.size = size
+ self.nooffset = nooffset
+ self.undeclared = undeclared
+ self.childnames = childnames
+ if self.size:
+ t = type(self.size)
+ if t in _inttypes:
+ self.size = (self.size,)
+ elif t in _seqtypes:
+ self.size = tuple(self.size)
+ elif TypeCode.typechecks:
+ raise TypeError('Size must be integer or list, not ' + str(t))
+
+ if TypeCode.typechecks:
+ if self.undeclared is False and type(atype) not in _seqtypes and len(atype) == 2:
+ raise TypeError("Array type must be a sequence of len 2.")
+ t = type(ofwhat)
+ if not isinstance(ofwhat, TypeCode):
+ raise TypeError(
+ 'Array ofwhat outside the TypeCode hierarchy, ' +
+ str(ofwhat.__class__))
+ if self.size:
+ if len(self.size) != self.dimensions:
+ raise TypeError('Array dimension/size mismatch')
+ for s in self.size:
+ if type(s) not in _inttypes:
+ raise TypeError('Array size "' + str(s) +
+ '" is not an integer.')
+ self.ofwhat = ofwhat
+
+ def parse_offset(self, elt, ps):
+ o = _find_arrayoffset(elt)
+ if not o: return 0
+ if not _offset_pat.match(o):
+ raise EvaluateException('Bad offset "' + o + '"',
+ ps.Backtrace(elt))
+ return int(o[1:-1])
+
+ def parse_position(self, elt, ps):
+ o = _find_arrayposition(elt)
+ if not o: return None
+ if o.find(','):
+ raise EvaluateException('Sorry, no multi-dimensional arrays',
+ ps.Backtrace(elt))
+ if not _position_pat.match(o):
+ raise EvaluateException('Bad array position "' + o + '"',
+ ps.Backtrace(elt))
+ return int(o[-1:1])
+
+ def parse(self, elt, ps):
+ href = _find_href(elt)
+ if href:
+ if _children(elt):
+ raise EvaluateException('Array has content and HREF',
+ ps.Backtrace(elt))
+ elt = ps.FindLocalHREF(href, elt)
+ if self.nilled(elt, ps): return Nilled
+ if not _find_arraytype(elt) and self.undeclared is False:
+ raise EvaluateException('Array expected', ps.Backtrace(elt))
+ t = _find_type(elt)
+ if t:
+ pass # XXX should check the type, but parsing that is hairy.
+ offset = self.parse_offset(elt, ps)
+ v, vlen = [], 0
+ if offset and not self.sparse:
+ while vlen < offset:
+ vlen += 1
+ v.append(self.fill)
+ for c in _child_elements(elt):
+ item = self.ofwhat.parse(c, ps)
+ position = self.parse_position(c, ps) or offset
+ if self.sparse:
+ v.append((position, item))
+ else:
+ while offset < position:
+ offset += 1
+ v.append(self.fill)
+ v.append(item)
+ offset += 1
+ return v
+
+ def serialize(self, elt, sw, pyobj, name=None, childnames=None, **kw):
+ debug = self.logger.debugOn()
+ if debug:
+ self.logger.debug("serialize: %r" %pyobj)
+
+ if self.mutable is False and sw.Known(pyobj): return
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ el = elt.createAppendElement(ns, n)
+
+ # nillable
+ if self.nillable is True and pyobj is None:
+ self.serialize_as_nil(el)
+ return None
+
+ # other attributes
+ self.set_attributes(el, pyobj)
+
+ # soap href attribute
+ unique = self.unique or kw.get('unique', False)
+ if unique is False and sw.Known(pyobj):
+ self.set_attribute_href(el, objid)
+ return None
+
+ # xsi:type attribute
+ if kw.get('typed', self.typed) is True:
+ self.set_attribute_xsi_type(el, **kw)
+
+ # soap id attribute
+ if self.unique is False:
+ self.set_attribute_id(el, objid)
+
+ offset = 0
+ if self.sparse is False and self.nooffset is False:
+ offset, end = 0, len(pyobj)
+ while offset < end and pyobj[offset] == self.fill:
+ offset += 1
+ if offset:
+ el.setAttributeNS(SOAP.ENC, 'offset', '[%d]' %offset)
+
+ if self.undeclared is False:
+ el.setAttributeNS(SOAP.ENC, 'arrayType',
+ '%s:%s' %(el.getPrefix(self.atype[0]), self.atype[1])
+ )
+
+ if debug:
+ self.logger.debug("ofwhat: %r" %self.ofwhat)
+
+ d = {}
+ kn = childnames or self.childnames
+ if kn:
+ d['name'] = kn
+ elif not self.ofwhat.aname:
+ d['name'] = 'element'
+
+ if self.sparse is False:
+ for e in pyobj[offset:]: self.ofwhat.serialize(el, sw, e, **d)
+ else:
+ position = 0
+ for pos, v in pyobj:
+ if pos != position:
+ el.setAttributeNS(SOAP.ENC, 'position', '[%d]' %pos)
+ position = pos
+
+ self.ofwhat.serialize(el, sw, v, **d)
+ position += 1
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/TCnumbers.py b/ZSI/TCnumbers.py
new file mode 100644
index 0000000..67007b0
--- /dev/null
+++ b/ZSI/TCnumbers.py
@@ -0,0 +1,184 @@
+#! /usr/bin/env python
+# $Header$
+'''Typecodes for numbers.
+'''
+import types
+from ZSI import _copyright, _inttypes, _floattypes, _seqtypes, \
+ EvaluateException
+from ZSI.TC import TypeCode, Integer, Decimal
+from ZSI.wstools.Namespaces import SCHEMA
+
+class IunsignedByte(Integer):
+ '''Unsigned 8bit value.
+ '''
+ type = (SCHEMA.XSD3, "unsignedByte")
+ parselist = [ (None, "unsignedByte") ]
+ seriallist = [ ]
+
+class IunsignedShort(Integer):
+ '''Unsigned 16bit value.
+ '''
+ type = (SCHEMA.XSD3, "unsignedShort")
+ parselist = [ (None, "unsignedShort") ]
+ seriallist = [ ]
+
+class IunsignedInt(Integer):
+ '''Unsigned 32bit value.
+ '''
+ type = (SCHEMA.XSD3, "unsignedInt")
+ parselist = [ (None, "unsignedInt") ]
+ seriallist = [ ]
+
+class IunsignedLong(Integer):
+ '''Unsigned 64bit value.
+ '''
+ type = (SCHEMA.XSD3, "unsignedLong")
+ parselist = [ (None, "unsignedLong") ]
+ seriallist = [ ]
+
+class Ibyte(Integer):
+ '''Signed 8bit value.
+ '''
+ type = (SCHEMA.XSD3, "byte")
+ parselist = [ (None, "byte") ]
+ seriallist = [ ]
+
+class Ishort(Integer):
+ '''Signed 16bit value.
+ '''
+ type = (SCHEMA.XSD3, "short")
+ parselist = [ (None, "short") ]
+ seriallist = [ ]
+
+class Iint(Integer):
+ '''Signed 32bit value.
+ '''
+ type = (SCHEMA.XSD3, "int")
+ parselist = [ (None, "int") ]
+ seriallist = [ types.IntType ]
+
+class Ilong(Integer):
+ '''Signed 64bit value.
+ '''
+ type = (SCHEMA.XSD3, "long")
+ parselist = [(None, "long")]
+ seriallist = [ types.LongType ]
+
+class InegativeInteger(Integer):
+ '''Value less than zero.
+ '''
+ type = (SCHEMA.XSD3, "negativeInteger")
+ parselist = [ (None, "negativeInteger") ]
+ seriallist = [ ]
+
+class InonPositiveInteger(Integer):
+ '''Value less than or equal to zero.
+ '''
+ type = (SCHEMA.XSD3, "nonPositiveInteger")
+ parselist = [ (None, "nonPositiveInteger") ]
+ seriallist = [ ]
+
+class InonNegativeInteger(Integer):
+ '''Value greater than or equal to zero.
+ '''
+ type = (SCHEMA.XSD3, "nonNegativeInteger")
+ parselist = [ (None, "nonNegativeInteger") ]
+ seriallist = [ ]
+
+class IpositiveInteger(Integer):
+ '''Value greater than zero.
+ '''
+ type = (SCHEMA.XSD3, "positiveInteger")
+ parselist = [ (None, "positiveInteger") ]
+ seriallist = [ ]
+
+class Iinteger(Integer):
+ '''Integer value.
+ '''
+ type = (SCHEMA.XSD3, "integer")
+ parselist = [ (None, "integer") ]
+ seriallist = [ ]
+
+class IEnumeration(Integer):
+ '''Integer value, limited to a specified set of values.
+ '''
+
+ def __init__(self, choices, pname=None, **kw):
+ Integer.__init__(self, pname, **kw)
+ self.choices = choices
+ t = type(choices)
+ if t in _seqtypes:
+ self.choices = tuple(choices)
+ elif TypeCode.typechecks:
+ raise TypeError(
+ 'Enumeration choices must be list or sequence, not ' + str(t))
+ if TypeCode.typechecks:
+ for c in self.choices:
+ if type(c) not in _inttypes:
+ raise TypeError('Enumeration choice "' +
+ str(c) + '" is not an integer')
+
+ def parse(self, elt, ps):
+ val = Integer.parse(self, elt, ps)
+ if val not in self.choices:
+ raise EvaluateException('Value "' + str(val) + \
+ '" not in enumeration list',
+ ps.Backtrace(elt))
+ return val
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ if pyobj not in self.choices:
+ raise EvaluateException('Value not in int enumeration list',
+ ps.Backtrace(elt))
+ Integer.serialize(self, elt, sw, pyobj, name=name, orig=orig, **kw)
+
+
+class FPfloat(Decimal):
+ '''IEEE 32bit floating point value.
+ '''
+ type = (SCHEMA.XSD3, "float")
+ parselist = [ (None, "float") ]
+ seriallist = [ types.FloatType ]
+
+class FPdouble(Decimal):
+ '''IEEE 64bit floating point value.
+ '''
+ type = (SCHEMA.XSD3, "double")
+ parselist = [ (None, "double") ]
+ seriallist = [ ]
+
+class FPEnumeration(FPfloat):
+ '''Floating point value, limited to a specified set of values.
+ '''
+
+ def __init__(self, choices, pname=None, **kw):
+ FPfloat.__init__(self, pname, **kw)
+ self.choices = choices
+ t = type(choices)
+ if t in _seqtypes:
+ self.choices = tuple(choices)
+ elif TypeCode.typechecks:
+ raise TypeError(
+ 'Enumeration choices must be list or sequence, not ' + str(t))
+ if TypeCode.typechecks:
+ for c in self.choices:
+ if type(c) not in _floattypes:
+ raise TypeError('Enumeration choice "' +
+ str(c) + '" is not floating point number')
+
+ def parse(self, elt, ps):
+ val = Decimal.parse(self, elt, ps)
+ if val not in self.choices:
+ raise EvaluateException('Value "' + str(val) + \
+ '" not in enumeration list',
+ ps.Backtrace(elt))
+ return val
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ if pyobj not in self.choices:
+ raise EvaluateException('Value not in int enumeration list',
+ ps.Backtrace(elt))
+ Decimal.serialize(self, elt, sw, pyobj, name=name, orig=orig, **kw)
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/TCtimes.py b/ZSI/TCtimes.py
new file mode 100644
index 0000000..4c317e9
--- /dev/null
+++ b/ZSI/TCtimes.py
@@ -0,0 +1,314 @@
+#! /usr/bin/env python
+# $Header$
+'''Typecodes for dates and times.
+'''
+
+from ZSI import _copyright, _floattypes, _inttypes, _get_idstr, EvaluateException
+from ZSI.TC import TypeCode, SimpleType
+from ZSI.wstools.Namespaces import SCHEMA
+import operator, re, time as _time
+from time import mktime as _mktime, localtime as _localtime, gmtime as _gmtime
+from datetime import tzinfo as _tzinfo, timedelta as _timedelta,\
+ datetime as _datetime
+from math import modf as _modf
+
+_niltime = [
+ 0, 0, 0, # year month day
+ 0, 0, 0, # hour minute second
+ 0, 0, 0 # weekday, julian day, dst flag
+]
+
+#### Code added to check current timezone offset
+_zero = _timedelta(0)
+_dstoffset = _stdoffset = _timedelta(seconds=-_time.timezone)
+if _time.daylight: _dstoffset = _timedelta(seconds=-_time.altzone)
+_dstdiff = _dstoffset - _stdoffset
+
+
+class _localtimezone(_tzinfo):
+ """ """
+ def dst(self, dt):
+ """datetime -> DST offset in minutes east of UTC."""
+ tt = _localtime(_mktime((dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1)))
+ if tt.tm_isdst > 0: return _dstdiff
+ return _zero
+
+ #def fromutc(...)
+ #datetime in UTC -> datetime in local time.
+
+ def tzname(self, dt):
+ """datetime -> string name of time zone."""
+ tt = _localtime(_mktime((dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1)))
+ return _time.tzname[tt.tm_isdst > 0]
+
+ def utcoffset(self, dt):
+ """datetime -> minutes east of UTC (negative for west of UTC)."""
+ tt = _localtime(_mktime((dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1)))
+ if tt.tm_isdst > 0: return _dstoffset
+ return _stdoffset
+
+class _fixedoffset(_tzinfo):
+ """Fixed offset in minutes east from UTC.
+
+ A class building tzinfo objects for fixed-offset time zones.
+ Note that _fixedoffset(0, "UTC") is a different way to build a
+ UTC tzinfo object.
+ """
+ #def __init__(self, offset, name):
+ def __init__(self, offset):
+ self.__offset = _timedelta(minutes=offset)
+ #self.__name = name
+
+ def dst(self, dt):
+ """datetime -> DST offset in minutes east of UTC."""
+ return _zero
+
+ def tzname(self, dt):
+ """datetime -> string name of time zone."""
+ #return self.__name
+ return "server"
+
+ def utcoffset(self, dt):
+ """datetime -> minutes east of UTC (negative for west of UTC)."""
+ return self.__offset
+
+
+def _dict_to_tuple(d):
+ '''Convert a dictionary to a time tuple. Depends on key values in the
+ regexp pattern!
+ '''
+ retval = _niltime[:]
+ for k,i in ( ('Y', 0), ('M', 1), ('D', 2), ('h', 3), ('m', 4), ):
+ v = d.get(k)
+ if v: retval[i] = int(v)
+
+ v = d.get('s')
+ if v:
+ msec,sec = _modf(float(v))
+ retval[6],retval[5] = int(round(msec*1000)), int(sec)
+
+ v = d.get('tz')
+ if v and v != 'Z':
+ h,m = map(int, v.split(':'))
+ # check for time zone offset, if within the same timezone,
+ # ignore offset specific calculations
+ offset=_localtimezone().utcoffset(_datetime.now())
+ local_offset_hour = offset.seconds/3600
+ local_offset_min = (offset.seconds%3600)%60
+ if local_offset_hour > 12:
+ local_offset_hour -= 24
+
+ if local_offset_hour != h or local_offset_min != m:
+ if h<0:
+ #TODO: why is this set to server
+ #foff = _fixedoffset(-((abs(h)*60+m)),"server")
+ foff = _fixedoffset(-((abs(h)*60+m)))
+ else:
+ #TODO: why is this set to server
+ #foff = _fixedoffset((abs(h)*60+m),"server")
+ foff = _fixedoffset((abs(h)*60+m))
+
+ dt = _datetime(retval[0],retval[1],retval[2],retval[3],retval[4],
+ retval[5],0,foff)
+
+ # update dict with calculated timezone
+ localdt=dt.astimezone(_localtimezone())
+ retval[0] = localdt.year
+ retval[1] = localdt.month
+ retval[2] = localdt.day
+ retval[3] = localdt.hour
+ retval[4] = localdt.minute
+ retval[5] = localdt.second
+
+ if d.get('neg', 0):
+ retval[0:5] = map(operator.__neg__, retval[0:5])
+ return tuple(retval)
+
+
+class Duration(SimpleType):
+ '''Time duration.
+ '''
+ parselist = [ (None,'duration') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)P' \
+ r'((?P<Y>\d+)Y)?' r'((?P<M>\d+)M)?' r'((?P<D>\d+)D)?' \
+ r'(?P<T>T?)' r'((?P<h>\d+)H)?' r'((?P<m>\d+)M)?' \
+ r'((?P<s>\d*(\.\d+)?)S)?' '$')
+ type = (SCHEMA.XSD3, 'duration')
+
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ if text is None:
+ return None
+ m = Duration.lex_pattern.match(text)
+ if m is None:
+ raise EvaluateException('Illegal duration', ps.Backtrace(elt))
+ d = m.groupdict()
+ if d['T'] and (d['h'] is None and d['m'] is None and d['s'] is None):
+ raise EvaluateException('Duration has T without time')
+ try:
+ retval = _dict_to_tuple(d)
+ except ValueError, e:
+ raise EvaluateException(str(e))
+
+ if self.pyclass is not None:
+ return self.pyclass(retval)
+ return retval
+
+ def get_formatted_content(self, pyobj):
+ if type(pyobj) in _floattypes or type(pyobj) in _inttypes:
+ pyobj = _gmtime(pyobj)
+
+ d = {}
+ pyobj = tuple(pyobj)
+ if 1 in map(lambda x: x < 0, pyobj[0:6]):
+ pyobj = map(abs, pyobj)
+ neg = '-'
+ else:
+ neg = ''
+
+ val = '%sP%dY%dM%dDT%dH%dM%dS' % \
+ ( neg, pyobj[0], pyobj[1], pyobj[2], pyobj[3], pyobj[4], pyobj[5])
+
+ return val
+
+
+class Gregorian(SimpleType):
+ '''Gregorian times.
+ '''
+ lex_pattern = tag = format = None
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ if text is None:
+ return None
+
+ m = self.lex_pattern.match(text)
+ if not m:
+ raise EvaluateException('Bad Gregorian: %s' %text, ps.Backtrace(elt))
+ try:
+ retval = _dict_to_tuple(m.groupdict())
+ except ValueError, e:
+ #raise EvaluateException(str(e))
+ raise
+
+ if self.pyclass is not None:
+ return self.pyclass(retval)
+ return retval
+
+ def get_formatted_content(self, pyobj):
+ if type(pyobj) in _floattypes or type(pyobj) in _inttypes:
+ pyobj = _gmtime(pyobj)
+
+ d = {}
+ pyobj = tuple(pyobj)
+ if 1 in map(lambda x: x < 0, pyobj[0:6]):
+ pyobj = map(abs, pyobj)
+ d['neg'] = '-'
+ else:
+ d['neg'] = ''
+
+ ms = pyobj[6]
+ if not ms:
+ d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2],
+ 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], }
+ return self.format % d
+
+ if ms > 999:
+ raise ValueError, 'milliseconds must be a integer between 0 and 999'
+
+ d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2],
+ 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], 'ms':ms, }
+ return self.format_ms % d
+
+
+class gDateTime(Gregorian):
+ '''A date and time.
+ '''
+ parselist = [ (None,'dateTime') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ '(?P<Y>\d{4,})-' r'(?P<M>\d\d)-' r'(?P<D>\d\d)' 'T' \
+ r'(?P<h>\d\d):' r'(?P<m>\d\d):' r'(?P<s>\d*(\.\d+)?)' \
+ r'(?P<tz>(Z|([-+]\d\d:\d\d))?)' '$')
+ tag, format = 'dateTime', '%(Y)04d-%(M)02d-%(D)02dT%(h)02d:%(m)02d:%(s)02dZ'
+ format_ms = format[:-1] + '.%(ms)03dZ'
+ type = (SCHEMA.XSD3, 'dateTime')
+
+class gDate(Gregorian):
+ '''A date.
+ '''
+ parselist = [ (None,'date') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ '(?P<Y>\d{4,})-' r'(?P<M>\d\d)-' r'(?P<D>\d\d)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'date', '%(Y)04d-%(M)02d-%(D)02dZ'
+ type = (SCHEMA.XSD3, 'date')
+
+class gYearMonth(Gregorian):
+ '''A date.
+ '''
+ parselist = [ (None,'gYearMonth') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ '(?P<Y>\d{4,})-' r'(?P<M>\d\d)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'gYearMonth', '%(Y)04d-%(M)02dZ'
+ type = (SCHEMA.XSD3, 'gYearMonth')
+
+class gYear(Gregorian):
+ '''A date.
+ '''
+ parselist = [ (None,'gYear') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ '(?P<Y>\d{4,})' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'gYear', '%(Y)04dZ'
+ type = (SCHEMA.XSD3, 'gYear')
+
+class gMonthDay(Gregorian):
+ '''A gMonthDay.
+ '''
+ parselist = [ (None,'gMonthDay') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ r'--(?P<M>\d\d)-' r'(?P<D>\d\d)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'gMonthDay', '---%(M)02d-%(D)02dZ'
+ type = (SCHEMA.XSD3, 'gMonthDay')
+
+
+class gDay(Gregorian):
+ '''A gDay.
+ '''
+ parselist = [ (None,'gDay') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ r'---(?P<D>\d\d)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'gDay', '---%(D)02dZ'
+ type = (SCHEMA.XSD3, 'gDay')
+
+class gMonth(Gregorian):
+ '''A gMonth.
+ '''
+ parselist = [ (None,'gMonth') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ r'---(?P<M>\d\d)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'gMonth', '---%(M)02dZ'
+ type = (SCHEMA.XSD3, 'gMonth')
+
+class gTime(Gregorian):
+ '''A time.
+ '''
+ parselist = [ (None,'time') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ r'(?P<h>\d\d):' r'(?P<m>\d\d):' r'(?P<s>\d*(\.\d+)?)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'time', '%(h)02d:%(m)02d:%(s)02dZ'
+ format_ms = format[:-1] + '.%(ms)03dZ'
+ type = (SCHEMA.XSD3, 'time')
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/__init__.py b/ZSI/__init__.py
new file mode 100644
index 0000000..2e13da7
--- /dev/null
+++ b/ZSI/__init__.py
@@ -0,0 +1,433 @@
+#! /usr/bin/env python
+# $Header$
+'''ZSI: Zolera Soap Infrastructure.
+
+Copyright 2001, Zolera Systems, Inc. All Rights Reserved.
+'''
+
+_copyright = """ZSI: Zolera Soap Infrastructure.
+
+Copyright 2001, Zolera Systems, Inc. All Rights Reserved.
+Copyright 2002-2003, Rich Salz. All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or
+sell copies of the Software, and to permit persons to whom the Software
+is furnished to do so, provided that the above copyright notice(s) and
+this permission notice appear in all copies of the Software and that
+both the above copyright notice(s) and this permission notice appear in
+supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+
+Portions are also:
+
+Copyright (c) 2003, The Regents of the University of California,
+through Lawrence Berkeley National Laboratory (subject to receipt of
+any required approvals from the U.S. Dept. of Energy). All rights
+reserved. Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+(1) Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+(2) Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+(3) Neither the name of the University of California, Lawrence Berkeley
+National Laboratory, U.S. Dept. of Energy nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+You are under no obligation whatsoever to provide any bug fixes,
+patches, or upgrades to the features, functionality or performance of
+the source code ("Enhancements") to anyone; however, if you choose to
+make your Enhancements available either publicly, or directly to
+Lawrence Berkeley National Laboratory, without imposing a separate
+written license agreement for such Enhancements, then you hereby grant
+the following license: a non-exclusive, royalty-free perpetual license
+to install, use, modify, prepare derivative works, incorporate into
+other computer software, distribute, and sublicense such Enhancements
+or derivative works thereof, in binary and source code form.
+
+
+For wstools also:
+
+Zope Public License (ZPL) Version 2.0
+-----------------------------------------------
+
+This software is Copyright (c) Zope Corporation (tm) and
+Contributors. All rights reserved.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the above
+ copyright notice, this list of conditions, and the following
+ disclaimer.
+
+2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+3. The name Zope Corporation (tm) must not be used to
+ endorse or promote products derived from this software
+ without prior written permission from Zope Corporation.
+
+4. The right to distribute this software or to use it for
+ any purpose does not give you the right to use Servicemarks
+ (sm) or Trademarks (tm) of Zope Corporation. Use of them is
+ covered in a separate agreement (see
+ http://www.zope.com/Marks).
+
+5. If any files are modified, you must cause the modified
+ files to carry prominent notices stating that you changed
+ the files and the date of any change.
+
+Disclaimer
+
+ THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
+ AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGE.
+
+
+This software consists of contributions made by Zope
+Corporation and many individuals on behalf of Zope
+Corporation. Specific attributions are listed in the
+accompanying credits file.
+"""
+
+##
+## Stuff imported from elsewhere.
+from xml.dom import Node as _Node
+import types as _types
+
+##
+## Public constants.
+from ZSI.wstools.Namespaces import ZSI_SCHEMA_URI
+
+
+##
+## Not public constants.
+_inttypes = [ _types.IntType, _types.LongType ]
+_floattypes = [ _types.FloatType ]
+_seqtypes = [ _types.TupleType, _types.ListType ]
+_stringtypes = [ _types.StringType, _types.UnicodeType ]
+
+##
+## Low-level DOM oriented utilities; useful for typecode implementors.
+_attrs = lambda E: (E.attributes and E.attributes.values()) or []
+_children = lambda E: E.childNodes or []
+_child_elements = lambda E: [ n for n in (E.childNodes or [])
+ if n.nodeType == _Node.ELEMENT_NODE ]
+
+##
+## Stuff imported from elsewhere.
+from ZSI.wstools.Namespaces import SOAP as _SOAP, SCHEMA as _SCHEMA, XMLNS as _XMLNS
+
+##
+## Low-level DOM oriented utilities; useful for typecode implementors.
+_find_arraytype = lambda E: E.getAttributeNS(_SOAP.ENC, "arrayType")
+_find_encstyle = lambda E: E.getAttributeNS(_SOAP.ENV, "encodingStyle")
+try:
+ from xml.dom import EMPTY_NAMESPACE
+ _empty_nsuri_list = [ EMPTY_NAMESPACE ]
+ #if '' not in _empty_nsuri_list: __empty_nsuri_list.append('')
+ #if None not in _empty_nsuri_list: __empty_nsuri_list.append(None)
+except:
+ _empty_nsuri_list = [ None, '' ]
+def _find_attr(E, attr):
+ for nsuri in _empty_nsuri_list:
+ try:
+ v = E.getAttributeNS(nsuri, attr)
+ if v: return v
+ except: pass
+ return None
+
+def _find_attrNS(E, namespaceURI, localName):
+ '''namespaceURI
+ localName
+ '''
+ try:
+ v = E.getAttributeNS(namespaceURI, localName)
+ if v: return v
+ except: pass
+ return None
+
+
+def _find_attrNodeNS(E, namespaceURI, localName):
+ '''Must grab the attribute Node to distinquish between
+ an unspecified attribute(None) and one set to empty string("").
+ namespaceURI
+ localName
+ '''
+ attr = E.getAttributeNodeNS(namespaceURI, localName)
+ if attr is None: return None
+ try:
+ return attr.value
+ except: pass
+ return E.getAttributeNS(namespaceURI, localName)
+
+
+
+
+_find_href = lambda E: _find_attr(E, "href")
+_find_xsi_attr = lambda E, attr: \
+ E.getAttributeNS(_SCHEMA.XSI3, attr) \
+ or E.getAttributeNS(_SCHEMA.XSI1, attr) \
+ or E.getAttributeNS(_SCHEMA.XSI2, attr)
+_find_type = lambda E: _find_xsi_attr(E, "type")
+
+_find_xmlns_prefix = lambda E, attr: E.getAttributeNS(_XMLNS.BASE, attr)
+_find_default_namespace = lambda E: E.getAttributeNS(_XMLNS.BASE, None)
+
+#_textprotect = lambda s: s.replace('&', '&amp;').replace('<', '&lt;')
+
+_get_element_nsuri_name = lambda E: (E.namespaceURI, E.localName)
+
+_is_element = lambda E: E.nodeType == _Node.ELEMENT_NODE
+
+def _resolve_prefix(celt, prefix):
+ '''resolve prefix to a namespaceURI. If None or
+ empty str, return default namespace or None.
+
+ Parameters:
+ celt -- element node
+ prefix -- xmlns:prefix, or empty str or None
+ '''
+ namespace = None
+ while _is_element(celt):
+ if prefix:
+ namespaceURI = _find_xmlns_prefix(celt, prefix)
+ else:
+ namespaceURI = _find_default_namespace(celt)
+ if namespaceURI: break
+ celt = celt.parentNode
+ else:
+ if prefix:
+ raise EvaluateException, 'cant resolve xmlns:%s' %prefix
+ return namespaceURI
+
+def _valid_encoding(elt):
+ '''Does this node have a valid encoding?
+ '''
+ enc = _find_encstyle(elt)
+ if not enc or enc == _SOAP.ENC: return 1
+ for e in enc.split():
+ if e.startswith(_SOAP.ENC):
+ # XXX Is this correct? Once we find a Sec5 compatible
+ # XXX encoding, should we check that all the rest are from
+ # XXX that same base? Perhaps. But since the if test above
+ # XXX will surely get 99% of the cases, leave it for now.
+ return 1
+ return 0
+
+def _backtrace(elt, dom):
+ '''Return a "backtrace" from the given element to the DOM root,
+ in XPath syntax.
+ '''
+ s = ''
+ while elt != dom:
+ name, parent = elt.nodeName, elt.parentNode
+ if parent is None: break
+ matches = [ c for c in _child_elements(parent)
+ if c.nodeName == name ]
+ if len(matches) == 1:
+ s = '/' + name + s
+ else:
+ i = matches.index(elt) + 1
+ s = ('/%s[%d]' % (name, i)) + s
+ elt = parent
+ return s
+
+def _get_idstr(pyobj):
+ '''Python 2.3.x generates a FutureWarning for negative IDs, so
+ we use a different prefix character to ensure uniqueness, and
+ call abs() to avoid the warning.'''
+ x = id(pyobj)
+ if x < 0:
+ return 'x%x' % abs(x)
+ return 'o%x' % x
+
+
+def _get_postvalue_from_absoluteURI(url):
+ """Bug [ 1513000 ] POST Request-URI not limited to "abs_path"
+ Request-URI = "*" | absoluteURI | abs_path | authority
+
+ Not a complete solution, but it seems to work with all known
+ implementations. ValueError thrown if bad uri.
+ """
+ cache = _get_postvalue_from_absoluteURI.cache
+ path = cache.get(url, '')
+ if not path:
+ scheme,authpath = url.split('://')
+ s = authpath.split('/', 1)
+ if len(s) == 2: path = '/%s' %s[1]
+ if len(cache) > _get_postvalue_from_absoluteURI.MAXLEN:cache.clear()
+ cache[url] = path
+ return path
+_get_postvalue_from_absoluteURI.cache = {}
+_get_postvalue_from_absoluteURI.MAXLEN = 20
+
+
+##
+## Exception classes.
+class ZSIException(Exception):
+ '''Base class for all ZSI exceptions.
+ '''
+ pass
+
+class ParseException(ZSIException):
+ '''Exception raised during parsing.
+ '''
+
+ def __init__(self, str, inheader, elt=None, dom=None):
+ Exception.__init__(self)
+ self.str, self.inheader, self.trace = str, inheader, None
+ if elt and dom:
+ self.trace = _backtrace(elt, dom)
+
+ def __str__(self):
+ if self.trace:
+ return self.str + '\n[Element trace: ' + self.trace + ']'
+ return self.str
+
+ def __repr__(self):
+ return "<%s.ParseException %s>" % (__name__, _get_idstr(self))
+
+
+class EvaluateException(ZSIException):
+ '''Exception raised during data evaluation (serialization).
+ '''
+
+ def __init__(self, str, trace=None):
+ Exception.__init__(self)
+ self.str, self.trace = str, trace
+
+ def __str__(self):
+ if self.trace:
+ return self.str + '\n[Element trace: ' + self.trace + ']'
+ return self.str
+
+ def __repr__(self):
+ return "<%s.EvaluateException %s>" % (__name__, _get_idstr(self))
+
+class FaultException(ZSIException):
+ '''Exception raised when a fault is received.
+ '''
+
+ def __init__(self, fault):
+ self.fault = fault
+
+ def __str__(self):
+ return str(self.fault)
+
+ def __repr__(self):
+ return "<%s.FaultException %s>" % (__name__, _get_idstr(self))
+
+class WSActionException(ZSIException):
+ '''Exception raised when WS-Address Action Header is incorrectly
+ specified when received by client or server.
+ '''
+ pass
+
+##
+## Importing the rest of ZSI.
+import version
+def Version():
+ return version.Version
+
+from writer import SoapWriter
+from parse import ParsedSoap
+from fault import Fault, \
+ FaultFromActor, FaultFromException, FaultFromFaultMessage, \
+ FaultFromNotUnderstood, FaultFromZSIException
+import TC
+TC.RegisterType(TC.String, minOccurs=0, nillable=False)
+TC.RegisterType(TC.URI, minOccurs=0, nillable=False)
+TC.RegisterType(TC.Base64String, minOccurs=0, nillable=False)
+TC.RegisterType(TC.HexBinaryString, minOccurs=0, nillable=False)
+
+#TC.RegisterType(TC.Integer)
+#TC.RegisterType(TC.Decimal)
+for pyclass in (TC.IunsignedByte, TC.IunsignedShort, TC.IunsignedInt, TC.IunsignedLong,
+ TC.Ibyte, TC.Ishort, TC.Iint, TC.Ilong, TC.InegativeInteger,
+ TC.InonPositiveInteger, TC.InonNegativeInteger, TC.IpositiveInteger,
+ TC.Iinteger, TC.FPfloat, TC.FPdouble, ):
+
+ TC.RegisterType(pyclass, minOccurs=0, nillable=False)
+
+TC.RegisterType(TC.Boolean, minOccurs=0, nillable=False)
+TC.RegisterType(TC.Duration, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gDateTime, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gDate, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gYearMonth, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gYear, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gMonthDay, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gDay, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gTime, minOccurs=0, nillable=False)
+TC.RegisterType(TC.Apache.Map, minOccurs=0, nillable=False)
+
+##
+## Register Wrappers for builtin types.
+## TC.AnyElement wraps builtins so element name information can be saved
+##
+import schema
+for i in [int,float,str,tuple,list,unicode]:
+ schema._GetPyobjWrapper.RegisterBuiltin(i)
+
+## Load up Wrappers for builtin types
+schema.RegisterAnyElement()
+
+
+#try:
+# from ServiceProxy import *
+#except:
+# pass
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/address.py b/ZSI/address.py
new file mode 100644
index 0000000..98f9884
--- /dev/null
+++ b/ZSI/address.py
@@ -0,0 +1,331 @@
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+###########################################################################
+
+import time, urlparse, socket
+from ZSI import _seqtypes, EvaluateException, WSActionException
+from TC import AnyElement, AnyType, TypeCode
+from schema import GED, GTD, _has_type_definition
+from ZSI.TCcompound import ComplexType
+from ZSI.wstools.Namespaces import WSA_LIST
+
+
+class Address(object):
+ '''WS-Address
+
+ Implemented is dependent on the default "wsdl2py" convention of generating aname,
+ so the attributes representing element declaration names should be prefixed with
+ an underscore.
+ '''
+ def __init__(self, addressTo=None, wsAddressURI=None, action=None):
+ self.wsAddressURI = wsAddressURI
+ self.anonymousURI = None
+ self._addressTo = addressTo
+ self._messageID = None
+ self._action = action
+ self._endPointReference = None
+ self._replyTo = None
+ self._relatesTo = None
+ self.setUp()
+
+ def setUp(self):
+ '''Look for WS-Address
+ '''
+ toplist = filter(lambda wsa: wsa.ADDRESS==self.wsAddressURI, WSA_LIST)
+ epr = 'EndpointReferenceType'
+ for WSA in toplist+WSA_LIST:
+ if (self.wsAddressURI is not None and self.wsAddressURI != WSA.ADDRESS) or \
+ _has_type_definition(WSA.ADDRESS, epr) is True:
+ break
+ else:
+ raise EvaluateException,\
+ 'enabling wsAddressing requires the inclusion of that namespace'
+
+ self.wsAddressURI = WSA.ADDRESS
+ self.anonymousURI = WSA.ANONYMOUS
+ self._replyTo = WSA.ANONYMOUS
+
+ def _checkAction(self, action, value):
+ '''WS-Address Action
+ action -- Action value expecting.
+ value -- Action value server returned.
+ '''
+ if action is None:
+ raise WSActionException, 'User did not specify WSAddress Action value to expect'
+ if not value:
+ raise WSActionException, 'missing WSAddress Action, expecting %s' %action
+ if value != action:
+ raise WSActionException, 'wrong WSAddress Action(%s), expecting %s'%(value,action)
+
+ def _checkFrom(self, pyobj):
+ '''WS-Address From,
+ XXX currently not checking the hostname, not forwarding messages.
+ pyobj -- From server returned.
+ '''
+ if pyobj is None: return
+ value = pyobj._Address
+ if value != self._addressTo:
+ scheme,netloc,path,query,fragment = urlparse.urlsplit(value)
+ hostport = netloc.split(':')
+ schemeF,netlocF,pathF,queryF,fragmentF = urlparse.urlsplit(self._addressTo)
+ if scheme==schemeF and path==pathF and query==queryF and fragment==fragmentF:
+ netloc = netloc.split(':') + ['80']
+ netlocF = netlocF.split(':') + ['80']
+ if netloc[1]==netlocF[1] and \
+ socket.gethostbyname(netloc[0])==socket.gethostbyname(netlocF[0]):
+ return
+
+ raise WSActionException, 'wrong WS-Address From(%s), expecting %s'%(value,self._addressTo)
+
+ def _checkRelatesTo(self, value):
+ '''WS-Address From
+ value -- From server returned.
+ '''
+ if value != self._messageID:
+ raise WSActionException, 'wrong WS-Address RelatesTo(%s), expecting %s'%(value,self._messageID)
+
+ def _checkReplyTo(self, value):
+ '''WS-Address From
+ value -- From server returned in wsa:To
+ '''
+ if value != self._replyTo:
+ raise WSActionException, 'wrong WS-Address ReplyTo(%s), expecting %s'%(value,self._replyTo)
+
+ def setAction(self, action):
+ self._action = action
+
+ def getAction(self):
+ return self._action
+
+ def getRelatesTo(self):
+ return self._relatesTo
+
+ def getMessageID(self):
+ return self._messageID
+
+ def _getWSAddressTypeCodes(self, **kw):
+ '''kw -- namespaceURI keys with sequence of element names.
+ '''
+ typecodes = []
+ try:
+ for nsuri,elements in kw.items():
+ for el in elements:
+ typecode = GED(nsuri, el)
+ if typecode is None:
+ raise WSActionException, 'Missing namespace, import "%s"' %nsuri
+
+ typecodes.append(typecode)
+ else:
+ pass
+ except EvaluateException, ex:
+ raise EvaluateException, \
+ 'To use ws-addressing register typecodes for namespace(%s)' %self.wsAddressURI
+ return typecodes
+
+ def checkResponse(self, ps, action):
+ '''
+ ps -- ParsedSoap
+ action -- ws-action for response
+ '''
+ namespaceURI = self.wsAddressURI
+ d = {namespaceURI:("MessageID","Action","To","From","RelatesTo")}
+ typecodes = self._getWSAddressTypeCodes(**d)
+ pyobjs = ps.ParseHeaderElements(typecodes)
+
+ got_action = pyobjs.get((namespaceURI,"Action"))
+ self._checkAction(got_action, action)
+
+ From = pyobjs.get((namespaceURI,"From"))
+ self._checkFrom(From)
+
+ RelatesTo = pyobjs.get((namespaceURI,"RelatesTo"))
+ self._checkRelatesTo(RelatesTo)
+
+ To = pyobjs.get((namespaceURI,"To"))
+ if To: self._checkReplyTo(To)
+
+ def setRequest(self, endPointReference, action):
+ '''Call For Request
+ '''
+ self._action = action
+ self.header_pyobjs = None
+ pyobjs = []
+ namespaceURI = self.wsAddressURI
+ addressTo = self._addressTo
+ messageID = self._messageID = "uuid:%s" %time.time()
+
+ # Set Message Information Headers
+ # MessageID
+ typecode = GED(namespaceURI, "MessageID")
+ pyobjs.append(typecode.pyclass(messageID))
+
+ # Action
+ typecode = GED(namespaceURI, "Action")
+ pyobjs.append(typecode.pyclass(action))
+
+ # To
+ typecode = GED(namespaceURI, "To")
+ pyobjs.append(typecode.pyclass(addressTo))
+
+ # From
+ typecode = GED(namespaceURI, "From")
+ mihFrom = typecode.pyclass()
+ mihFrom._Address = self.anonymousURI
+ pyobjs.append(mihFrom)
+
+ if endPointReference:
+ if hasattr(endPointReference, 'typecode') is False:
+ raise EvaluateException, 'endPointReference must have a typecode attribute'
+
+ if isinstance(endPointReference.typecode, \
+ GTD(namespaceURI ,'EndpointReferenceType')) is False:
+ raise EvaluateException, 'endPointReference must be of type %s' \
+ %GTD(namespaceURI ,'EndpointReferenceType')
+
+ ReferenceProperties = endPointReference._ReferenceProperties
+ any = ReferenceProperties._any or []
+ #if not (what.maxOccurs=='unbounded' and type(any) in _seqtypes):
+ # raise EvaluateException, 'ReferenceProperties <any> assumed maxOccurs unbounded'
+
+ for v in any:
+ if not hasattr(v,'typecode'):
+ raise EvaluateException, '<any> element, instance missing typecode attribute'
+
+ pyobjs.append(v)
+
+ #pyobjs.append(v)
+
+ self.header_pyobjs = tuple(pyobjs)
+
+ def setResponseFromWSAddress(self, address, localURL):
+ '''Server-side has to set these fields in response.
+ address -- Address instance, representing a WS-Address
+ '''
+ self.From = localURL
+ self.header_pyobjs = None
+ pyobjs = []
+ namespaceURI = self.wsAddressURI
+
+ for nsuri,name,value in (\
+ (namespaceURI, "Action", self._action),
+ (namespaceURI, "MessageID","uuid:%s" %time.time()),
+ (namespaceURI, "RelatesTo", address.getMessageID()),
+ (namespaceURI, "To", self.anonymousURI),):
+
+ typecode = GED(nsuri, name)
+ pyobjs.append(typecode.pyclass(value))
+
+ typecode = GED(nsuri, "From")
+ pyobj = typecode.pyclass()
+ pyobj._Address = self.From
+ pyobjs.append(pyobj)
+ self.header_pyobjs = tuple(pyobjs)
+
+
+ def serialize(self, sw, **kw):
+ '''
+ sw -- SoapWriter instance, add WS-Address header.
+ '''
+ for pyobj in self.header_pyobjs:
+ if hasattr(pyobj, 'typecode') is False:
+ raise RuntimeError, 'all header pyobjs must have a typecode attribute'
+
+ sw.serialize_header(pyobj, **kw)
+
+
+ def parse(self, ps, **kw):
+ '''
+ ps -- ParsedSoap instance
+ '''
+ namespaceURI = self.wsAddressURI
+ elements = ("MessageID","Action","To","From","RelatesTo")
+ d = {namespaceURI:elements}
+ typecodes = self._getWSAddressTypeCodes(**d)
+ pyobjs = ps.ParseHeaderElements(typecodes)
+ self._messageID = pyobjs[(namespaceURI,elements[0])]
+ self._action = pyobjs[(namespaceURI,elements[1])]
+ self._addressTo = pyobjs[(namespaceURI,elements[2])]
+ self._from = pyobjs[(namespaceURI,elements[3])]
+ self._relatesTo = pyobjs[(namespaceURI,elements[4])]
+
+# TODO: Remove MessageContainer. Hopefully the new <any> functionality
+# makes this irrelevant. But could create an Interop problem.
+"""
+class MessageContainer:
+ '''Automatically wraps all primitive types so attributes
+ can be specified.
+ '''
+ class IntHolder(int): pass
+ class StrHolder(str): pass
+ class UnicodeHolder(unicode): pass
+ class LongHolder(long): pass
+ class FloatHolder(float): pass
+ class BoolHolder(int): pass
+ #class TupleHolder(tuple): pass
+ #class ListHolder(list): pass
+ typecode = None
+ pyclass_list = [IntHolder,StrHolder,UnicodeHolder,LongHolder,
+ FloatHolder,BoolHolder,]
+
+ def __setattr__(self, key, value):
+ '''wrap all primitives with Holders if present.
+ '''
+ if type(value) in _seqtypes:
+ value = list(value)
+ for indx in range(len(value)):
+ try:
+ item = self._wrap(value[indx])
+ except TypeError, ex:
+ pass
+ else:
+ value[indx] = item
+ elif type(value) is bool:
+ value = BoolHolder(value)
+ else:
+ try:
+ value = self._wrap(value)
+ except TypeError, ex:
+ pass
+ self.__dict__[key] = value
+
+ def _wrap(self, value):
+ '''wrap primitive, return None
+ '''
+ if value is None:
+ return value
+ for pyclass in self.pyclass_list:
+ if issubclass(value.__class__, pyclass): break
+ else:
+ raise TypeError, 'MessageContainer does not know about type %s' %(type(value))
+ return pyclass(value)
+
+ def setUp(self, typecode=None, pyclass=None):
+ '''set up all attribute names (aname) in this python instance.
+ If what is a ComplexType or a simpleType w/attributes instantiate
+ a new MessageContainer, else set attribute aname to None.
+ '''
+ if typecode is None:
+ typecode = self.typecode
+ else:
+ self.typecode = typecode
+
+ if not isinstance(typecode, TypeCode):
+ raise TypeError, 'typecode must be a TypeCode class instance'
+
+ if isinstance(typecode, ComplexType):
+ if typecode.has_attributes() is True:
+ setattr(self, typecode.attrs_aname, {})
+ if typecode.mixed is True:
+ setattr(self, typecode.mixed_aname, None)
+ for what in typecode.ofwhat:
+ setattr(self, what.aname, None)
+ if isinstance(what, ComplexType):
+ setattr(self, what.aname, MessageContainer())
+ getattr(self, what.aname).setUp(typecode=what)
+ else:
+ raise TypeError, 'Primitive type'
+"""
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/auth.py b/ZSI/auth.py
new file mode 100644
index 0000000..966b6b0
--- /dev/null
+++ b/ZSI/auth.py
@@ -0,0 +1,65 @@
+#! /usr/bin/env python
+# $Header$
+'''Simple CGI dispatching.
+'''
+
+from ZSI import *
+from ZSI import _copyright
+import base64, os
+
+_b64_decode = base64.decodestring
+
+# Typecode to parse a ZSI BasicAuth header.
+_auth_tc = TC.Struct(None,
+ [ TC.String('Name'), TC.String('Password') ],
+ extras=1)
+
+class AUTH:
+ '''Constants for authentication mechanisms.
+ '''
+ none = 0
+ httpbasic = 1
+ zsibasic = 2
+ httpdigest = 4
+
+class ClientBinding:
+ '''Information about the client that is connected to us.
+ '''
+
+ def __init__(self, ps):
+ self.ps, self.auth = \
+ ps, None
+ self.environ = os.environ.copy()
+ self.environ['CONTENT_LENGTH'] = str(0)
+
+ def GetAuth(self):
+ '''Return a tuple containing client authentication data.
+ '''
+ if self.auth: return self.auth
+ for elt in self.ps.GetMyHeaderElements():
+ if elt.localName == 'BasicAuth' \
+ and elt.namespaceURI == ZSI_SCHEMA_URI:
+ d = _auth_tc.parse(elt, self.ps)
+ self.auth = (AUTH.zsibasic, d['Name'], d['Password'])
+ return self.auth
+ ba = self.environ.get('HTTP_AUTHENTICATION')
+ if ba:
+ ba = ba.split(' ')
+ if len(ba) == 2 and ba[0].lower() == 'basic':
+ ba = _b64_decode(ba[1])
+ self.auth = (AUTH.httpbasic,) + tuple(ba.split(':'))
+ return self.auth
+ self.auth = (AUTH.none,)
+ return self.auth
+
+ def GetNS(self):
+ '''Return namespace for the top main request element.
+ '''
+ return self.ps.body_root.namespaceURI or ''
+
+ def GetRequest(self):
+ '''Return the ParsedSoap request.
+ '''
+ return self.ps
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/client.py b/ZSI/client.py
new file mode 100755
index 0000000..7b3a615
--- /dev/null
+++ b/ZSI/client.py
@@ -0,0 +1,541 @@
+#! /usr/bin/env python
+# $Header$
+#
+# Copyright (c) 2001 Zolera Systems. All rights reserved.
+
+from ZSI import _copyright, _seqtypes, ParsedSoap, SoapWriter, TC, ZSI_SCHEMA_URI,\
+ EvaluateException, FaultFromFaultMessage, _child_elements, _attrs, _find_arraytype,\
+ _find_type, _get_idstr, _get_postvalue_from_absoluteURI, FaultException, WSActionException
+from ZSI.auth import AUTH
+from ZSI.TC import AnyElement, AnyType, String, TypeCode, _get_global_element_declaration,\
+ _get_type_definition
+from ZSI.TCcompound import Struct
+import base64, httplib, Cookie, types, time, urlparse
+from ZSI.address import Address
+from ZSI.wstools.logging import getLogger as _GetLogger
+_b64_encode = base64.encodestring
+
+class _AuthHeader:
+ """<BasicAuth xmlns="ZSI_SCHEMA_URI">
+ <Name>%s</Name><Password>%s</Password>
+ </BasicAuth>
+ """
+ def __init__(self, name=None, password=None):
+ self.Name = name
+ self.Password = password
+_AuthHeader.typecode = Struct(_AuthHeader, ofwhat=(String((ZSI_SCHEMA_URI,'Name'), typed=False),
+ String((ZSI_SCHEMA_URI,'Password'), typed=False)), pname=(ZSI_SCHEMA_URI,'BasicAuth'),
+ typed=False)
+
+
+class _Caller:
+ '''Internal class used to give the user a callable object
+ that calls back to the Binding object to make an RPC call.
+ '''
+
+ def __init__(self, binding, name):
+ self.binding, self.name = binding, name
+
+ def __call__(self, *args):
+ return self.binding.RPC(None, self.name, args,
+ encodingStyle="http://schemas.xmlsoap.org/soap/encoding/",
+ replytype=TC.Any(self.name+"Response"))
+
+
+class _NamedParamCaller:
+ '''Similar to _Caller, expect that there are named parameters
+ not positional.
+ '''
+
+ def __init__(self, binding, name):
+ self.binding, self.name = binding, name
+
+ def __call__(self, **params):
+ # Pull out arguments that Send() uses
+ kw = { }
+ for key in [ 'auth_header', 'nsdict', 'requesttypecode' 'soapaction' ]:
+ if params.has_key(key):
+ kw[key] = params[key]
+ del params[key]
+ return self.binding.RPC(None, self.name, None,
+ encodingStyle="http://schemas.xmlsoap.org/soap/encoding/",
+ _args=params,
+ replytype=TC.Any(self.name+"Response", aslist=False),
+ **kw)
+
+
+class _Binding:
+ '''Object that represents a binding (connection) to a SOAP server.
+ Once the binding is created, various ways of sending and
+ receiving SOAP messages are available.
+ '''
+ defaultHttpTransport = httplib.HTTPConnection
+ defaultHttpsTransport = httplib.HTTPSConnection
+ logger = _GetLogger('ZSI.client.Binding')
+
+ def __init__(self, nsdict=None, transport=None, url=None, tracefile=None,
+ readerclass=None, writerclass=None, soapaction='',
+ wsAddressURI=None, sig_handler=None, transdict=None, **kw):
+ '''Initialize.
+ Keyword arguments include:
+ transport -- default use HTTPConnection.
+ transdict -- dict of values to pass to transport.
+ url -- URL of resource, POST is path
+ soapaction -- value of SOAPAction header
+ auth -- (type, name, password) triplet; default is unauth
+ nsdict -- namespace entries to add
+ tracefile -- file to dump packet traces
+ cert_file, key_file -- SSL data (q.v.)
+ readerclass -- DOM reader class
+ writerclass -- DOM writer class, implements MessageInterface
+ wsAddressURI -- namespaceURI of WS-Address to use. By default
+ it's not used.
+ sig_handler -- XML Signature handler, must sign and verify.
+ endPointReference -- optional Endpoint Reference.
+ '''
+ self.data = None
+ self.ps = None
+ self.user_headers = []
+ self.nsdict = nsdict or {}
+ self.transport = transport
+ self.transdict = transdict or {}
+ self.url = url
+ self.trace = tracefile
+ self.readerclass = readerclass
+ self.writerclass = writerclass
+ self.soapaction = soapaction
+ self.wsAddressURI = wsAddressURI
+ self.sig_handler = sig_handler
+ self.address = None
+ self.endPointReference = kw.get('endPointReference', None)
+ self.cookies = Cookie.SimpleCookie()
+ self.http_callbacks = {}
+
+ if kw.has_key('auth'):
+ self.SetAuth(*kw['auth'])
+ else:
+ self.SetAuth(AUTH.none)
+
+ def SetAuth(self, style, user=None, password=None):
+ '''Change auth style, return object to user.
+ '''
+ self.auth_style, self.auth_user, self.auth_pass = \
+ style, user, password
+ return self
+
+ def SetURL(self, url):
+ '''Set the URL we post to.
+ '''
+ self.url = url
+ return self
+
+ def ResetHeaders(self):
+ '''Empty the list of additional headers.
+ '''
+ self.user_headers = []
+ return self
+
+ def ResetCookies(self):
+ '''Empty the list of cookies.
+ '''
+ self.cookies = Cookie.SimpleCookie()
+
+ def AddHeader(self, header, value):
+ '''Add a header to send.
+ '''
+ self.user_headers.append((header, value))
+ return self
+
+ def __addcookies(self):
+ '''Add cookies from self.cookies to request in self.h
+ '''
+ for cname, morsel in self.cookies.items():
+ attrs = []
+ value = morsel.get('version', '')
+ if value != '' and value != '0':
+ attrs.append('$Version=%s' % value)
+ attrs.append('%s=%s' % (cname, morsel.coded_value))
+ value = morsel.get('path')
+ if value:
+ attrs.append('$Path=%s' % value)
+ value = morsel.get('domain')
+ if value:
+ attrs.append('$Domain=%s' % value)
+ self.h.putheader('Cookie', "; ".join(attrs))
+
+ def RPC(self, url, opname, obj, replytype=None, **kw):
+ '''Send a request, return the reply. See Send() and Recieve()
+ docstrings for details.
+ '''
+ self.Send(url, opname, obj, **kw)
+ return self.Receive(replytype, **kw)
+
+ def Send(self, url, opname, obj, nsdict={}, soapaction=None, wsaction=None,
+ endPointReference=None, **kw):
+ '''Send a message. If url is None, use the value from the
+ constructor (else error). obj is the object (data) to send.
+ Data may be described with a requesttypecode keyword, the default
+ is the class's typecode (if there is one), else Any.
+
+ Try to serialize as a Struct, if this is not possible serialize an Array. If
+ data is a sequence of built-in python data types, it will be serialized as an
+ Array, unless requesttypecode is specified.
+
+ arguments:
+ url --
+ opname -- struct wrapper
+ obj -- python instance
+
+ key word arguments:
+ nsdict --
+ soapaction --
+ wsaction -- WS-Address Action, goes in SOAP Header.
+ endPointReference -- set by calling party, must be an
+ EndPointReference type instance.
+ requesttypecode --
+
+ '''
+ url = url or self.url
+ endPointReference = endPointReference or self.endPointReference
+
+ # Serialize the object.
+ d = {}
+ d.update(self.nsdict)
+ d.update(nsdict)
+
+ sw = SoapWriter(nsdict=d, header=True, outputclass=self.writerclass,
+ encodingStyle=kw.get('encodingStyle'),)
+
+ requesttypecode = kw.get('requesttypecode')
+ if kw.has_key('_args'): #NamedParamBinding
+ tc = requesttypecode or TC.Any(pname=opname, aslist=False)
+ sw.serialize(kw['_args'], tc)
+ elif not requesttypecode:
+ tc = getattr(obj, 'typecode', None) or TC.Any(pname=opname, aslist=False)
+ try:
+ if type(obj) in _seqtypes:
+ obj = dict(map(lambda i: (i.typecode.pname,i), obj))
+ except AttributeError:
+ # can't do anything but serialize this in a SOAP:Array
+ tc = TC.Any(pname=opname, aslist=True)
+ else:
+ tc = TC.Any(pname=opname, aslist=False)
+
+ sw.serialize(obj, tc)
+ else:
+ sw.serialize(obj, requesttypecode)
+
+ #
+ # Determine the SOAP auth element. SOAP:Header element
+ if self.auth_style & AUTH.zsibasic:
+ sw.serialize_header(_AuthHeader(self.auth_user, self.auth_pass),
+ _AuthHeader.typecode)
+
+ #
+ # Serialize WS-Address
+ if self.wsAddressURI is not None:
+ if self.soapaction and wsaction.strip('\'"') != self.soapaction:
+ raise WSActionException, 'soapAction(%s) and WS-Action(%s) must match'\
+ %(self.soapaction,wsaction)
+
+ self.address = Address(url, self.wsAddressURI)
+ self.address.setRequest(endPointReference, wsaction)
+ self.address.serialize(sw)
+
+ #
+ # WS-Security Signature Handler
+ if self.sig_handler is not None:
+ self.sig_handler.sign(sw)
+
+ scheme,netloc,path,nil,nil,nil = urlparse.urlparse(url)
+ transport = self.transport
+ if transport is None and url is not None:
+ if scheme == 'https':
+ transport = self.defaultHttpsTransport
+ elif scheme == 'http':
+ transport = self.defaultHttpTransport
+ else:
+ raise RuntimeError, 'must specify transport or url startswith https/http'
+
+ # Send the request.
+ if issubclass(transport, httplib.HTTPConnection) is False:
+ raise TypeError, 'transport must be a HTTPConnection'
+
+ soapdata = str(sw)
+ self.h = transport(netloc, None, **self.transdict)
+ self.h.connect()
+ self.SendSOAPData(soapdata, url, soapaction, **kw)
+
+ def SendSOAPData(self, soapdata, url, soapaction, headers={}, **kw):
+ # Tracing?
+ if self.trace:
+ print >>self.trace, "_" * 33, time.ctime(time.time()), "REQUEST:"
+ print >>self.trace, soapdata
+
+ #scheme,netloc,path,nil,nil,nil = urlparse.urlparse(url)
+ path = _get_postvalue_from_absoluteURI(url)
+ self.h.putrequest("POST", path)
+ self.h.putheader("Content-length", "%d" % len(soapdata))
+ self.h.putheader("Content-type", 'text/xml; charset=utf-8')
+ self.__addcookies()
+
+ for header,value in headers.items():
+ self.h.putheader(header, value)
+
+ SOAPActionValue = '"%s"' % (soapaction or self.soapaction)
+ self.h.putheader("SOAPAction", SOAPActionValue)
+ if self.auth_style & AUTH.httpbasic:
+ val = _b64_encode(self.auth_user + ':' + self.auth_pass) \
+ .replace("\012", "")
+ self.h.putheader('Authorization', 'Basic ' + val)
+ elif self.auth_style == AUTH.httpdigest and not headers.has_key('Authorization') \
+ and not headers.has_key('Expect'):
+ def digest_auth_cb(response):
+ self.SendSOAPDataHTTPDigestAuth(response, soapdata, url, soapaction, **kw)
+ self.http_callbacks[401] = None
+ self.http_callbacks[401] = digest_auth_cb
+
+ for header,value in self.user_headers:
+ self.h.putheader(header, value)
+ self.h.endheaders()
+ self.h.send(soapdata)
+
+ # Clear prior receive state.
+ self.data, self.ps = None, None
+
+ def SendSOAPDataHTTPDigestAuth(self, response, soapdata, url, soapaction, **kw):
+ '''Resend the initial request w/http digest authorization headers.
+ The SOAP server has requested authorization. Fetch the challenge,
+ generate the authdict for building a response.
+ '''
+ if self.trace:
+ print >>self.trace, "------ Digest Auth Header"
+ url = url or self.url
+ if response.status != 401:
+ raise RuntimeError, 'Expecting HTTP 401 response.'
+ if self.auth_style != AUTH.httpdigest:
+ raise RuntimeError,\
+ 'Auth style(%d) does not support requested digest authorization.' %self.auth_style
+
+ from ZSI.digest_auth import fetch_challenge,\
+ generate_response,\
+ build_authorization_arg,\
+ dict_fetch
+
+ chaldict = fetch_challenge( response.getheader('www-authenticate') )
+ if dict_fetch(chaldict,'challenge','').lower() == 'digest' and \
+ dict_fetch(chaldict,'nonce',None) and \
+ dict_fetch(chaldict,'realm',None) and \
+ dict_fetch(chaldict,'qop',None):
+ authdict = generate_response(chaldict,
+ url, self.auth_user, self.auth_pass, method='POST')
+ headers = {\
+ 'Authorization':build_authorization_arg(authdict),
+ 'Expect':'100-continue',
+ }
+ self.SendSOAPData(soapdata, url, soapaction, headers, **kw)
+ return
+
+ raise RuntimeError,\
+ 'Client expecting digest authorization challenge.'
+
+ def ReceiveRaw(self, **kw):
+ '''Read a server reply, unconverted to any format and return it.
+ '''
+ if self.data: return self.data
+ trace = self.trace
+ while 1:
+ response = self.h.getresponse()
+ self.reply_code, self.reply_msg, self.reply_headers, self.data = \
+ response.status, response.reason, response.msg, response.read()
+ if trace:
+ print >>trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
+ for i in (self.reply_code, self.reply_msg,):
+ print >>trace, str(i)
+ print >>trace, "-------"
+ print >>trace, str(self.reply_headers)
+ print >>trace, self.data
+ saved = None
+ for d in response.msg.getallmatchingheaders('set-cookie'):
+ if d[0] in [ ' ', '\t' ]:
+ saved += d.strip()
+ else:
+ if saved: self.cookies.load(saved)
+ saved = d.strip()
+ if saved: self.cookies.load(saved)
+ if response.status == 401:
+ if not callable(self.http_callbacks.get(response.status,None)):
+ raise RuntimeError, 'HTTP Digest Authorization Failed'
+ self.http_callbacks[response.status](response)
+ continue
+ if response.status != 100: break
+
+ # The httplib doesn't understand the HTTP continuation header.
+ # Horrible internals hack to patch things up.
+ self.h._HTTPConnection__state = httplib._CS_REQ_SENT
+ self.h._HTTPConnection__response = None
+ return self.data
+
+ def IsSOAP(self):
+ if self.ps: return 1
+ self.ReceiveRaw()
+ mimetype = self.reply_headers.type
+ return mimetype == 'text/xml'
+
+ def ReceiveSOAP(self, readerclass=None, **kw):
+ '''Get back a SOAP message.
+ '''
+ if self.ps: return self.ps
+ if not self.IsSOAP():
+ raise TypeError(
+ 'Response is "%s", not "text/xml"' % self.reply_headers.type)
+ if len(self.data) == 0:
+ raise TypeError('Received empty response')
+
+ self.ps = ParsedSoap(self.data,
+ readerclass=readerclass or self.readerclass,
+ encodingStyle=kw.get('encodingStyle'))
+
+ if self.sig_handler is not None:
+ self.sig_handler.verify(self.ps)
+
+ return self.ps
+
+ def IsAFault(self):
+ '''Get a SOAP message, see if it has a fault.
+ '''
+ self.ReceiveSOAP()
+ return self.ps.IsAFault()
+
+ def ReceiveFault(self, **kw):
+ '''Parse incoming message as a fault. Raise TypeError if no
+ fault found.
+ '''
+ self.ReceiveSOAP(**kw)
+ if not self.ps.IsAFault():
+ raise TypeError("Expected SOAP Fault not found")
+ return FaultFromFaultMessage(self.ps)
+
+ def Receive(self, replytype, **kw):
+ '''Parse message, create Python object.
+
+ KeyWord data:
+ faults -- list of WSDL operation.fault typecodes
+ wsaction -- If using WS-Address, must specify Action value we expect to
+ receive.
+ '''
+ self.ReceiveSOAP(**kw)
+ if self.ps.IsAFault():
+ msg = FaultFromFaultMessage(self.ps)
+ raise FaultException(msg)
+
+ tc = replytype
+ if hasattr(replytype, 'typecode'):
+ tc = replytype.typecode
+
+ reply = self.ps.Parse(tc)
+ if self.address is not None:
+ self.address.checkResponse(self.ps, kw.get('wsaction'))
+ return reply
+
+ def __repr__(self):
+ return "<%s instance %s>" % (self.__class__.__name__, _get_idstr(self))
+
+
+class Binding(_Binding):
+ '''Object that represents a binding (connection) to a SOAP server.
+ Can be used in the "name overloading" style.
+
+ class attr:
+ gettypecode -- funcion that returns typecode from typesmodule,
+ can be set so can use whatever mapping you desire.
+ '''
+ gettypecode = staticmethod(lambda mod,e: getattr(mod, str(e.localName)).typecode)
+ logger = _GetLogger('ZSI.client.Binding')
+
+ def __init__(self, typesmodule=None, **kw):
+ self.typesmodule = typesmodule
+ _Binding.__init__(self, **kw)
+
+ def __getattr__(self, name):
+ '''Return a callable object that will invoke the RPC method
+ named by the attribute.
+ '''
+ if name[:2] == '__' and len(name) > 5 and name[-2:] == '__':
+ if hasattr(self, name): return getattr(self, name)
+ return getattr(self.__class__, name)
+ return _Caller(self, name)
+
+ def __parse_child(self, node):
+ '''for rpc-style map each message part to a class in typesmodule
+ '''
+ try:
+ tc = self.gettypecode(self.typesmodule, node)
+ except:
+ self.logger.debug('didnt find typecode for "%s" in typesmodule: %s',
+ node.localName, self.typesmodule)
+ tc = TC.Any(aslist=1)
+ return tc.parse(node, self.ps)
+
+ self.logger.debug('parse child with typecode : %s', tc)
+ try:
+ return tc.parse(node, self.ps)
+ except Exception:
+ self.logger.debug('parse failed try Any : %s', tc)
+
+ tc = TC.Any(aslist=1)
+ return tc.parse(node, self.ps)
+
+ def Receive(self, replytype, **kw):
+ '''Parse message, create Python object.
+
+ KeyWord data:
+ faults -- list of WSDL operation.fault typecodes
+ wsaction -- If using WS-Address, must specify Action value we expect to
+ receive.
+ '''
+ self.ReceiveSOAP(**kw)
+ ps = self.ps
+ tp = _find_type(ps.body_root)
+ isarray = ((type(tp) in (tuple,list) and tp[1] == 'Array') or _find_arraytype(ps.body_root))
+ if self.typesmodule is None or isarray:
+ return _Binding.Receive(self, replytype, **kw)
+
+ if ps.IsAFault():
+ msg = FaultFromFaultMessage(ps)
+ raise FaultException(msg)
+
+ tc = replytype
+ if hasattr(replytype, 'typecode'):
+ tc = replytype.typecode
+
+ #Ignore response wrapper
+ reply = {}
+ for elt in _child_elements(ps.body_root):
+ name = str(elt.localName)
+ reply[name] = self.__parse_child(elt)
+
+ if self.address is not None:
+ self.address.checkResponse(ps, kw.get('wsaction'))
+
+ return reply
+
+
+
+class NamedParamBinding(Binding):
+ '''Like Binding, except the argument list for invocation is
+ named parameters.
+ '''
+ logger = _GetLogger('ZSI.client.Binding')
+
+ def __getattr__(self, name):
+ '''Return a callable object that will invoke the RPC method
+ named by the attribute.
+ '''
+ if name[:2] == '__' and len(name) > 5 and name[-2:] == '__':
+ if hasattr(self, name): return getattr(self, name)
+ return getattr(self.__class__, name)
+ return _NamedParamCaller(self, name)
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/digest_auth.py b/ZSI/digest_auth.py
new file mode 100644
index 0000000..9c59e81
--- /dev/null
+++ b/ZSI/digest_auth.py
@@ -0,0 +1,105 @@
+#! /usr/bin/env python
+# $Header$
+'''Utilities for HTTP Digest Authentication
+'''
+
+from md5 import md5
+import random
+import time
+import httplib
+
+random.seed(int(time.time()*10))
+
+def H(val):
+ return md5(val).hexdigest()
+
+def KD(secret,data):
+ return H('%s:%s' % (secret,data))
+
+def A1(username,realm,passwd,nonce=None,cnonce=None):
+ if nonce and cnonce:
+ return '%s:%s:%s:%s:%s' % (username,realm,passwd,nonce,cnonce)
+ else:
+ return '%s:%s:%s' % (username,realm,passwd)
+
+def A2(method,uri):
+ return '%s:%s' % (method,uri)
+
+def dict_fetch(d,k,defval=None):
+ if d.has_key(k):
+ return d[k]
+ return defval
+
+def generate_response(chaldict,uri,username,passwd,method='GET',cnonce=None):
+ """
+ Generate an authorization response dictionary. chaldict should contain the digest
+ challenge in dict form. Use fetch_challenge to create a chaldict from a HTTPResponse
+ object like this: fetch_challenge(res.getheaders()).
+
+ returns dict (the authdict)
+
+ Note. Use build_authorization_arg() to turn an authdict into the final Authorization
+ header value.
+ """
+ authdict = {}
+ qop = dict_fetch(chaldict,'qop')
+ domain = dict_fetch(chaldict,'domain')
+ nonce = dict_fetch(chaldict,'nonce')
+ stale = dict_fetch(chaldict,'stale')
+ algorithm = dict_fetch(chaldict,'algorithm','MD5')
+ realm = dict_fetch(chaldict,'realm','MD5')
+ opaque = dict_fetch(chaldict,'opaque')
+ nc = "00000001"
+ if not cnonce:
+ cnonce = H(str(random.randint(0,10000000)))[:16]
+
+ if algorithm.lower()=='md5-sess':
+ a1 = A1(username,realm,passwd,nonce,cnonce)
+ else:
+ a1 = A1(username,realm,passwd)
+
+ a2 = A2(method,uri)
+
+ secret = H(a1)
+ data = '%s:%s:%s:%s:%s' % (nonce,nc,cnonce,qop,H(a2))
+ authdict['username'] = '"%s"' % username
+ authdict['realm'] = '"%s"' % realm
+ authdict['nonce'] = '"%s"' % nonce
+ authdict['uri'] = '"%s"' % uri
+ authdict['response'] = '"%s"' % KD(secret,data)
+ authdict['qop'] = '"%s"' % qop
+ authdict['nc'] = nc
+ authdict['cnonce'] = '"%s"' % cnonce
+
+ return authdict
+
+
+def fetch_challenge(http_header):
+ """
+ Create a challenge dictionary from a HTTPResponse objects getheaders() method.
+ """
+ chaldict = {}
+ vals = http_header.split(' ')
+ chaldict['challenge'] = vals[0]
+ for val in vals[1:]:
+ try:
+ a,b = val.split('=')
+ b=b.replace('"','')
+ b=b.replace("'",'')
+ b=b.replace(",",'')
+ chaldict[a.lower()] = b
+ except:
+ pass
+ return chaldict
+
+
+def build_authorization_arg(authdict):
+ """
+ Create an "Authorization" header value from an authdict (created by generate_response()).
+ """
+ vallist = []
+ for k in authdict.keys():
+ vallist += ['%s=%s' % (k,authdict[k])]
+ return 'Digest '+', '.join(vallist)
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/dispatch.py b/ZSI/dispatch.py
new file mode 100644
index 0000000..c97c3c3
--- /dev/null
+++ b/ZSI/dispatch.py
@@ -0,0 +1,290 @@
+#! /usr/bin/env python
+# $Header$
+'''Simple CGI dispatching.
+'''
+
+import types, os, sys
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+from ZSI import *
+from ZSI import _child_elements, _copyright, _seqtypes, _find_arraytype, _find_type, resolvers
+from ZSI.auth import _auth_tc, AUTH, ClientBinding
+
+
+# Client binding information is stored in a global. We provide an accessor
+# in case later on it's not.
+_client_binding = None
+
+def GetClientBinding():
+ '''Return the client binding object.
+ '''
+ return _client_binding
+
+gettypecode = lambda mod,e: getattr(mod, str(e.localName)).typecode
+def _Dispatch(ps, modules, SendResponse, SendFault, nsdict={}, typesmodule=None,
+ gettypecode=gettypecode, rpc=False, docstyle=False, **kw):
+ '''Find a handler for the SOAP request in ps; search modules.
+ Call SendResponse or SendFault to send the reply back, appropriately.
+
+ Behaviors:
+ default -- Call "handler" method with pyobj representation of body root, and return
+ a self-describing request (w/typecode). Parsing done via a typecode from
+ typesmodule, or Any.
+
+ docstyle -- Call "handler" method with ParsedSoap instance and parse result with an
+ XML typecode (DOM). Behavior, wrap result in a body_root "Response" appended message.
+
+ rpc -- Specify RPC wrapper of result. Behavior, ignore body root (RPC Wrapper)
+ of request, parse all "parts" of message via individual typecodes. Expect
+ the handler to return the parts of the message, whether it is a dict, single instance,
+ or a list try to serialize it as a Struct but if this is not possible put it in an Array.
+ Parsing done via a typecode from typesmodule, or Any.
+
+ '''
+ global _client_binding
+ try:
+ what = str(ps.body_root.localName)
+
+ # See what modules have the element name.
+ if modules is None:
+ modules = ( sys.modules['__main__'], )
+
+ handlers = [ getattr(m, what) for m in modules if hasattr(m, what) ]
+ if len(handlers) == 0:
+ raise TypeError("Unknown method " + what)
+
+ # Of those modules, see who's callable.
+ handlers = [ h for h in handlers if callable(h) ]
+ if len(handlers) == 0:
+ raise TypeError("Unimplemented method " + what)
+ if len(handlers) > 1:
+ raise TypeError("Multiple implementations found: " + `handlers`)
+ handler = handlers[0]
+
+ _client_binding = ClientBinding(ps)
+ if docstyle:
+ result = handler(ps.body_root)
+ tc = TC.XML(aslist=1, pname=what+'Response')
+ elif not rpc:
+ try:
+ tc = gettypecode(typesmodule, ps.body_root)
+ except Exception:
+ tc = TC.Any()
+
+ try:
+ arg = tc.parse(ps.body_root, ps)
+ except EvaluateException, ex:
+ SendFault(FaultFromZSIException(ex), **kw)
+ return
+
+ try:
+ result = handler(*arg)
+ except Exception,ex:
+ SendFault(FaultFromZSIException(ex), **kw)
+
+ try:
+ tc = result.typecode
+ except AttributeError,ex:
+ SendFault(FaultFromZSIException(ex), **kw)
+
+ elif typesmodule is not None:
+ kwargs = {}
+ for e in _child_elements(ps.body_root):
+ try:
+ tc = gettypecode(typesmodule, e)
+ except Exception:
+ tc = TC.Any()
+
+ try:
+ kwargs[str(e.localName)] = tc.parse(e, ps)
+ except EvaluateException, ex:
+ SendFault(FaultFromZSIException(ex), **kw)
+ return
+
+ result = handler(**kwargs)
+ aslist = False
+ # make sure data is wrapped, try to make this a Struct
+ if type(result) in _seqtypes:
+ for o in result:
+ aslist = hasattr(result, 'typecode')
+ if aslist: break
+ elif type(result) is not dict:
+ aslist = not hasattr(result, 'typecode')
+ result = (result,)
+
+ tc = TC.Any(pname=what+'Response', aslist=aslist)
+ else:
+ # if this is an Array, call handler with list
+ # if this is an Struct, call handler with dict
+ tp = _find_type(ps.body_root)
+ isarray = ((type(tp) in (tuple,list) and tp[1] == 'Array') or _find_arraytype(ps.body_root))
+ data = _child_elements(ps.body_root)
+ tc = TC.Any()
+ if isarray and len(data) == 0:
+ result = handler()
+ elif isarray:
+ try: arg = [ tc.parse(e, ps) for e in data ]
+ except EvaluateException, e:
+ #SendFault(FaultFromZSIException(e), **kw)
+ SendFault(RuntimeError("THIS IS AN ARRAY: %s" %isarray))
+ return
+
+ result = handler(*arg)
+ else:
+ try: kwarg = dict([ (str(e.localName),tc.parse(e, ps)) for e in data ])
+ except EvaluateException, e:
+ SendFault(FaultFromZSIException(e), **kw)
+ return
+
+ result = handler(**kwarg)
+
+ # reponse typecode
+ #tc = getattr(result, 'typecode', TC.Any(pname=what+'Response'))
+ tc = TC.Any(pname=what+'Response')
+
+ sw = SoapWriter(nsdict=nsdict)
+ sw.serialize(result, tc)
+ return SendResponse(str(sw), **kw)
+ except Fault, e:
+ return SendFault(e, **kw)
+ except Exception, e:
+ # Something went wrong, send a fault.
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+
+def _ModPythonSendXML(text, code=200, **kw):
+ req = kw['request']
+ req.content_type = 'text/xml'
+ req.content_length = len(text)
+ req.send_http_header()
+ req.write(text)
+
+
+def _ModPythonSendFault(f, **kw):
+ _ModPythonSendXML(f.AsSOAP(), 500, **kw)
+
+def _JonPySendFault(f, **kw):
+ _JonPySendXML(f.AsSOAP(), 500, **kw)
+
+def _JonPySendXML(text, code=200, **kw):
+ req = kw['request']
+ req.set_header("Content-Type", 'text/xml; charset="utf-8"')
+ req.set_header("Content-Length", str(len(text)))
+ req.write(text)
+
+def _CGISendXML(text, code=200, **kw):
+ print 'Status: %d' % code
+ print 'Content-Type: text/xml; charset="utf-8"'
+ print 'Content-Length: %d' % len(text)
+ print ''
+ print text
+
+def _CGISendFault(f, **kw):
+ _CGISendXML(f.AsSOAP(), 500, **kw)
+
+
+class SOAPRequestHandler(BaseHTTPRequestHandler):
+ '''SOAP handler.
+ '''
+ server_version = 'ZSI/1.1 ' + BaseHTTPRequestHandler.server_version
+
+ def send_xml(self, text, code=200):
+ '''Send some XML.
+ '''
+ self.send_response(code)
+ self.send_header('Content-type', 'text/xml; charset="utf-8"')
+ self.send_header('Content-Length', str(len(text)))
+ self.end_headers()
+ self.wfile.write(text)
+ self.wfile.flush()
+
+ def send_fault(self, f, code=500):
+ '''Send a fault.
+ '''
+ self.send_xml(f.AsSOAP(), code)
+
+ def do_POST(self):
+ '''The POST command.
+ '''
+ try:
+ ct = self.headers['content-type']
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, self.rfile)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ length = int(self.headers['content-length'])
+ ps = ParsedSoap(self.rfile.read(length))
+ except ParseException, e:
+ self.send_fault(FaultFromZSIException(e))
+ return
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ self.send_fault(FaultFromException(e, 1, sys.exc_info()[2]))
+ return
+
+ _Dispatch(ps, self.server.modules, self.send_xml, self.send_fault,
+ docstyle=self.server.docstyle, nsdict=self.server.nsdict,
+ typesmodule=self.server.typesmodule, rpc=self.server.rpc)
+
+def AsServer(port=80, modules=None, docstyle=False, nsdict={}, typesmodule=None,
+ rpc=False, addr=''):
+ address = (addr, port)
+ httpd = HTTPServer(address, SOAPRequestHandler)
+ httpd.modules = modules
+ httpd.docstyle = docstyle
+ httpd.nsdict = nsdict
+ httpd.typesmodule = typesmodule
+ httpd.rpc = rpc
+ httpd.serve_forever()
+
+def AsCGI(nsdict={}, typesmodule=None, rpc=False, modules=None):
+ '''Dispatch within a CGI script.
+ '''
+ if os.environ.get('REQUEST_METHOD') != 'POST':
+ _CGISendFault(Fault(Fault.Client, 'Must use POST'))
+ return
+ ct = os.environ['CONTENT_TYPE']
+ try:
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, sys.stdin)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ length = int(os.environ['CONTENT_LENGTH'])
+ ps = ParsedSoap(sys.stdin.read(length))
+ except ParseException, e:
+ _CGISendFault(FaultFromZSIException(e))
+ return
+ _Dispatch(ps, modules, _CGISendXML, _CGISendFault, nsdict=nsdict,
+ typesmodule=typesmodule, rpc=rpc)
+
+def AsHandler(request=None, modules=None, **kw):
+ '''Dispatch from within ModPython.'''
+ ps = ParsedSoap(request)
+ kw['request'] = request
+ _Dispatch(ps, modules, _ModPythonSendXML, _ModPythonSendFault, **kw)
+
+def AsJonPy(request=None, modules=None, **kw):
+ '''Dispatch within a jonpy CGI/FastCGI script.
+ '''
+
+ kw['request'] = request
+ if request.environ.get('REQUEST_METHOD') != 'POST':
+ _JonPySendFault(Fault(Fault.Client, 'Must use POST'), **kw)
+ return
+ ct = request.environ['CONTENT_TYPE']
+ try:
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, request.stdin)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ length = int(request.environ['CONTENT_LENGTH'])
+ ps = ParsedSoap(request.stdin.read(length))
+ except ParseException, e:
+ _JonPySendFault(FaultFromZSIException(e), **kw)
+ return
+ _Dispatch(ps, modules, _JonPySendXML, _JonPySendFault, **kw)
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/fault.py b/ZSI/fault.py
new file mode 100644
index 0000000..96f507f
--- /dev/null
+++ b/ZSI/fault.py
@@ -0,0 +1,262 @@
+#! /usr/bin/env python
+# $Header$
+'''Faults.
+'''
+
+from ZSI import _copyright, _children, _child_elements, \
+ _get_idstr, _stringtypes, _seqtypes, _Node, SoapWriter, ZSIException
+
+from ZSI.TCcompound import Struct
+from ZSI.TC import QName, URI, String, XMLString, AnyElement, UNBOUNDED
+
+from ZSI.wstools.Namespaces import SOAP, ZSI_SCHEMA_URI
+from ZSI.wstools.c14n import Canonicalize
+from ZSI.TC import ElementDeclaration
+
+import traceback, cStringIO as StringIO
+
+
+class Detail:
+ def __init__(self, any=None):
+ self.any = any
+
+Detail.typecode = Struct(Detail, [AnyElement(aname='any',minOccurs=0, maxOccurs="unbounded")], pname='detail', minOccurs=0)
+
+class FaultType:
+ def __init__(self, faultcode=None, faultstring=None, faultactor=None, detail=None):
+ self.faultcode = faultcode
+ self.faultstring= faultstring
+ self.faultactor = faultactor
+ self.detail = detail
+
+FaultType.typecode = \
+ Struct(FaultType,
+ [QName(pname='faultcode'),
+ String(pname='faultstring'),
+ URI(pname=(SOAP.ENV,'faultactor'), minOccurs=0),
+ Detail.typecode,
+ AnyElement(aname='any',minOccurs=0, maxOccurs=UNBOUNDED),
+ ],
+ pname=(SOAP.ENV,'Fault'),
+ inline=True,
+ hasextras=0,
+ )
+
+class ZSIHeaderDetail:
+ def __init__(self, detail):
+ self.any = detail
+
+ZSIHeaderDetail.typecode =\
+ Struct(ZSIHeaderDetail,
+ [AnyElement(aname='any', minOccurs=0, maxOccurs=UNBOUNDED)],
+ pname=(ZSI_SCHEMA_URI, 'detail'))
+
+
+class ZSIFaultDetailTypeCode(ElementDeclaration, Struct):
+ '''<ZSI:FaultDetail>
+ <ZSI:string>%s</ZSI:string>
+ <ZSI:trace>%s</ZSI:trace>
+ </ZSI:FaultDetail>
+ '''
+ schema = ZSI_SCHEMA_URI
+ literal = 'FaultDetail'
+ def __init__(self, **kw):
+ Struct.__init__(self, ZSIFaultDetail, [String(pname=(ZSI_SCHEMA_URI, 'string')),
+ String(pname=(ZSI_SCHEMA_URI, 'trace'),minOccurs=0),],
+ pname=(ZSI_SCHEMA_URI, 'FaultDetail'), **kw
+ )
+
+class ZSIFaultDetail:
+ def __init__(self, string=None, trace=None):
+ self.string = string
+ self.trace = trace
+
+ def __str__(self):
+ if self.trace:
+ return self.string + '\n[trace: ' + self.trace + ']'
+ return self.string
+
+ def __repr__(self):
+ return "<%s.ZSIFaultDetail %s>" % (__name__, _get_idstr(self))
+ZSIFaultDetail.typecode = ZSIFaultDetailTypeCode()
+
+
+class URIFaultDetailTypeCode(ElementDeclaration, Struct):
+ '''
+ <ZSI:URIFaultDetail>
+ <ZSI:URI>uri</ZSI:URI>
+ <ZSI:localname>localname</ZSI:localname>
+ </ZSI:URIFaultDetail>
+ '''
+ schema = ZSI_SCHEMA_URI
+ literal = 'URIFaultDetail'
+ def __init__(self, **kw):
+ Struct.__init__(self, URIFaultDetail,
+ [String(pname=(ZSI_SCHEMA_URI, 'URI')), String(pname=(ZSI_SCHEMA_URI, 'localname')),],
+ pname=(ZSI_SCHEMA_URI, 'URIFaultDetail'), **kw
+ )
+
+class URIFaultDetail:
+ def __init__(self, uri=None, localname=None):
+ self.URI = uri
+ self.localname = localname
+URIFaultDetail.typecode = URIFaultDetailTypeCode()
+
+
+class ActorFaultDetailTypeCode(ElementDeclaration, Struct):
+ '''
+ <ZSI:ActorFaultDetail>
+ <ZSI:URI>%s</ZSI:URI>
+ </ZSI:ActorFaultDetail>
+ '''
+ schema = ZSI_SCHEMA_URI
+ literal = 'ActorFaultDetail'
+ def __init__(self, **kw):
+ Struct.__init__(self, ActorFaultDetail, [String(pname=(ZSI_SCHEMA_URI, 'URI')),],
+ pname=(ZSI_SCHEMA_URI, 'ActorFaultDetail'), **kw
+ )
+
+class ActorFaultDetail:
+ def __init__(self, uri=None):
+ self.URI = uri
+ActorFaultDetail.typecode = ActorFaultDetailTypeCode()
+
+
+class Fault(ZSIException):
+ '''SOAP Faults.
+ '''
+
+ Client = "SOAP-ENV:Client"
+ Server = "SOAP-ENV:Server"
+ MU = "SOAP-ENV:MustUnderstand"
+
+ def __init__(self, code, string,
+ actor=None, detail=None, headerdetail=None):
+ if detail is not None and type(detail) not in _seqtypes:
+ detail = (detail,)
+ if headerdetail is not None and type(headerdetail) not in _seqtypes:
+ headerdetail = (headerdetail,)
+ self.code, self.string, self.actor, self.detail, self.headerdetail = \
+ code, string, actor, detail, headerdetail
+ ZSIException.__init__(self, code, string, actor, detail, headerdetail)
+
+ def DataForSOAPHeader(self):
+ if not self.headerdetail: return None
+ # SOAP spec doesn't say how to encode header fault data.
+ return ZSIHeaderDetail(self.headerdetail)
+
+ def serialize(self, sw):
+ '''Serialize the object.'''
+ detail = None
+ if self.detail is not None:
+ detail = Detail()
+ detail.any = self.detail
+
+ pyobj = FaultType(self.code, self.string, self.actor, detail)
+ sw.serialize(pyobj, typed=False)
+
+ def AsSOAP(self, **kw):
+
+ header = self.DataForSOAPHeader()
+ sw = SoapWriter(**kw)
+ self.serialize(sw)
+ if header is not None:
+ sw.serialize_header(header, header.typecode, typed=False)
+ return str(sw)
+
+ def __str__(self):
+ strng = str(self.string) + "\n"
+ if hasattr(self, 'detail'):
+ if hasattr(self.detail, '__len__'):
+ for d in self.detail:
+ strng += str(d)
+ else:
+ strng += str(self.detail)
+ return strng
+
+ def __repr__(self):
+ return "<%s.Fault at %s>" % (__name__, _get_idstr(self))
+
+ AsSoap = AsSOAP
+
+
+def FaultFromNotUnderstood(uri, localname, actor=None):
+ detail, headerdetail = None, URIFaultDetail(uri, localname)
+ return Fault(Fault.MU, 'SOAP mustUnderstand not understood',
+ actor, detail, headerdetail)
+
+
+def FaultFromActor(uri, actor=None):
+ detail, headerdetail = None, ActorFaultDetail(uri)
+ return Fault(Fault.Client, 'Cannot process specified actor',
+ actor, detail, headerdetail)
+
+
+def FaultFromZSIException(ex, actor=None):
+ '''Return a Fault object created from a ZSI exception object.
+ '''
+ mystr = getattr(ex, 'str', None) or str(ex)
+ mytrace = getattr(ex, 'trace', '')
+ elt = '''<ZSI:ParseFaultDetail>
+<ZSI:string>%s</ZSI:string>
+<ZSI:trace>%s</ZSI:trace>
+</ZSI:ParseFaultDetail>
+''' % (mystr, mytrace)
+ if getattr(ex, 'inheader', 0):
+ detail, headerdetail = None, elt
+ else:
+ detail, headerdetail = elt, None
+ return Fault(Fault.Client, 'Unparseable message',
+ actor, detail, headerdetail)
+
+
+def FaultFromException(ex, inheader, tb=None, actor=None):
+ '''Return a Fault object created from a Python exception.
+
+ <SOAP-ENV:Fault>
+ <faultcode>SOAP-ENV:Server</faultcode>
+ <faultstring>Processing Failure</faultstring>
+ <detail>
+ <ZSI:FaultDetail>
+ <ZSI:string></ZSI:string>
+ <ZSI:trace></ZSI:trace>
+ </ZSI:FaultDetail>
+ </detail>
+ </SOAP-ENV:Fault>
+ '''
+ tracetext = None
+ if tb:
+ try:
+ lines = '\n'.join(['%s:%d:%s' % (name, line, func)
+ for name, line, func, text in traceback.extract_tb(tb)])
+ except:
+ pass
+ else:
+ tracetext = lines
+
+ exceptionName = ""
+ try:
+ exceptionName = ":".join([ex.__module__, ex.__class__.__name__])
+ except: pass
+ elt = ZSIFaultDetail(string=exceptionName + "\n" + str(ex), trace=tracetext)
+ if inheader:
+ detail, headerdetail = None, elt
+ else:
+ detail, headerdetail = elt, None
+ return Fault(Fault.Server, 'Processing Failure',
+ actor, detail, headerdetail)
+
+
+def FaultFromFaultMessage(ps):
+ '''Parse the message as a fault.
+ '''
+ pyobj = ps.Parse(FaultType.typecode)
+
+ if pyobj.detail == None: detailany = None
+ else: detailany = pyobj.detail.any
+
+ return Fault(pyobj.faultcode, pyobj.faultstring,
+ pyobj.faultactor, detailany)
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/generate/__init__.py b/ZSI/generate/__init__.py
new file mode 100644
index 0000000..07f9493
--- /dev/null
+++ b/ZSI/generate/__init__.py
@@ -0,0 +1,24 @@
+# $Id: __init__.py 1261 2006-10-10 23:45:43Z boverhof $
+
+__all__ = ['wsdl2python', 'utility', 'containers', 'commands']
+
+class WSDLFormatError(Exception):
+ '''Detected errors in the WSDL document.
+ '''
+
+class WsdlGeneratorError(Exception):
+ pass
+
+class Wsdl2PythonError(Exception):
+ pass
+
+class WSInteropError(Exception):
+ '''Conformance to WS-I Basic-Profile 1.0 specification
+ '''
+
+class WSISpec:
+ R2203 = 'An rpc-literal binding in a DESCRIPTION MUST refer, in its soapbind:body element(s), only to wsdl:part element(s) that have been defined using the type attribute.'
+ R2710 = 'The operations in a wsdl:binding in a DESCRIPTION MUST result in wire signatures that are different from one another.'
+ R2717 = 'An rpc-literal binding in a DESCRIPTION MUST have the namespace attribute specified, the value of which MUST be an absolute URI, on contained soapbind:body elements.'
+ R2729 = 'A MESSAGE described with an rpc-literal binding that is a response message MUST have a wrapper element whose name is the corresponding wsdl:operation name suffixed with the string "Response"'
+
diff --git a/ZSI/generate/commands.py b/ZSI/generate/commands.py
new file mode 100644
index 0000000..9b9603e
--- /dev/null
+++ b/ZSI/generate/commands.py
@@ -0,0 +1,304 @@
+############################################################################
+# Joshua Boverhof<JRBoverhof@lbl.gov>, LBNL
+# Monte Goode <MMGoode@lbl.gov>, LBNL
+# See Copyright for copyright notice!
+############################################################################
+
+import exceptions, sys, optparse, os, warnings
+from operator import xor
+import ZSI
+from ConfigParser import ConfigParser
+from ZSI.generate.wsdl2python import WriteServiceModule, ServiceDescription
+from ZSI.wstools import WSDLTools, XMLSchema
+from ZSI.wstools.logging import setBasicLoggerDEBUG
+from ZSI.generate import containers, utility
+from ZSI.generate.utility import NCName_to_ClassName as NC_to_CN, TextProtect
+from ZSI.generate.wsdl2dispatch import ServiceModuleWriter as ServiceDescription
+from ZSI.generate.wsdl2dispatch import DelAuthServiceModuleWriter as DelAuthServiceDescription
+from ZSI.generate.wsdl2dispatch import WSAServiceModuleWriter as ServiceDescriptionWSA
+from ZSI.generate.wsdl2dispatch import DelAuthWSAServiceModuleWriter as DelAuthServiceDescriptionWSA
+
+warnings.filterwarnings('ignore', '', exceptions.UserWarning)
+def SetDebugCallback(option, opt, value, parser, *args, **kwargs):
+ setBasicLoggerDEBUG()
+ warnings.resetwarnings()
+
+def SetPyclassMetaclass(option, opt, value, parser, *args, **kwargs):
+ """set up pyclass metaclass for complexTypes"""
+ from ZSI.generate.containers import ServiceHeaderContainer, TypecodeContainerBase, TypesHeaderContainer
+ TypecodeContainerBase.metaclass = kwargs['metaclass']
+ TypesHeaderContainer.imports.append(\
+ 'from %(module)s import %(metaclass)s' %kwargs
+ )
+ ServiceHeaderContainer.imports.append(\
+ 'from %(module)s import %(metaclass)s' %kwargs
+ )
+
+def SetUpTwistedClient(option, opt, value, parser, *args, **kwargs):
+ from ZSI.generate.containers import ServiceHeaderContainer
+ ServiceHeaderContainer.imports.remove('from ZSI import client')
+ ServiceHeaderContainer.imports.append('from ZSI.twisted import client')
+
+
+def SetUpLazyEvaluation(option, opt, value, parser, *args, **kwargs):
+ from ZSI.generate.containers import TypecodeContainerBase
+ TypecodeContainerBase.lazy = True
+
+
+def formatSchemaObject(fname, schemaObj):
+ """ In the case of a 'schema only' generation (-s) this creates
+ a fake wsdl object that will function w/in the adapters
+ and allow the generator to do what it needs to do.
+ """
+
+ class fake:
+ pass
+
+ f = fake()
+
+ if fname.rfind('/'):
+ tmp = fname[fname.rfind('/') + 1 :].split('.')
+ else:
+ tmp = fname.split('.')
+
+ f.name = tmp[0] + '_' + tmp[1]
+ f.types = { schemaObj.targetNamespace : schemaObj }
+
+ return f
+
+def wsdl2py(args=None):
+ """
+ A utility for automatically generating client interface code from a wsdl
+ definition, and a set of classes representing element declarations and
+ type definitions. This will produce two files in the current working
+ directory named after the wsdl definition name.
+
+ eg. <definition name='SampleService'>
+ SampleService.py
+ SampleService_types.py
+ """
+
+ op = optparse.OptionParser(usage="usage: %prog [options]",
+ description=wsdl2py.__doc__)
+
+ # Basic options
+ op.add_option("-f", "--file",
+ action="store", dest="file", default=None, type="string",
+ help="FILE to load wsdl from")
+ op.add_option("-u", "--url",
+ action="store", dest="url", default=None, type="string",
+ help="URL to load wsdl from")
+ op.add_option("-x", "--schema",
+ action="store_true", dest="schema", default=False,
+ help="process just the schema from an xsd file [no services]")
+ op.add_option("-d", "--debug",
+ action="callback", callback=SetDebugCallback,
+ help="debug output")
+
+ # WS Options
+ op.add_option("-a", "--address",
+ action="store_true", dest="address", default=False,
+ help="ws-addressing support, must include WS-Addressing schema.")
+
+ # pyclass Metaclass
+ op.add_option("-b", "--complexType",
+ action="callback", callback=SetPyclassMetaclass,
+ callback_kwargs={'module':'ZSI.generate.pyclass',
+ 'metaclass':'pyclass_type'},
+ help="add convenience functions for complexTypes, including Getters, Setters, factory methods, and properties (via metaclass). *** DONT USE WITH --simple-naming ***")
+
+ # Lazy Evaluation of Typecodes (done at serialization/parsing when needed).
+ op.add_option("-l", "--lazy",
+ action="callback", callback=SetUpLazyEvaluation,
+ callback_kwargs={},
+ help="EXPERIMENTAL: recursion error solution, lazy evalution of typecodes")
+
+ # Use Twisted
+ op.add_option("-w", "--twisted",
+ action="callback", callback=SetUpTwistedClient,
+ callback_kwargs={'module':'ZSI.generate.pyclass',
+ 'metaclass':'pyclass_type'},
+ help="generate a twisted.web client, dependencies python>=2.4, Twisted>=2.0.0, TwistedWeb>=0.5.0")
+
+ # Extended generation options
+ op.add_option("-e", "--extended",
+ action="store_true", dest="extended", default=False,
+ help="Do Extended code generation.")
+ op.add_option("-z", "--aname",
+ action="store", dest="aname", default=None, type="string",
+ help="pass in a function for attribute name creation")
+ op.add_option("-t", "--types",
+ action="store", dest="types", default=None, type="string",
+ help="file to load types from")
+ op.add_option("-o", "--output-dir",
+ action="store", dest="output_dir", default=".", type="string",
+ help="Write generated files to OUTPUT_DIR")
+ op.add_option("-s", "--simple-naming",
+ action="store_true", dest="simple_naming", default=False,
+ help="Simplify generated naming.")
+ op.add_option("-c", "--clientClassSuffix",
+ action="store", dest="clientClassSuffix", default=None, type="string",
+ help="Suffix to use for service client class (default \"SOAP\")")
+ op.add_option("-m", "--pyclassMapModule",
+ action="store", dest="pyclassMapModule", default=None, type="string",
+ help="Python file that maps external python classes to a schema type. The classes are used as the \"pyclass\" for that type. The module should contain a dict() called mapping in the format: mapping = {schemaTypeName:(moduleName.py,className) }")
+
+ if args is None:
+ (options, args) = op.parse_args()
+ else:
+ (options, args) = op.parse_args(args)
+
+ if not xor(options.file is None, options.url is None):
+ print 'Must specify either --file or --url option'
+ sys.exit(os.EX_USAGE)
+
+ location = options.file
+ if options.url is not None:
+ location = options.url
+
+ if options.schema is True:
+ reader = XMLSchema.SchemaReader(base_url=location)
+ else:
+ reader = WSDLTools.WSDLReader()
+
+ load = reader.loadFromFile
+ if options.url is not None:
+ load = reader.loadFromURL
+
+ wsdl = None
+ try:
+ wsdl = load(location)
+ except Exception, e:
+ print "Error loading %s: \n\t%s" % (location, e)
+ # exit code UNIX specific, Windows?
+ sys.exit(os.EX_NOINPUT)
+
+ if options.simple_naming:
+ # Use a different client suffix
+ WriteServiceModule.client_module_suffix = "_client"
+ # Write messages definitions to a separate file.
+ ServiceDescription.separate_messages = True
+ # Use more simple type and element class names
+ containers.SetTypeNameFunc( lambda n: '%s_' %(NC_to_CN(n)) )
+ containers.SetElementNameFunc( lambda n: '%s' %(NC_to_CN(n)) )
+ # Don't add "_" to the attribute name (remove when --aname works well)
+ containers.ContainerBase.func_aname = lambda instnc,n: TextProtect(str(n))
+ # write out the modules with their names rather than their number.
+ utility.namespace_name = lambda cls, ns: utility.Namespace2ModuleName(ns)
+
+ if options.clientClassSuffix:
+ from ZSI.generate.containers import ServiceContainerBase
+ ServiceContainerBase.clientClassSuffix = options.clientClassSuffix
+
+ if options.schema is True:
+ wsdl = formatSchemaObject(location, wsdl)
+
+ if options.aname is not None:
+ args = options.aname.rsplit('.',1)
+ assert len(args) == 2, 'expecting module.function'
+ # The following exec causes a syntax error.
+ #exec('from %s import %s as FUNC' %(args[0],args[1]))
+ assert callable(FUNC),\
+ '%s must be a callable method with one string parameter' %options.aname
+ from ZSI.generate.containers import TypecodeContainerBase
+ TypecodeContainerBase.func_aname = staticmethod(FUNC)
+
+ if options.pyclassMapModule != None:
+ mod = __import__(options.pyclassMapModule)
+ components = options.pyclassMapModule.split('.')
+ for comp in components[1:]:
+ mod = getattr(mod, comp)
+ extPyClasses = mod.mapping
+ else:
+ extPyClasses = None
+
+ wsm = WriteServiceModule(wsdl, addressing=options.address, do_extended=options.extended, extPyClasses=extPyClasses)
+ if options.types != None:
+ wsm.setTypesModuleName(options.types)
+ if options.schema is False:
+ fd = open(os.path.join(options.output_dir, '%s.py' %wsm.getClientModuleName()), 'w+')
+ # simple naming writes the messages to a separate file
+ if not options.simple_naming:
+ wsm.writeClient(fd)
+ else: # provide a separate file to store messages to.
+ msg_fd = open(os.path.join(options.output_dir, '%s.py' %wsm.getMessagesModuleName()), 'w+')
+ wsm.writeClient(fd, msg_fd=msg_fd)
+ msg_fd.close()
+ fd.close()
+
+ fd = open( os.path.join(options.output_dir, '%s.py' %wsm.getTypesModuleName()), 'w+')
+ wsm.writeTypes(fd)
+ fd.close()
+
+
+def wsdl2dispatch(args=None):
+ """
+ wsdl2dispatch
+
+ A utility for automatically generating service skeleton code from a wsdl
+ definition.
+ """
+
+ op = optparse.OptionParser()
+ op.add_option("-f", "--file",
+ action="store", dest="file", default=None, type="string",
+ help="file to load wsdl from")
+ op.add_option("-u", "--url",
+ action="store", dest="url", default=None, type="string",
+ help="URL to load wsdl from")
+ op.add_option("-a", "--address",
+ action="store_true", dest="address", default=False,
+ help="ws-addressing support, must include WS-Addressing schema.")
+ op.add_option("-e", "--extended",
+ action="store_true", dest="extended", default=False,
+ help="Extended code generation.")
+ op.add_option("-d", "--debug",
+ action="callback", callback=SetDebugCallback,
+ help="debug output")
+ op.add_option("-t", "--types",
+ action="store", dest="types", default=None, type="string",
+ help="Write generated files to OUTPUT_DIR")
+ op.add_option("-o", "--output-dir",
+ action="store", dest="output_dir", default=".", type="string",
+ help="file to load types from")
+ op.add_option("-s", "--simple-naming",
+ action="store_true", dest="simple_naming", default=False,
+ help="Simplify generated naming.")
+
+ if args is None:
+ (options, args) = op.parse_args()
+ else:
+ (options, args) = op.parse_args(args)
+
+ if options.simple_naming:
+ ServiceDescription.server_module_suffix = '_interface'
+ ServiceDescription.func_aname = lambda instnc,n: TextProtect(n)
+ ServiceDescription.separate_messages = True
+ # use module names rather than their number.
+ utility.namespace_name = lambda cls, ns: utility.Namespace2ModuleName(ns)
+
+ reader = WSDLTools.WSDLReader()
+ wsdl = None
+ if options.file is not None:
+ wsdl = reader.loadFromFile(options.file)
+ elif options.url is not None:
+ wsdl = reader.loadFromURL(options.url)
+
+ assert wsdl is not None, 'Must specify WSDL either with --file or --url'
+
+ ss = None
+ if options.address is True:
+ if options.extended:
+ ss = DelAuthServiceDescriptionWSA(do_extended=options.extended)
+ else:
+ ss = ServiceDescriptionWSA(do_extended=options.extended)
+ else:
+ if options.extended:
+ ss = DelAuthServiceDescription(do_extended=options.extended)
+ else:
+ ss = ServiceDescription(do_extended=options.extended)
+
+ ss.fromWSDL(wsdl)
+ fd = open( os.path.join(options.output_dir, ss.getServiceModuleName()+'.py'), 'w+')
+ ss.write(fd)
+ fd.close()
diff --git a/ZSI/generate/containers.py b/ZSI/generate/containers.py
new file mode 100644
index 0000000..3a59fda
--- /dev/null
+++ b/ZSI/generate/containers.py
@@ -0,0 +1,2869 @@
+############################################################################
+# Monte M. Goode, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+# contains text container classes for new generation generator
+
+# $Id: containers.py 1276 2006-10-23 23:18:07Z boverhof $
+import types
+from utility import StringWriter, TextProtect, TextProtectAttributeName,\
+ GetPartsSubNames
+from utility import NamespaceAliasDict as NAD, NCName_to_ClassName as NC_to_CN
+
+import ZSI
+from ZSI.TC import _is_xsd_or_soap_ns
+from ZSI.wstools import XMLSchema, WSDLTools
+from ZSI.wstools.Namespaces import SCHEMA, SOAP, WSDL
+from ZSI.wstools.logging import getLogger as _GetLogger
+from ZSI.typeinterpreter import BaseTypeInterpreter
+from ZSI.generate import WSISpec, WSInteropError, Wsdl2PythonError,\
+ WsdlGeneratorError, WSDLFormatError
+
+ID1 = ' '
+ID2 = 2*ID1
+ID3 = 3*ID1
+ID4 = 4*ID1
+ID5 = 5*ID1
+ID6 = 6*ID1
+
+KW = {'ID1':ID1, 'ID2':ID2, 'ID3':ID3,'ID4':ID4, 'ID5':ID5, 'ID6':ID6,}
+
+DEC = '_Dec'
+DEF = '_Def'
+
+"""
+type_class_name -- function to return the name formatted as a type class.
+element_class_name -- function to return the name formatted as an element class.
+"""
+type_class_name = lambda n: '%s%s' %(NC_to_CN(n), DEF)
+element_class_name = lambda n: '%s%s' %(NC_to_CN(n), DEC)
+
+
+def IsRPC(item):
+ """item -- OperationBinding instance.
+ """
+ if not isinstance(item, WSDLTools.OperationBinding):
+ raise TypeError, 'IsRPC takes 1 argument of type WSDLTools.OperationBinding'
+ soapbinding = item.getBinding().findBinding(WSDLTools.SoapBinding)
+ sob = item.findBinding(WSDLTools.SoapOperationBinding)
+ style = soapbinding.style
+ if sob is not None:
+ style = sob.style or soapbinding.style
+ return style == 'rpc'
+
+
+def IsLiteral(item):
+ """item -- MessageRoleBinding instance.
+ """
+ if not isinstance(item, WSDLTools.MessageRoleBinding):
+ raise TypeError, 'IsLiteral takes 1 argument of type WSDLTools.MessageRoleBinding'
+ sbb = None
+ if item.type == 'input' or item.type == 'output':
+ sbb = item.findBinding(WSDLTools.SoapBodyBinding)
+ if sbb is None:
+ raise ValueError, 'Missing soap:body binding.'
+ return sbb.use == 'literal'
+
+
+def SetTypeNameFunc(func):
+ global type_class_name
+ type_class_name = func
+
+def SetElementNameFunc(func):
+ global element_class_name
+ element_class_name = func
+
+def GetClassNameFromSchemaItem(item,do_extended=False):
+ '''
+ '''
+ assert isinstance(item, XMLSchema.XMLSchemaComponent), 'must be a schema item.'
+ alias = NAD.getAlias(item.getTargetNamespace())
+ if item.isDefinition() is True:
+ return '%s.%s' %(alias, NC_to_CN('%s' %type_class_name(item.getAttributeName())))
+ return None
+
+def FromMessageGetSimpleElementDeclaration(message):
+ '''If message consists of one part with an element attribute,
+ and this element is a simpleType return a string representing
+ the python type, else return None.
+
+ '''
+ assert isinstance(message, WSDLTools.Message), 'expecting WSDLTools.Message'
+
+ if len(message.parts) == 1 and message.parts[0].element is not None:
+ part = message.parts[0]
+ nsuri,name = part.element
+ wsdl = message.getWSDL()
+ types = wsdl.types
+ if types.has_key(nsuri) and types[nsuri].elements.has_key(name):
+ e = types[nsuri].elements[name]
+ if isinstance(e, XMLSchema.ElementDeclaration) is True and e.getAttribute('type'):
+ typ = e.getAttribute('type')
+ bt = BaseTypeInterpreter()
+ ptype = bt.get_pythontype(typ[1], typ[0])
+ return ptype
+
+ return None
+
+
+class AttributeMixIn:
+ '''for containers that can declare attributes.
+ Class Attributes:
+ attribute_typecode -- typecode attribute name typecode dict
+ built_in_refs -- attribute references that point to built-in
+ types. Skip resolving them into attribute declarations.
+ '''
+ attribute_typecode = 'self.attribute_typecode_dict'
+ built_in_refs = [(SOAP.ENC, 'arrayType'),]
+
+ def _setAttributes(self, attributes):
+ '''parameters
+ attributes -- a flat list of all attributes,
+ from this list all items in attribute_typecode_dict will
+ be generated into attrComponents.
+
+ returns a list of strings representing the attribute_typecode_dict.
+ '''
+ atd = self.attribute_typecode
+ atd_list = formatted_attribute_list = []
+ if not attributes:
+ return formatted_attribute_list
+
+ atd_list.append('# attribute handling code')
+ for a in attributes:
+
+ if a.isWildCard() and a.isDeclaration():
+ atd_list.append(\
+ '%s[("%s","anyAttribute")] = ZSI.TC.AnyElement()'\
+ % (atd, SCHEMA.XSD3)
+ )
+ elif a.isDeclaration():
+ tdef = a.getTypeDefinition('type')
+ if tdef is not None:
+ tc = '%s.%s(None)' %(NAD.getAlias(tdef.getTargetNamespace()),
+ self.mangle(type_class_name(tdef.getAttributeName()))
+ )
+ else:
+ # built-in
+ t = a.getAttribute('type')
+ try:
+ tc = BTI.get_typeclass(t[1], t[0])
+ except:
+ # hand back a string by default.
+ tc = ZSI.TC.String
+
+ if tc is not None:
+ tc = '%s()' %tc
+
+ key = None
+ if a.getAttribute('form') == 'qualified':
+ key = '("%s","%s")' % ( a.getTargetNamespace(),
+ a.getAttribute('name') )
+ elif a.getAttribute('form') == 'unqualified':
+ key = '"%s"' % a.getAttribute('name')
+ else:
+ raise ContainerError, \
+ 'attribute form must be un/qualified %s' \
+ % a.getAttribute('form')
+
+ atd_list.append(\
+ '%s[%s] = %s' % (atd, key, tc)
+ )
+ elif a.isReference() and a.isAttributeGroup():
+ # flatten 'em out....
+ for ga in a.getAttributeGroup().getAttributeContent():
+ if not ga.isAttributeGroup():
+ attributes += (ga,)
+ continue
+ elif a.isReference():
+ try:
+ ga = a.getAttributeDeclaration()
+ except XMLSchema.SchemaError:
+ key = a.getAttribute('ref')
+ self.logger.debug('No schema item for attribute ref (%s, %s)' %key)
+ if key in self.built_in_refs: continue
+ raise
+
+ tp = None
+ if ga is not None:
+ tp = ga.getTypeDefinition('type')
+ key = '("%s","%s")' %(ga.getTargetNamespace(),
+ ga.getAttribute('name'))
+
+ if ga is None:
+ # TODO: probably SOAPENC:arrayType
+ key = '("%s","%s")' %(
+ a.getAttribute('ref').getTargetNamespace(),
+ a.getAttribute('ref').getName())
+ atd_list.append(\
+ '%s[%s] = ZSI.TC.String()' %(atd, key)
+ )
+ elif tp is None:
+ # built in simple type
+ try:
+ namespace,typeName = ga.getAttribute('type')
+ except TypeError, ex:
+ # TODO: attribute declaration could be anonymous type
+ # hack in something to work
+ atd_list.append(\
+ '%s[%s] = ZSI.TC.String()' %(atd, key)
+ )
+ else:
+ atd_list.append(\
+ '%s[%s] = %s()' %(atd, key,
+ BTI.get_typeclass(typeName, namespace))
+ )
+ else:
+ typeName = tp.getAttribute('name')
+ namespace = tp.getTargetNamespace()
+ alias = NAD.getAlias(namespace)
+ key = '("%s","%s")' \
+ % (ga.getTargetNamespace(),ga.getAttribute('name'))
+ atd_list.append(\
+ '%s[%s] = %s.%s(None)' \
+ % (atd, key, alias, type_class_name(typeName))
+ )
+ else:
+ raise TypeError, 'expecting an attribute: %s' %a.getItemTrace()
+
+ return formatted_attribute_list
+
+
+class ContainerError(Exception):
+ pass
+
+
+class ContainerBase:
+ '''Base class for all Containers.
+ func_aname -- function that takes name, and returns aname.
+ '''
+ func_aname = TextProtectAttributeName
+ func_aname = staticmethod(func_aname)
+ logger = _GetLogger("ContainerBase")
+
+ def __init__(self):
+ self.content = StringWriter('\n')
+ self.__setup = False
+ self.ns = None
+
+ def __str__(self):
+ return self.getvalue()
+
+ # - string content methods
+ def mangle(self, s):
+ '''class/variable name illegalities
+ '''
+ return TextProtect(s)
+
+ def write(self, s):
+ self.content.write(s)
+
+ def writeArray(self, a):
+ self.content.write('\n'.join(a))
+
+ def _setContent(self):
+ '''override in subclasses. formats the content in the desired way.
+ '''
+ raise NotImplementedError, 'abstract method not implemented'
+
+ def getvalue(self):
+ if not self.__setup:
+ self._setContent()
+ self.__setup = True
+
+ return self.content.getvalue()
+
+ # - namespace utility methods
+ def getNSAlias(self):
+ if self.ns:
+ return NAD.getAlias(self.ns)
+ raise ContainerError, 'no self.ns attr defined in %s' % self.__class__
+
+ def getNSModuleName(self):
+ if self.ns:
+ return NAD.getModuleName(self.ns)
+ raise ContainerError, 'no self.ns attr defined in %s' % self.__class__
+
+ def getAttributeName(self, name):
+ '''represents the aname
+ '''
+ if self.func_aname is None:
+ return name
+ assert callable(self.func_aname), \
+ 'expecting callable method for attribute func_aname, not %s' %type(self.func_aname)
+ f = self.func_aname
+ return f(name)
+
+
+# -- containers for services file components
+
+class ServiceContainerBase(ContainerBase):
+ clientClassSuffix = "SOAP"
+ logger = _GetLogger("ServiceContainerBase")
+
+
+class ServiceHeaderContainer(ServiceContainerBase):
+ imports = ['\nimport urlparse, types',
+ 'from ZSI.TCcompound import ComplexType, Struct',
+ 'from ZSI import client',
+ 'import ZSI'
+ ]
+ logger = _GetLogger("ServiceHeaderContainer")
+
+ def __init__(self, do_extended=False):
+ ServiceContainerBase.__init__(self)
+
+ self.basic = self.imports[:]
+ self.types = None
+ self.messages = None
+ self.extras = []
+ self.do_extended = do_extended
+
+ def setTypesModuleName(self, module):
+ self.types = module
+
+ def setMessagesModuleName(self, module):
+ self.messages = module
+
+ def appendImport(self, statement):
+ '''append additional import statement(s).
+ import_stament -- tuple or list or str
+ '''
+ if type(statement) in (list,tuple):
+ self.extras += statement
+ else:
+ self.extras.append(statement)
+
+ def _setContent(self):
+ if self.messages:
+ self.write('from %s import *' % self.messages)
+ if self.types:
+ self.write('from %s import *' % self.types)
+
+ imports = self.basic[:]
+ imports += self.extras
+ self.writeArray(imports)
+
+
+class ServiceLocatorContainer(ServiceContainerBase):
+ logger = _GetLogger("ServiceLocatorContainer")
+
+ def __init__(self):
+ ServiceContainerBase.__init__(self)
+ self.serviceName = None
+ self.portInfo = []
+ self.locatorName = None
+ self.portMethods = []
+
+ def setUp(self, service):
+ assert isinstance(service, WSDLTools.Service), \
+ 'expecting WDSLTools.Service instance.'
+
+ self.serviceName = service.name
+ for p in service.ports:
+ try:
+ ab = p.getAddressBinding()
+ except WSDLTools.WSDLError, ex:
+ self.logger.warning('Skip port(%s), missing address binding' %p.name)
+ continue
+ if isinstance(ab, WSDLTools.SoapAddressBinding) is False:
+ self.logger.warning('Skip port(%s), not a SOAP-1.1 address binding' %p.name)
+ continue
+
+ info = (p.getBinding().getPortType().name, p.getBinding().name, ab.location)
+ self.portInfo.append(info)
+
+ def getLocatorName(self):
+ '''return class name of generated locator.
+ '''
+ return self.locatorName
+
+ def getPortMethods(self):
+ '''list of get port accessor methods of generated locator class.
+ '''
+ return self.portMethods
+
+ def _setContent(self):
+ if not self.serviceName:
+ raise ContainerError, 'no service name defined!'
+
+ self.serviceName = self.mangle(self.serviceName)
+ self.locatorName = '%sLocator' %self.serviceName
+ locator = ['# Locator', 'class %s:' %self.locatorName, ]
+ self.portMethods = []
+ for p in self.portInfo:
+ ptName = NC_to_CN(p[0])
+ bName = NC_to_CN(p[1])
+ sAdd = p[2]
+ method = 'get%s' %ptName
+
+ pI = [
+ '%s%s_address = "%s"' % (ID1, ptName, sAdd),
+ '%sdef get%sAddress(self):' % (ID1, ptName),
+ '%sreturn %sLocator.%s_address' % (ID2,
+ self.serviceName,ptName),
+ '%sdef %s(self, url=None, **kw):' %(ID1, method),
+ '%sreturn %s%s(url or %sLocator.%s_address, **kw)' \
+ % (ID2, bName, self.clientClassSuffix, self.serviceName, ptName),
+ ]
+ self.portMethods.append(method)
+ locator += pI
+
+ self.writeArray(locator)
+
+
+class ServiceOperationContainer(ServiceContainerBase):
+ logger = _GetLogger("ServiceOperationContainer")
+
+ def __init__(self, useWSA=False, do_extended=False):
+ '''Parameters:
+ useWSA -- boolean, enable ws-addressing
+ do_extended -- boolean
+ '''
+ ServiceContainerBase.__init__(self)
+ self.useWSA = useWSA
+ self.do_extended = do_extended
+
+ def hasInput(self):
+ return self.inputName is not None
+
+ def hasOutput(self):
+ return self.outputName is not None
+
+ def isRPC(self):
+ return IsRPC(self.binding_operation)
+
+ def isLiteral(self, input=True):
+ msgrole = self.binding_operation.input
+ if input is False:
+ msgrole = self.binding_operation.output
+ return IsLiteral(msgrole)
+
+ def isSimpleType(self, input=True):
+ if input is False:
+ return self.outputSimpleType
+ return self.inputSimpleType
+
+ def getOperation(self):
+ return self.port.operations.get(self.name)
+
+ def getBOperation(self):
+ return self.port.get(self.name)
+
+ def getOperationName(self):
+ return self.name
+
+ def setUp(self, item):
+ '''
+ Parameters:
+ item -- WSDLTools BindingOperation instance.
+ '''
+ if not isinstance(item, WSDLTools.OperationBinding):
+ raise TypeError, 'Expecting WSDLTools Operation instance'
+
+ if not item.input:
+ raise WSDLFormatError('No <input/> in <binding name="%s"><operation name="%s">' %(
+ item.getBinding().name, item.name))
+
+ self.name = None
+ self.port = None
+ self.soapaction = None
+ self.inputName = None
+ self.outputName = None
+ self.inputSimpleType = None
+ self.outputSimpleType = None
+ self.inputAction = None
+ self.outputAction = None
+ self.port = port = item.getBinding().getPortType()
+ self._wsdl = item.getWSDL()
+ self.name = name = item.name
+ self.binding_operation = bop = item
+
+ op = port.operations.get(name)
+ if op is None:
+ raise WSDLFormatError(
+ '<portType name="%s"/> no match for <binding name="%s"><operation name="%s">' %(
+ port.name, item.getBinding().name, item.name))
+
+ soap_bop = bop.findBinding(WSDLTools.SoapOperationBinding)
+ if soap_bop is None:
+ raise SOAPBindingError, 'expecting SOAP Bindings'
+
+ self.soapaction = soap_bop.soapAction
+ sbody = bop.input.findBinding(WSDLTools.SoapBodyBinding)
+ if not sbody:
+ raise SOAPBindingError('Missing <binding name="%s"><operation name="%s"><input><soap:body>' %(
+ port.binding.name, bop.name))
+
+ self.encodingStyle = None
+ if sbody.use == 'encoded':
+ assert sbody.encodingStyle == SOAP.ENC,\
+ 'Supporting encodingStyle=%s, not %s'%(SOAP.ENC, sbody.encodingStyle)
+ self.encodingStyle = sbody.encodingStyle
+
+ self.inputName = op.getInputMessage().name
+ self.inputSimpleType = \
+ FromMessageGetSimpleElementDeclaration(op.getInputMessage())
+ self.inputAction = op.getInputAction()
+
+ if bop.output is not None:
+ sbody = bop.output.findBinding(WSDLTools.SoapBodyBinding)
+ if not item.output:
+ raise WSDLFormatError, "Operation %s, no match for output binding" %name
+
+ self.outputName = op.getOutputMessage().name
+ self.outputSimpleType = \
+ FromMessageGetSimpleElementDeclaration(op.getOutputMessage())
+ self.outputAction = op.getOutputAction()
+
+ def _setContent(self):
+ '''create string representation of operation.
+ '''
+ kwstring = 'kw = {}'
+ tCheck = 'if isinstance(request, %s) is False:' % self.inputName
+ bindArgs = ''
+ if self.encodingStyle is not None:
+ bindArgs = 'encodingStyle="%s", ' %self.encodingStyle
+
+ if self.useWSA:
+ wsactionIn = 'wsaction = "%s"' % self.inputAction
+ wsactionOut = 'wsaction = "%s"' % self.outputAction
+ bindArgs += 'wsaction=wsaction, endPointReference=self.endPointReference, '
+ responseArgs = ', wsaction=wsaction'
+ else:
+ wsactionIn = '# no input wsaction'
+ wsactionOut = '# no output wsaction'
+ responseArgs = ''
+
+ bindArgs += '**kw)'
+
+ if self.do_extended:
+ inputName = self.getOperation().getInputMessage().name
+ wrap_str = ""
+ partsList = self.getOperation().getInputMessage().parts.values()
+ try:
+ subNames = GetPartsSubNames(partsList, self._wsdl)
+ except TypeError, ex:
+ raise Wsdl2PythonError,\
+ "Extended generation failure: only supports doc/lit, "\
+ +"and all element attributes (<message><part element="\
+ +"\"my:GED\"></message>) must refer to single global "\
+ +"element declaration with complexType content. "\
+ +"\n\n**** TRY WITHOUT EXTENDED ****\n"
+
+ args = []
+ for pa in subNames:
+ args += pa
+
+ for arg in args:
+ wrap_str += "%srequest.%s = %s\n" % (ID2,
+ self.getAttributeName(arg),
+ self.mangle(arg))
+
+ #args = [pa.name for pa in self.getOperation().getInputMessage().parts.values()]
+ argsStr = ",".join(args)
+ if len(argsStr) > 1: # add inital comma if args exist
+ argsStr = ", " + argsStr
+
+ method = [
+ '%s# op: %s' % (ID1, self.getOperation().getInputMessage()),
+ '%sdef %s(self%s):' % (ID1, self.name, argsStr),
+ '\n%srequest = %s()' % (ID2, self.inputName),
+ '%s' % (wrap_str),
+ '%s%s' % (ID2, kwstring),
+ '%s%s' % (ID2, wsactionIn),
+ '%sself.binding.Send(None, None, request, soapaction="%s", %s'\
+ %(ID2, self.soapaction, bindArgs),
+ ]
+ else:
+ method = [
+ '%s# op: %s' % (ID1, self.name),
+ '%sdef %s(self, request):' % (ID1, self.name),
+ '%s%s' % (ID2, tCheck),
+ '%sraise TypeError, "%%s incorrect request type" %% (%s)' %(ID3, 'request.__class__'),
+ '%s%s' % (ID2, kwstring),
+ '%s%s' % (ID2, wsactionIn),
+ '%sself.binding.Send(None, None, request, soapaction="%s", %s'\
+ %(ID2, self.soapaction, bindArgs),
+ ]
+
+ #
+ # BP 1.0: rpc/literal
+ # WSDL 1.1 Section 3.5 could be interpreted to mean the RPC response
+ # wrapper element must be named identical to the name of the
+ # wsdl:operation.
+ # R2729
+
+ #
+ # SOAP-1.1 Note: rpc/encoded
+ # Each parameter accessor has a name corresponding to the name of the
+ # parameter and type corresponding to the type of the parameter. The name of
+ # the return value accessor is not significant. Likewise, the name of the struct is
+ # not significant. However, a convention is to name it after the method name
+ # with the string "Response" appended.
+ #
+ if self.outputName:
+ response = ['%s%s' % (ID2, wsactionOut),]
+ if self.isRPC() and not self.isLiteral():
+ # rpc/encoded Replace wrapper name with None
+ response.append(\
+ '%stypecode = Struct(pname=None, ofwhat=%s.typecode.ofwhat, pyclass=%s.typecode.pyclass)' %(
+ ID2, self.outputName, self.outputName)
+ )
+ response.append(\
+ '%sresponse = self.binding.Receive(typecode%s)' %(
+ ID2, responseArgs)
+ )
+ else:
+ response.append(\
+ '%sresponse = self.binding.Receive(%s.typecode%s)' %(
+ ID2, self.outputName, responseArgs)
+ )
+
+ if self.outputSimpleType:
+ response.append('%sreturn %s(response)' %(ID2, self.outputName))
+ else:
+ if self.do_extended:
+ partsList = self.getOperation().getOutputMessage().parts.values()
+ subNames = GetPartsSubNames(partsList, self._wsdl)
+ args = []
+ for pa in subNames:
+ args += pa
+
+ for arg in args:
+ response.append('%s%s = response.%s' % (ID2, self.mangle(arg), self.getAttributeName(arg)) )
+ margs = ",".join(args)
+ response.append("%sreturn %s" % (ID2, margs) )
+ else:
+ response.append('%sreturn response' %ID2)
+ method += response
+
+ self.writeArray(method)
+
+
+class ServiceOperationsClassContainer(ServiceContainerBase):
+ '''
+ class variables:
+ readerclass --
+ writerclass --
+ operationclass -- representation of each operation.
+ '''
+ readerclass = None
+ writerclass = None
+ operationclass = ServiceOperationContainer
+ logger = _GetLogger("ServiceOperationsClassContainer")
+
+ def __init__(self, useWSA=False, do_extended=False, wsdl=None):
+ '''Parameters:
+ name -- binding name
+ property -- resource properties
+ useWSA -- boolean, enable ws-addressing
+ name -- binding name
+ '''
+ ServiceContainerBase.__init__(self)
+ self.useWSA = useWSA
+ self.rProp = None
+ self.bName = None
+ self.operations = None
+ self.do_extended = do_extended
+ self._wsdl = wsdl # None unless do_extended == True
+
+ def setReaderClass(cls, className):
+ '''specify a reader class name, this must be imported
+ in service module.
+ '''
+ cls.readerclass = className
+ setReaderClass = classmethod(setReaderClass)
+
+ def setWriterClass(cls, className):
+ '''specify a writer class name, this must be imported
+ in service module.
+ '''
+ cls.writerclass = className
+ setWriterClass = classmethod(setWriterClass)
+
+ def setOperationClass(cls, className):
+ '''specify an operation container class name.
+ '''
+ cls.operationclass = className
+ setOperationClass = classmethod(setOperationClass)
+
+ def setUp(self, port):
+ '''This method finds all SOAP Binding Operations, it will skip
+ all bindings that are not SOAP.
+ port -- WSDL.Port instance
+ '''
+ assert isinstance(port, WSDLTools.Port), 'expecting WSDLTools Port instance'
+
+ self.operations = []
+ self.bName = port.getBinding().name
+ self.rProp = port.getBinding().getPortType().getResourceProperties()
+ soap_binding = port.getBinding().findBinding(WSDLTools.SoapBinding)
+ if soap_binding is None:
+ raise Wsdl2PythonError,\
+ 'port(%s) missing WSDLTools.SoapBinding' %port.name
+
+ for bop in port.getBinding().operations:
+ soap_bop = bop.findBinding(WSDLTools.SoapOperationBinding)
+ if soap_bop is None:
+ self.logger.warning(\
+ 'Skip port(%s) operation(%s) no SOAP Binding Operation'\
+ %(port.name, bop.name),
+ )
+ continue
+
+ #soapAction = soap_bop.soapAction
+ if bop.input is not None:
+ soapBodyBind = bop.input.findBinding(WSDLTools.SoapBodyBinding)
+ if soapBodyBind is None:
+ self.logger.warning(\
+ 'Skip port(%s) operation(%s) Bindings(%s) not supported'\
+ %(port.name, bop.name, bop.extensions)
+ )
+ continue
+
+ op = port.getBinding().getPortType().operations.get(bop.name)
+ if op is None:
+ raise Wsdl2PythonError,\
+ 'no matching portType/Binding operation(%s)' % bop.name
+
+ c = self.operationclass(useWSA=self.useWSA,
+ do_extended=self.do_extended)
+ c.setUp(bop)
+ self.operations.append(c)
+
+ def _setContent(self):
+ if self.useWSA is True:
+ ctorArgs = 'endPointReference=None, **kw'
+ epr = 'self.endPointReference = endPointReference'
+ else:
+ ctorArgs = '**kw'
+ epr = '# no ws-addressing'
+
+ if self.rProp:
+ rprop = 'kw.setdefault("ResourceProperties", ("%s","%s"))'\
+ %(self.rProp[0], self.rProp[1])
+ else:
+ rprop = '# no resource properties'
+
+ methods = [
+ '# Methods',
+ 'class %s%s:' % (NC_to_CN(self.bName), self.clientClassSuffix),
+ '%sdef __init__(self, url, %s):' % (ID1, ctorArgs),
+ '%skw.setdefault("readerclass", %s)' % (ID2, self.readerclass),
+ '%skw.setdefault("writerclass", %s)' % (ID2, self.writerclass),
+ '%s%s' % (ID2, rprop),
+ '%sself.binding = client.Binding(url=url, **kw)' %ID2,
+ '%s%s' % (ID2,epr),
+ ]
+
+ for op in self.operations:
+ methods += [ op.getvalue() ]
+
+ self.writeArray(methods)
+
+
+
+class MessageContainerInterface:
+ logger = _GetLogger("MessageContainerInterface")
+
+ def setUp(self, port, soc, input):
+ '''sets the attribute _simple which represents a
+ primitive type message represents, or None if not primitive.
+
+ soc -- WSDLTools.ServiceOperationContainer instance
+ port -- WSDLTools.Port instance
+ input-- boolean, input messasge or output message of operation.
+ '''
+ raise NotImplementedError, 'Message container must implemented setUp.'
+
+
+class ServiceDocumentLiteralMessageContainer(ServiceContainerBase, MessageContainerInterface):
+ logger = _GetLogger("ServiceDocumentLiteralMessageContainer")
+
+ def __init__(self, do_extended=False):
+
+ ServiceContainerBase.__init__(self)
+ self.do_extended=do_extended
+
+ def setUp(self, port, soc, input):
+ content = self.content
+ # TODO: check soapbody for part name
+ simple = self._simple = soc.isSimpleType(soc.getOperationName())
+ name = soc.getOperationName()
+
+ # Document/literal
+ operation = port.getBinding().getPortType().operations.get(name)
+ bop = port.getBinding().operations.get(name)
+ soapBodyBind = None
+ if input is True:
+ soapBodyBind = bop.input.findBinding(WSDLTools.SoapBodyBinding)
+ message = operation.getInputMessage()
+ else:
+ soapBodyBind = bop.output.findBinding(WSDLTools.SoapBodyBinding)
+ message = operation.getOutputMessage()
+
+ # using underlying data structure to avoid phantom problem.
+# parts = message.parts.data.values()
+# if len(parts) > 1:
+# raise Wsdl2PythonError, 'not suporting multi part doc/lit msgs'
+ if len(message.parts) == 0:
+ raise Wsdl2PythonError, 'must specify part for doc/lit msg'
+
+ p = None
+ if soapBodyBind.parts is not None:
+ if len(soapBodyBind.parts) > 1:
+ raise Wsdl2PythonError,\
+ 'not supporting multiple parts in soap body'
+ if len(soapBodyBind.parts) == 0:
+ return
+
+ p = message.parts.get(soapBodyBind.parts[0])
+
+ # XXX: Allow for some slop
+ p = p or message.parts[0]
+
+ if p.type:
+ raise Wsdl2PythonError, 'no doc/lit suport for <part type>'
+
+ if not p.element:
+ return
+
+ content.ns = p.element[0]
+ content.pName = p.element[1]
+ content.mName = message.name
+
+ def _setContent(self):
+ '''create string representation of doc/lit message container. If
+ message element is simple(primitive), use python type as base class.
+ '''
+ try:
+ simple = self._simple
+ except AttributeError:
+ raise RuntimeError, 'call setUp first'
+
+ # TODO: Hidden contract. Must set self.ns before getNSAlias...
+ # File "/usr/local/python/lib/python2.4/site-packages/ZSI/generate/containers.py", line 625, in _setContent
+ # kw['message'],kw['prefix'],kw['typecode'] = \
+ # File "/usr/local/python/lib/python2.4/site-packages/ZSI/generate/containers.py", line 128, in getNSAlias
+ # raise ContainerError, 'no self.ns attr defined in %s' % self.__class__
+ # ZSI.generate.containers.ContainerError: no self.ns attr defined in ZSI.generate.containers.ServiceDocumentLiteralMessageContainer
+ #
+ self.ns = self.content.ns
+
+
+ kw = KW.copy()
+ kw['message'],kw['prefix'],kw['typecode'] = \
+ self.content.mName, self.getNSAlias(), element_class_name(self.content.pName)
+
+ # These messsages are just global element declarations
+ self.writeArray(['%(message)s = %(prefix)s.%(typecode)s().pyclass' %kw])
+
+
+class ServiceRPCEncodedMessageContainer(ServiceContainerBase, MessageContainerInterface):
+ logger = _GetLogger("ServiceRPCEncodedMessageContainer")
+
+ def setUp(self, port, soc, input):
+ '''
+ Instance Data:
+ op -- WSDLTools Operation instance
+ bop -- WSDLTools BindingOperation instance
+ input -- boolean input/output
+ '''
+ name = soc.getOperationName()
+ bop = port.getBinding().operations.get(name)
+ op = port.getBinding().getPortType().operations.get(name)
+
+ assert op is not None, 'port has no operation %s' %name
+ assert bop is not None, 'port has no binding operation %s' %name
+
+ self.input = input
+ self.op = op
+ self.bop = bop
+
+ def _setContent(self):
+ try:
+ self.op
+ except AttributeError:
+ raise RuntimeError, 'call setUp first'
+
+ pname = self.op.name
+ msgRole = self.op.input
+ msgRoleB = self.bop.input
+ if self.input is False:
+ pname = '%sResponse' %self.op.name
+ msgRole = self.op.output
+ msgRoleB = self.bop.output
+
+ sbody = msgRoleB.findBinding(WSDLTools.SoapBodyBinding)
+ if not sbody or not sbody.namespace:
+ raise WSInteropError, WSISpec.R2717
+
+ assert sbody.use == 'encoded', 'Expecting use=="encoded"'
+ encodingStyle = sbody.encodingStyle
+
+ assert encodingStyle == SOAP.ENC,\
+ 'Supporting encodingStyle=%s, not %s' %(SOAP.ENC, encodingStyle)
+
+ namespace = sbody.namespace
+ tcb = MessageTypecodeContainer(\
+ tuple(msgRole.getMessage().parts.list),
+ )
+ ofwhat = '[%s]' %tcb.getTypecodeList()
+ pyclass = msgRole.getMessage().name
+
+ fdict = KW.copy()
+ fdict['nspname'] = sbody.namespace
+ fdict['pname'] = pname
+ fdict['pyclass'] = None
+ fdict['ofwhat'] = ofwhat
+ fdict['encoded'] = namespace
+
+ #if self.input is False:
+ # fdict['typecode'] = \
+ # 'Struct(pname=None, ofwhat=%(ofwhat)s, pyclass=%(pyclass)s, encoded="%(encoded)s")'
+ #else:
+ fdict['typecode'] = \
+ 'Struct(pname=("%(nspname)s","%(pname)s"), ofwhat=%(ofwhat)s, pyclass=%(pyclass)s, encoded="%(encoded)s")'
+
+ message = ['class %(pyclass)s:',
+ '%(ID1)sdef __init__(self):']
+
+ for aname in tcb.getAttributeNames():
+ message.append('%(ID2)sself.' + aname +' = None')
+ message.append('%(ID2)sreturn')
+
+ # TODO: This isn't a TypecodeContainerBase instance but it
+ # certaintly generates a pyclass and typecode.
+ #if self.metaclass is None:
+ if TypecodeContainerBase.metaclass is None:
+ fdict['pyclass'] = pyclass
+ fdict['typecode'] = fdict['typecode'] %fdict
+ message.append('%(pyclass)s.typecode = %(typecode)s')
+ else:
+ # Need typecode to be available when class is constructed.
+ fdict['typecode'] = fdict['typecode'] %fdict
+ fdict['pyclass'] = pyclass
+ fdict['metaclass'] = TypecodeContainerBase.metaclass
+ message.insert(0, '_%(pyclass)sTypecode = %(typecode)s')
+ message.insert(2, '%(ID1)stypecode = _%(pyclass)sTypecode')
+ message.insert(3, '%(ID1)s__metaclass__ = %(metaclass)s')
+ message.append('%(pyclass)s.typecode.pyclass = %(pyclass)s')
+
+ self.writeArray(map(lambda l: l %fdict, message))
+
+
+class ServiceRPCLiteralMessageContainer(ServiceContainerBase, MessageContainerInterface):
+ logger = _GetLogger("ServiceRPCLiteralMessageContainer")
+
+ def setUp(self, port, soc, input):
+ '''
+ Instance Data:
+ op -- WSDLTools Operation instance
+ bop -- WSDLTools BindingOperation instance
+ input -- boolean input/output
+ '''
+ name = soc.getOperationName()
+ bop = port.getBinding().operations.get(name)
+ op = port.getBinding().getPortType().operations.get(name)
+
+ assert op is not None, 'port has no operation %s' %name
+ assert bop is not None, 'port has no binding operation %s' %name
+
+ self.op = op
+ self.bop = bop
+ self.input = input
+
+ def _setContent(self):
+ try:
+ self.op
+ except AttributeError:
+ raise RuntimeError, 'call setUp first'
+
+ operation = self.op
+ input = self.input
+ pname = operation.name
+ msgRole = operation.input
+ msgRoleB = self.bop.input
+ if input is False:
+ pname = '%sResponse' %operation.name
+ msgRole = operation.output
+ msgRoleB = self.bop.output
+
+ sbody = msgRoleB.findBinding(WSDLTools.SoapBodyBinding)
+ if not sbody or not sbody.namespace:
+ raise WSInteropError, WSISpec.R2717
+
+ namespace = sbody.namespace
+ tcb = MessageTypecodeContainer(\
+ tuple(msgRole.getMessage().parts.list),
+ )
+ ofwhat = '[%s]' %tcb.getTypecodeList()
+ pyclass = msgRole.getMessage().name
+
+ fdict = KW.copy()
+ fdict['nspname'] = sbody.namespace
+ fdict['pname'] = pname
+ fdict['pyclass'] = None
+ fdict['ofwhat'] = ofwhat
+ fdict['encoded'] = namespace
+ fdict['typecode'] = \
+ 'Struct(pname=("%(nspname)s","%(pname)s"), ofwhat=%(ofwhat)s, pyclass=%(pyclass)s, encoded="%(encoded)s")'
+
+ message = ['class %(pyclass)s:',
+ '%(ID1)sdef __init__(self):']
+
+ for aname in tcb.getAttributeNames():
+ message.append('%(ID2)sself.' + aname +' = None')
+ message.append('%(ID2)sreturn')
+
+ # TODO: This isn't a TypecodeContainerBase instance but it
+ # certaintly generates a pyclass and typecode.
+ #if self.metaclass is None:
+ if TypecodeContainerBase.metaclass is None:
+ fdict['pyclass'] = pyclass
+ fdict['typecode'] = fdict['typecode'] %fdict
+ message.append('%(pyclass)s.typecode = %(typecode)s')
+ else:
+ # Need typecode to be available when class is constructed.
+ fdict['typecode'] = fdict['typecode'] %fdict
+ fdict['pyclass'] = pyclass
+ fdict['metaclass'] = TypecodeContainerBase.metaclass
+ message.insert(0, '_%(pyclass)sTypecode = %(typecode)s')
+ message.insert(2, '%(ID1)stypecode = _%(pyclass)sTypecode')
+ message.insert(3, '%(ID1)s__metaclass__ = %(metaclass)s')
+ message.append('%(pyclass)s.typecode.pyclass = %(pyclass)s')
+
+ self.writeArray(map(lambda l: l %fdict, message))
+
+
+TypesContainerBase = ContainerBase
+
+
+class TypesHeaderContainer(TypesContainerBase):
+ '''imports for all generated types modules.
+ '''
+ imports = [
+ 'import ZSI',
+ 'import ZSI.TCcompound',
+ 'from ZSI.schema import LocalElementDeclaration, ElementDeclaration, TypeDefinition, GTD, GED',
+ ]
+ logger = _GetLogger("TypesHeaderContainer")
+
+ def _setContent(self):
+ self.writeArray(TypesHeaderContainer.imports)
+
+
+NamespaceClassContainerBase = TypesContainerBase
+
+
+class NamespaceClassHeaderContainer(NamespaceClassContainerBase):
+ logger = _GetLogger("NamespaceClassHeaderContainer")
+
+ def _setContent(self):
+
+ head = [
+ '#' * 30,
+ '# targetNamespace',
+ '# %s' % self.ns,
+ '#' * 30 + '\n',
+ 'class %s:' % self.getNSAlias(),
+ '%stargetNamespace = "%s"' % (ID1, self.ns)
+ ]
+
+ self.writeArray(head)
+
+class NamespaceClassFooterContainer(NamespaceClassContainerBase):
+ logger = _GetLogger("NamespaceClassFooterContainer")
+
+ def _setContent(self):
+
+ foot = [
+ '# end class %s (tns: %s)' % (self.getNSAlias(), self.ns),
+ ]
+
+ self.writeArray(foot)
+
+
+BTI = BaseTypeInterpreter()
+class TypecodeContainerBase(TypesContainerBase):
+ '''Base class for all classes representing anything
+ with element content.
+
+ class variables:
+ mixed_content_aname -- text content will be placed in this attribute.
+ attributes_aname -- attributes will be placed in this attribute.
+ metaclass -- set this attribute to specify a pyclass __metaclass__
+ '''
+ mixed_content_aname = 'text'
+ attributes_aname = 'attrs'
+ metaclass = None
+ lazy = False
+ logger = _GetLogger("TypecodeContainerBase")
+
+ def __init__(self, do_extended=False, extPyClasses=None):
+ TypesContainerBase.__init__(self)
+ self.name = None
+
+ # attrs for model groups and others with elements, tclists, etc...
+ self.allOptional = False
+ self.mgContent = None
+ self.contentFlattened = False
+ self.elementAttrs = []
+ self.tcListElements = []
+ self.tcListSet = False
+
+ self.localTypes = []
+
+ # used when processing nested anonymous types
+ self.parentClass = None
+
+ # used when processing attribute content
+ self.mixed = False
+ self.extraFlags = ''
+ self.attrComponents = None
+
+ # --> EXTENDED
+ # Used if an external pyclass was specified for this type.
+ self.do_extended = do_extended
+ if extPyClasses is None:
+ self.extPyClasses = {}
+ else:
+ self.extPyClasses = extPyClasses
+ # <--
+
+ def getvalue(self):
+ out = ContainerBase.getvalue(self)
+ for item in self.localTypes:
+ content = None
+ assert True is item.isElement() is item.isLocal(), 'expecting local elements only'
+
+ etp = item.content
+ qName = item.getAttribute('type')
+ if not qName:
+ etp = item.content
+ local = True
+ else:
+ etp = item.getTypeDefinition('type')
+
+ if etp is None:
+ if local is True:
+ content = ElementLocalComplexTypeContainer(do_extended=self.do_extended)
+ else:
+ content = ElementSimpleTypeContainer()
+ elif etp.isLocal() is False:
+ content = ElementGlobalDefContainer()
+ elif etp.isSimple() is True:
+ content = ElementLocalSimpleTypeContainer()
+ elif etp.isComplex():
+ content = ElementLocalComplexTypeContainer(do_extended=self.do_extended)
+ else:
+ raise Wsdl2PythonError, "Unknown element declaration: %s" %item.getItemTrace()
+
+ content.setUp(item)
+
+ out += '\n\n'
+ if self.parentClass:
+ content.parentClass = \
+ '%s.%s' %(self.parentClass, self.getClassName())
+ else:
+ content.parentClass = '%s.%s' %(self.getNSAlias(), self.getClassName())
+
+ for l in content.getvalue().split('\n'):
+ if l: out += '%s%s\n' % (ID1, l)
+ else: out += '\n'
+
+ out += '\n\n'
+
+ return out
+
+ def getAttributeName(self, name):
+ '''represents the aname
+ '''
+ if self.func_aname is None:
+ return name
+ assert callable(self.func_aname), \
+ 'expecting callable method for attribute func_aname, not %s' %type(self.func_aname)
+ f = self.func_aname
+ return f(name)
+
+ def getMixedTextAName(self):
+ '''returns an aname representing mixed text content.
+ '''
+ return self.getAttributeName(self.mixed_content_aname)
+
+ def getClassName(self):
+
+ if not self.name:
+ raise ContainerError, 'self.name not defined!'
+ if not hasattr(self.__class__, 'type'):
+ raise ContainerError, 'container type not defined!'
+
+ #suffix = self.__class__.type
+ if self.__class__.type == DEF:
+ classname = type_class_name(self.name)
+ elif self.__class__.type == DEC:
+ classname = element_class_name(self.name)
+
+ return self.mangle( classname )
+
+ # --> EXTENDED
+ def hasExtPyClass(self):
+ if self.name in self.extPyClasses:
+ return True
+ else:
+ return False
+ # <--
+
+ def getPyClass(self):
+ '''Name of generated inner class that will be specified as pyclass.
+ '''
+ # --> EXTENDED
+ if self.hasExtPyClass():
+ classInfo = self.extPyClasses[self.name]
+ return ".".join(classInfo)
+ # <--
+
+ return 'Holder'
+
+ def getPyClassDefinition(self):
+ '''Return a list containing pyclass definition.
+ '''
+ kw = KW.copy()
+
+ # --> EXTENDED
+ if self.hasExtPyClass():
+ classInfo = self.extPyClasses[self.name]
+ kw['classInfo'] = classInfo[0]
+ return ["%(ID3)simport %(classInfo)s" %kw ]
+ # <--
+
+ kw['pyclass'] = self.getPyClass()
+ definition = []
+ definition.append('%(ID3)sclass %(pyclass)s:' %kw)
+ if self.metaclass is not None:
+ kw['type'] = self.metaclass
+ definition.append('%(ID4)s__metaclass__ = %(type)s' %kw)
+ definition.append('%(ID4)stypecode = self' %kw)
+
+ #TODO: Remove pyclass holder __init__ -->
+ definition.append('%(ID4)sdef __init__(self):' %kw)
+ definition.append('%(ID5)s# pyclass' %kw)
+
+ # JRB HACK need to call _setElements via getElements
+ self._setUpElements()
+
+ # JRB HACK need to indent additional one level
+ for el in self.elementAttrs:
+ kw['element'] = el
+ definition.append('%(ID2)s%(element)s' %kw)
+ definition.append('%(ID5)sreturn' %kw)
+ # <--
+
+ # pyclass descriptive name
+ if self.name is not None:
+ kw['name'] = self.name
+ definition.append(\
+ '%(ID3)s%(pyclass)s.__name__ = "%(name)s_Holder"' %kw
+ )
+
+ return definition
+
+ def nsuriLogic(self):
+ '''set a variable "ns" that represents the targetNamespace in
+ which this item is defined. Used for namespacing local elements.
+ '''
+ if self.parentClass:
+ return 'ns = %s.%s.schema' %(self.parentClass, self.getClassName())
+ return 'ns = %s.%s.schema' %(self.getNSAlias(), self.getClassName())
+
+ def schemaTag(self):
+ if self.ns is not None:
+ return 'schema = "%s"' % self.ns
+ raise ContainerError, 'failed to set schema targetNamespace(%s)' %(self.__class__)
+
+ def typeTag(self):
+ if self.name is not None:
+ return 'type = (schema, "%s")' % self.name
+ raise ContainerError, 'failed to set type name(%s)' %(self.__class__)
+
+ def literalTag(self):
+ if self.name is not None:
+ return 'literal = "%s"' % self.name
+ raise ContainerError, 'failed to set element name(%s)' %(self.__class__)
+
+ def getExtraFlags(self):
+ if self.mixed:
+ self.extraFlags += 'mixed=True, mixed_aname="%s", ' %self.getMixedTextAName()
+
+ return self.extraFlags
+
+ def simpleConstructor(self, superclass=None):
+
+ if superclass:
+ return '%s.__init__(self, **kw)' % superclass
+ else:
+ return 'def __init__(self, **kw):'
+
+ def pnameConstructor(self, superclass=None):
+
+ if superclass:
+ return '%s.__init__(self, pname, **kw)' % superclass
+ else:
+ return 'def __init__(self, pname, **kw):'
+
+
+ def _setUpElements(self):
+ """TODO: Remove this method
+
+ This method ONLY sets up the instance attributes.
+ Dependency instance attribute:
+ mgContent -- expected to be either a complex definition
+ with model group content, a model group, or model group
+ content. TODO: should only support the first two.
+ """
+ self.logger.debug("_setUpElements: %s" %self._item.getItemTrace())
+ if hasattr(self, '_done'):
+ #return '\n'.join(self.elementAttrs)
+ return
+
+ self._done = True
+ flat = []
+ content = self.mgContent
+ if type(self.mgContent) is not tuple:
+ mg = self.mgContent
+ if not mg.isModelGroup():
+ mg = mg.content
+
+ content = mg.content
+ if mg.isAll():
+ flat = content
+ content = []
+ elif mg.isModelGroup() and mg.isDefinition():
+ mg = mg.content
+ content = mg.content
+
+ idx = 0
+ content = list(content)
+ while idx < len(content):
+ c = orig = content[idx]
+ if c.isElement():
+ flat.append(c)
+ idx += 1
+ continue
+
+ if c.isReference() and c.isModelGroup():
+ c = c.getModelGroupReference()
+
+ if c.isDefinition() and c.isModelGroup():
+ c = c.content
+
+ if c.isSequence() or c.isChoice():
+ begIdx = idx
+ endIdx = begIdx + len(c.content)
+ for i in range(begIdx, endIdx):
+ content.insert(i, c.content[i-begIdx])
+
+ content.remove(orig)
+ continue
+
+ raise ContainerError, 'unexpected schema item: %s' %c.getItemTrace()
+
+ for c in flat:
+ if c.isDeclaration() and c.isElement():
+ defaultValue = "None"
+ parent = c
+ defs = []
+ # stop recursion via global ModelGroupDefinition
+ while defs.count(parent) <= 1:
+ maxOccurs = parent.getAttribute('maxOccurs')
+ if maxOccurs == 'unbounded' or int(maxOccurs) > 1:
+ defaultValue = "[]"
+ break
+
+ parent = parent._parent()
+ if not parent.isModelGroup():
+ break
+
+ if parent.isReference():
+ parent = parent.getModelGroupReference()
+
+ if parent.isDefinition():
+ parent = parent.content
+ defs.append(parent)
+
+ if None == c.getAttribute('name') and c.isWildCard():
+ e = '%sself.%s = %s' %(ID3,
+ self.getAttributeName('any'), defaultValue)
+ else:
+ e = '%sself.%s = %s' %(ID3,
+ self.getAttributeName(c.getAttribute('name')), defaultValue)
+ self.elementAttrs.append(e)
+ continue
+
+ # TODO: This seems wrong
+ if c.isReference():
+ e = '%sself._%s = None' %(ID3,
+ self.mangle(c.getAttribute('ref')[1]))
+ self.elementAttrs.append(e)
+ continue
+
+ raise ContainerError, 'unexpected item: %s' % c.getItemTrace()
+
+ #return '\n'.join(self.elementAttrs)
+ return
+
+ def _setTypecodeList(self):
+ """generates ofwhat content, minOccurs/maxOccurs facet generation.
+ Dependency instance attribute:
+ mgContent -- expected to be either a complex definition
+ with model group content, a model group, or model group
+ content. TODO: should only support the first two.
+ localTypes -- produce local class definitions later
+ tcListElements -- elements, local/global
+ """
+ self.logger.debug("_setTypecodeList(%r): %s" %
+ (self.mgContent, self._item.getItemTrace()))
+
+ flat = []
+ content = self.mgContent
+
+ #TODO: too much slop permitted here, impossible
+ # to tell what is going on.
+
+ if type(content) is not tuple:
+ mg = content
+ if not mg.isModelGroup():
+ raise Wsdl2PythonErr("Expecting ModelGroup: %s" %
+ mg.getItemTrace())
+
+ self.logger.debug("ModelGroup(%r) contents(%r): %s" %
+ (mg, mg.content, mg.getItemTrace()))
+
+ #<group ref>
+ if mg.isReference():
+ raise RuntimeError("Unexpected modelGroup reference: %s" %
+ mg.getItemTrace())
+
+ #<group name>
+ if mg.isDefinition():
+ mg = mg.content
+
+ if mg.isAll():
+ flat = mg.content
+ content = []
+ elif mg.isSequence():
+ content = mg.content
+ elif mg.isChoice():
+ content = mg.content
+ else:
+ raise RuntimeError("Unknown schema item")
+
+ idx = 0
+ content = list(content)
+ self.logger.debug("content: %r" %content)
+ while idx < len(content):
+ c = orig = content[idx]
+ if c.isElement():
+ flat.append(c)
+ idx += 1
+ continue
+
+ if c.isReference() and c.isModelGroup():
+ c = c.getModelGroupReference()
+
+ if c.isDefinition() and c.isModelGroup():
+ c = c.content
+
+ if c.isSequence() or c.isChoice():
+ begIdx = idx
+ endIdx = begIdx + len(c.content)
+ for i in range(begIdx, endIdx):
+ content.insert(i, c.content[i-begIdx])
+
+ content.remove(orig)
+ continue
+
+ raise ContainerError, 'unexpected schema item: %s' %c.getItemTrace()
+
+ # TODO: Need to store "parents" in a dict[id] = list(),
+ # because cannot follow references, but not currently
+ # a big concern.
+
+ self.logger.debug("flat: %r" %list(flat))
+ for c in flat:
+ tc = TcListComponentContainer()
+ # TODO: Remove _getOccurs
+ min,max,nil = self._getOccurs(c)
+ min = max = None
+ maxOccurs = 1
+
+ parent = c
+ defs = []
+ # stop recursion via global ModelGroupDefinition
+ while defs.count(parent) <= 1:
+ max = parent.getAttribute('maxOccurs')
+ if max == 'unbounded':
+ maxOccurs = '"%s"' %max
+ break
+
+ maxOccurs = int(max) * maxOccurs
+ parent = parent._parent()
+ if not parent.isModelGroup():
+ break
+
+ if parent.isReference():
+ parent = parent.getModelGroupReference()
+
+ if parent.isDefinition():
+ parent = parent.content
+ defs.append(parent)
+
+ del defs
+ parent = c
+ while 1:
+ minOccurs = int(parent.getAttribute('minOccurs'))
+ if minOccurs == 0 or parent.isChoice():
+ minOccurs = 0
+ break
+
+ parent = parent._parent()
+ if not parent.isModelGroup():
+ minOccurs = int(c.getAttribute('minOccurs'))
+ break
+
+ if parent.isReference():
+ parent = parent.getModelGroupReference()
+
+ if parent.isDefinition():
+ parent = parent.content
+
+ tc.setOccurs(minOccurs, maxOccurs, nil)
+ processContents = self._getProcessContents(c)
+ tc.setProcessContents(processContents)
+ if c.isDeclaration() and c.isElement():
+ global_type = c.getAttribute('type')
+ content = getattr(c, 'content', None)
+ if c.isLocal() and c.isQualified() is False:
+ tc.unQualified()
+
+ if c.isWildCard():
+ tc.setStyleAnyElement()
+ elif global_type is not None:
+ tc.name = c.getAttribute('name')
+ ns = global_type[0]
+ if ns in SCHEMA.XSD_LIST:
+ tpc = BTI.get_typeclass(global_type[1],global_type[0])
+ tc.klass = tpc
+# elif (self.ns,self.name) == global_type:
+# # elif self._isRecursiveElement(c)
+# # TODO: Remove this, it only works for 1 level.
+# tc.setStyleRecursion()
+ else:
+ tc.setGlobalType(*global_type)
+# tc.klass = '%s.%s' % (NAD.getAlias(ns),
+# type_class_name(global_type[1]))
+ del ns
+ elif content is not None and content.isLocal() and content.isComplex():
+ tc.name = c.getAttribute('name')
+ tc.klass = 'self.__class__.%s' % (element_class_name(tc.name))
+ #TODO: Not an element reference, confusing nomenclature
+ tc.setStyleElementReference()
+ self.localTypes.append(c)
+ elif content is not None and content.isLocal() and content.isSimple():
+ # Local Simple Type
+ tc.name = c.getAttribute('name')
+ tc.klass = 'self.__class__.%s' % (element_class_name(tc.name))
+ #TODO: Not an element reference, confusing nomenclature
+ tc.setStyleElementReference()
+ self.localTypes.append(c)
+ else:
+ raise ContainerError, 'unexpected item: %s' % c.getItemTrace()
+
+ elif c.isReference():
+ # element references
+ ref = c.getAttribute('ref')
+# tc.klass = '%s.%s' % (NAD.getAlias(ref[0]),
+# element_class_name(ref[1]) )
+ tc.setStyleElementReference()
+ tc.setGlobalType(*ref)
+ else:
+ raise ContainerError, 'unexpected item: %s' % c.getItemTrace()
+
+ self.tcListElements.append(tc)
+
+ def getTypecodeList(self):
+ if not self.tcListSet:
+# self._flattenContent()
+ self._setTypecodeList()
+ self.tcListSet = True
+
+ list = []
+ for e in self.tcListElements:
+ list.append(str(e))
+
+ return ', '.join(list)
+
+ # the following _methods() are utility methods used during
+ # TCList generation, et al.
+
+ def _getOccurs(self, e):
+
+ nillable = e.getAttribute('nillable')
+ if nillable == 'true':
+ nillable = True
+ else:
+ nillable = False
+
+ maxOccurs = e.getAttribute('maxOccurs')
+ if maxOccurs == 'unbounded':
+ maxOccurs = '"%s"' %maxOccurs
+
+ minOccurs = e.getAttribute('minOccurs')
+
+ if self.allOptional is True:
+ #JRB Hack
+ minOccurs = '0'
+ maxOccurs = '"unbounded"'
+
+ return minOccurs,maxOccurs,nillable
+
+ def _getProcessContents(self, e):
+ processContents = e.getAttribute('processContents')
+ return processContents
+
+ def getBasesLogic(self, indent):
+ try:
+ prefix = NAD.getAlias(self.sKlassNS)
+ except WsdlGeneratorError, ex:
+ # XSD or SOAP
+ raise
+
+ bases = []
+ bases.append(\
+ 'if %s.%s not in %s.%s.__bases__:'\
+ %(NAD.getAlias(self.sKlassNS), type_class_name(self.sKlass), self.getNSAlias(), self.getClassName()),
+ )
+ bases.append(\
+ '%sbases = list(%s.%s.__bases__)'\
+ %(ID1,self.getNSAlias(),self.getClassName()),
+ )
+ bases.append(\
+ '%sbases.insert(0, %s.%s)'\
+ %(ID1,NAD.getAlias(self.sKlassNS), type_class_name(self.sKlass) ),
+ )
+ bases.append(\
+ '%s%s.%s.__bases__ = tuple(bases)'\
+ %(ID1, self.getNSAlias(), self.getClassName())
+ )
+
+ s = ''
+ for b in bases:
+ s += '%s%s\n' % (indent, b)
+
+ return s
+
+
+class MessageTypecodeContainer(TypecodeContainerBase):
+ '''Used for RPC style messages, where we have
+ serveral parts serialized within a rpc wrapper name.
+ '''
+ logger = _GetLogger("MessageTypecodeContainer")
+
+ def __init__(self, parts=None):
+ TypecodeContainerBase.__init__(self)
+ self.mgContent = parts
+
+ def _getOccurs(self, e):
+ '''return a 3 item tuple
+ '''
+ minOccurs = maxOccurs = '1'
+ nillable = True
+ return minOccurs,maxOccurs,nillable
+
+ def _setTypecodeList(self):
+ self.logger.debug("_setTypecodeList: %s" %
+ str(self.mgContent))
+
+ assert type(self.mgContent) is tuple,\
+ 'expecting tuple for mgContent not: %s' %type(self.mgContent)
+
+ for p in self.mgContent:
+ # JRB
+ # not sure what needs to be set for tc, this should be
+ # the job of the constructor or a setUp method.
+ min,max,nil = self._getOccurs(p)
+ if p.element:
+ raise WSInteropError, WSISpec.R2203
+ elif p.type:
+ nsuri,name = p.type
+ tc = RPCMessageTcListComponentContainer(qualified=False)
+ tc.setOccurs(min, max, nil)
+ tc.name = p.name
+ if nsuri in SCHEMA.XSD_LIST:
+ tpc = BTI.get_typeclass(name, nsuri)
+ tc.klass = tpc
+ else:
+ tc.klass = '%s.%s' % (NAD.getAlias(nsuri), type_class_name(name) )
+ else:
+ raise ContainerError, 'part must define an element or type attribute'
+
+ self.tcListElements.append(tc)
+
+ def getTypecodeList(self):
+ if not self.tcListSet:
+ self._setTypecodeList()
+ self.tcListSet = True
+
+ list = []
+ for e in self.tcListElements:
+ list.append(str(e))
+ return ', '.join(list)
+
+ def getAttributeNames(self):
+ '''returns a list of anames representing the parts
+ of the message.
+ '''
+ return map(lambda e: self.getAttributeName(e.name), self.tcListElements)
+
+ def setParts(self, parts):
+ self.mgContent = parts
+
+
+class TcListComponentContainer(ContainerBase):
+ '''Encapsulates a single value in the TClist list.
+ it inherits TypecodeContainerBase only to get the mangle() method,
+ it does not call the baseclass ctor.
+
+ TODO: Change this inheritance scheme.
+ '''
+ logger = _GetLogger("TcListComponentContainer")
+
+ def __init__(self, qualified=True):
+ '''
+ qualified -- qualify element. All GEDs should be qualified,
+ but local element declarations qualified if form attribute
+ is qualified, else they are unqualified. Only relevant for
+ standard style.
+ '''
+ #TypecodeContainerBase.__init__(self)
+ ContainerBase.__init__(self)
+
+ self.qualified = qualified
+ self.name = None
+ self.klass = None
+ self.global_type = None
+
+ self.min = None
+ self.max = None
+ self.nil = None
+ self.style = None
+ self.setStyleElementDeclaration()
+
+ def setOccurs(self, min, max, nil):
+ self.min = min
+ self.max = max
+ self.nil = nil
+
+ def setProcessContents(self, processContents):
+ self.processContents = processContents
+
+ def setGlobalType(self, namespace, name):
+ self.global_type = (namespace, name)
+
+ def setStyleElementDeclaration(self):
+ '''set the element style.
+ standard -- GED or local element
+ '''
+ self.style = 'standard'
+
+ def setStyleElementReference(self):
+ '''set the element style.
+ ref -- element reference
+ '''
+ self.style = 'ref'
+
+ def setStyleAnyElement(self):
+ '''set the element style.
+ anyElement -- <any> element wildcard
+ '''
+ self.name = 'any'
+ self.style = 'anyElement'
+
+# def setStyleRecursion(self):
+# '''TODO: Remove. good for 1 level
+# '''
+# self.style = 'recursion'
+
+ def unQualified(self):
+ '''Do not qualify element.
+ '''
+ self.qualified = False
+
+ def _getOccurs(self):
+ return 'minOccurs=%s, maxOccurs=%s, nillable=%s' \
+ % (self.min, self.max, self.nil)
+
+ def _getProcessContents(self):
+ return 'processContents="%s"' \
+ % (self.processContents)
+
+ def _getvalue(self):
+ kw = {'occurs':self._getOccurs(),
+ 'aname':self.getAttributeName(self.name),
+ 'klass':self.klass,
+ 'lazy':TypecodeContainerBase.lazy,
+ 'typed':'typed=False',
+ 'encoded':'encoded=kw.get("encoded")'}
+
+ gt = self.global_type
+ if gt is not None:
+ kw['nsuri'],kw['type'] = gt
+
+ if self.style == 'standard':
+ kw['pname'] = '"%s"' %self.name
+ if self.qualified is True:
+ kw['pname'] = '(ns,"%s")' %self.name
+ if gt is None:
+ return '%(klass)s(pname=%(pname)s, aname="%(aname)s", %(occurs)s, %(typed)s, %(encoded)s)' %kw
+ return 'GTD("%(nsuri)s","%(type)s",lazy=%(lazy)s)(pname=%(pname)s, aname="%(aname)s", %(occurs)s, %(typed)s, %(encoded)s)' %kw
+
+ if self.style == 'ref':
+ if gt is None:
+ return '%(klass)s(%(occurs)s, %(encoded)s)' %kw
+ return 'GED("%(nsuri)s","%(type)s",lazy=%(lazy)s, isref=True)(%(occurs)s, %(encoded)s)' %kw
+
+ kw['process'] = self._getProcessContents()
+ if self.style == 'anyElement':
+ return 'ZSI.TC.AnyElement(aname="%(aname)s", %(occurs)s, %(process)s)' %kw
+
+# if self.style == 'recursion':
+# return 'ZSI.TC.AnyElement(aname="%(aname)s", %(occurs)s, %(process)s)' %kw
+
+ raise RuntimeError, 'Must set style for typecode list generation'
+
+ def __str__(self):
+ return self._getvalue()
+
+
+class RPCMessageTcListComponentContainer(TcListComponentContainer):
+ '''Container for rpc/literal rpc/encoded message typecode.
+ '''
+ logger = _GetLogger("RPCMessageTcListComponentContainer")
+
+ def __init__(self, qualified=True, encoded=None):
+ '''
+ encoded -- encoded namespaceURI, if None treat as rpc/literal.
+ '''
+ TcListComponentContainer.__init__(self, qualified=qualified)
+ self._encoded = encoded
+
+ def _getvalue(self):
+ encoded = self._encoded
+ if encoded is not None:
+ encoded = '"%s"' %self._encoded
+
+ if self.style == 'standard':
+ pname = '"%s"' %self.name
+ if self.qualified is True:
+ pname = '(ns,"%s")' %self.name
+ return '%s(pname=%s, aname="%s", typed=False, encoded=%s, %s)' \
+ %(self.klass, pname, self.getAttributeName(self.name),
+ encoded, self._getOccurs())
+ elif self.style == 'ref':
+ return '%s(encoded=%s, %s)' % (self.klass, encoded, self._getOccurs())
+ elif self.style == 'anyElement':
+ return 'ZSI.TC.AnyElement(aname="%s", %s, %s)' \
+ %(self.getAttributeName(self.name), self._getOccurs(), self._getProcessContents())
+# elif self.style == 'recursion':
+# return 'ZSI.TC.AnyElement(aname="%s", %s, %s)' \
+# % (self.getAttributeName(self.name), self._getOccurs(), self._getProcessContents())
+
+ raise RuntimeError('Must set style(%s) for typecode list generation' %
+ self.style)
+
+
+class ElementSimpleTypeContainer(TypecodeContainerBase):
+ type = DEC
+ logger = _GetLogger("ElementSimpleTypeContainer")
+
+ def _setContent(self):
+ aname = self.getAttributeName(self.name)
+ pyclass = self.pyclass
+
+ # bool cannot be subclassed
+ if pyclass == 'bool': pyclass = 'int'
+ kw = KW.copy()
+ kw.update(dict(aname=aname, ns=self.ns, name=self.name,
+ subclass=self.sKlass,literal=self.literalTag(),
+ schema=self.schemaTag(), init=self.simpleConstructor(),
+ klass=self.getClassName(), element="ElementDeclaration"))
+
+ if self.local:
+ kw['element'] = 'LocalElementDeclaration'
+
+ element = map(lambda i: i %kw, [
+ '%(ID1)sclass %(klass)s(%(subclass)s, %(element)s):',
+ '%(ID2)s%(literal)s',
+ '%(ID2)s%(schema)s',
+ '%(ID2)s%(init)s',
+ '%(ID3)skw["pname"] = ("%(ns)s","%(name)s")',
+ '%(ID3)skw["aname"] = "%(aname)s"',
+ ]
+ )
+
+ # TODO: What about getPyClass and getPyClassDefinition?
+ # I want to add pyclass metaclass here but this needs to be
+ # corrected first.
+ #
+ # anyType (?others) has no pyclass.
+ app = element.append
+ if pyclass is not None:
+ app('%sclass IHolder(%s): typecode=self' % (ID3, pyclass),)
+ app('%skw["pyclass"] = IHolder' %(ID3),)
+ app('%sIHolder.__name__ = "%s_immutable_holder"' %(ID3, aname),)
+
+ app('%s%s' % (ID3, self.simpleConstructor(self.sKlass)),)
+
+ self.writeArray(element)
+
+ def setUp(self, tp):
+ self._item = tp
+ self.local = tp.isLocal()
+ try:
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ qName = tp.getAttribute('type')
+ except Exception, ex:
+ raise Wsdl2PythonError('Error occured processing element: %s' %(
+ tp.getItemTrace()), *ex.args)
+
+ if qName is None:
+ raise Wsdl2PythonError('Missing QName for element type attribute: %s' %tp.getItemTrace())
+
+ tns,local = qName.getTargetNamespace(),qName.getName()
+ self.sKlass = BTI.get_typeclass(local, tns)
+ if self.sKlass is None:
+ raise Wsdl2PythonError('No built-in typecode for type definition("%s","%s"): %s' %(tns,local,tp.getItemTrace()))
+
+ try:
+ self.pyclass = BTI.get_pythontype(None, None, typeclass=self.sKlass)
+ except Exception, ex:
+ raise Wsdl2PythonError('Error occured processing element: %s' %(
+ tp.getItemTrace()), *ex.args)
+
+
+class ElementLocalSimpleTypeContainer(TypecodeContainerBase):
+ '''local simpleType container
+ '''
+ type = DEC
+ logger = _GetLogger("ElementLocalSimpleTypeContainer")
+
+ def _setContent(self):
+ kw = KW.copy()
+ kw.update(dict(aname=self.getAttributeName(self.name), ns=self.ns, name=self.name,
+ subclass=self.sKlass,literal=self.literalTag(),
+ schema=self.schemaTag(), init=self.simpleConstructor(),
+ klass=self.getClassName(), element="ElementDeclaration",
+ baseinit=self.simpleConstructor(self.sKlass)))
+
+ if self.local:
+ kw['element'] = 'LocalElementDeclaration'
+
+ element = map(lambda i: i %kw, [
+ '%(ID1)sclass %(klass)s(%(subclass)s, %(element)s):',
+ '%(ID2)s%(literal)s',
+ '%(ID2)s%(schema)s',
+ '%(ID2)s%(init)s',
+ '%(ID3)skw["pname"] = ("%(ns)s","%(name)s")',
+ '%(ID3)skw["aname"] = "%(aname)s"',
+ '%(ID3)s%(baseinit)s',
+ ]
+ )
+ self.writeArray(element)
+
+ def setUp(self, tp):
+ self._item = tp
+ assert tp.isElement() is True and tp.content is not None and \
+ tp.content.isLocal() is True and tp.content.isSimple() is True ,\
+ 'expecting local simple type: %s' %tp.getItemTrace()
+
+ self.local = tp.isLocal()
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ content = tp.content.content
+ if content.isRestriction():
+ try:
+ base = content.getTypeDefinition()
+ except XMLSchema.SchemaError, ex:
+ base = None
+
+ qName = content.getAttributeBase()
+ if base is None:
+ self.sKlass = BTI.get_typeclass(qName[1], qName[0])
+ return
+
+ raise Wsdl2PythonError, 'unsupported local simpleType restriction: %s' \
+ %tp.content.getItemTrace()
+
+ if content.isList():
+ try:
+ base = content.getTypeDefinition()
+ except XMLSchema.SchemaError, ex:
+ base = None
+
+ if base is None:
+ qName = content.getItemType()
+ self.sKlass = BTI.get_typeclass(qName[1], qName[0])
+ return
+
+ raise Wsdl2PythonError, 'unsupported local simpleType List: %s' \
+ %tp.content.getItemTrace()
+
+ if content.isUnion():
+ raise Wsdl2PythonError, 'unsupported local simpleType Union: %s' \
+ %tp.content.getItemTrace()
+
+ raise Wsdl2PythonError, 'unexpected schema item: %s' \
+ %tp.content.getItemTrace()
+
+
+class ElementLocalComplexTypeContainer(TypecodeContainerBase, AttributeMixIn):
+ type = DEC
+ logger = _GetLogger("ElementLocalComplexTypeContainer")
+
+ def _setContent(self):
+ kw = KW.copy()
+ try:
+ kw.update(dict(klass=self.getClassName(),
+ subclass='ZSI.TCcompound.ComplexType',
+ element='ElementDeclaration',
+ literal=self.literalTag(),
+ schema=self.schemaTag(),
+ init=self.simpleConstructor(),
+ ns=self.ns, name=self.name,
+ aname=self.getAttributeName(self.name),
+ nsurilogic=self.nsuriLogic(),
+ ofwhat=self.getTypecodeList(),
+ atypecode=self.attribute_typecode,
+ pyclass=self.getPyClass(),
+ ))
+ except Exception, ex:
+ args = ['Failure processing an element w/local complexType: %s' %(
+ self._item.getItemTrace())]
+ args += ex.args
+ ex.args = tuple(args)
+ raise
+
+ if self.local:
+ kw['element'] = 'LocalElementDeclaration'
+
+ element = [
+ '%(ID1)sclass %(klass)s(%(subclass)s, %(element)s):',
+ '%(ID2)s%(literal)s',
+ '%(ID2)s%(schema)s',
+ '%(ID2)s%(init)s',
+ '%(ID3)s%(nsurilogic)s',
+ '%(ID3)sTClist = [%(ofwhat)s]',
+ '%(ID3)skw["pname"] = ("%(ns)s","%(name)s")',
+ '%(ID3)skw["aname"] = "%(aname)s"',
+ '%(ID3)s%(atypecode)s = {}',
+ '%(ID3)sZSI.TCcompound.ComplexType.__init__(self,None,TClist,inorder=0,**kw)',
+ ]
+ for l in self.attrComponents: element.append('%(ID3)s'+str(l))
+ element += self.getPyClassDefinition()
+ element.append('%(ID3)sself.pyclass = %(pyclass)s' %kw)
+ self.writeArray(map(lambda l: l %kw, element))
+
+ def setUp(self, tp):
+ '''
+ {'xsd':['annotation', 'simpleContent', 'complexContent',\
+ 'group', 'all', 'choice', 'sequence', 'attribute', 'attributeGroup',\
+ 'anyAttribute', 'any']}
+ '''
+ #
+ # TODO: Need a Recursive solution, this is incomplete will ignore many
+ # extensions, restrictions, etc.
+ #
+ self._item = tp
+ # JRB HACK SUPPORTING element/no content.
+ assert tp.isElement() is True and \
+ (tp.content is None or (tp.content.isComplex() is True and tp.content.isLocal() is True)),\
+ 'expecting element w/local complexType not: %s' %tp.content.getItemTrace()
+
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ self.local = tp.isLocal()
+
+ complex = tp.content
+ # JRB HACK SUPPORTING element/no content.
+ if complex is None:
+ self.mgContent = ()
+ return
+
+ #attributeContent = complex.getAttributeContent()
+ #self.mgContent = None
+ if complex.content is None:
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ is_simple = complex.content.isSimple()
+ if is_simple and complex.content.content.isExtension():
+ # TODO: Not really supported just passing thru
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ if is_simple and complex.content.content.isRestriction():
+ # TODO: Not really supported just passing thru
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ if is_simple:
+ raise ContainerError, 'not implemented local complexType/simpleContent: %s'\
+ %tp.getItemTrace()
+
+ is_complex = complex.content.isComplex()
+ if is_complex and complex.content.content is None:
+ # TODO: Recursion...
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ if (is_complex and complex.content.content.isExtension() and
+ complex.content.content.content is not None and
+ complex.content.content.content.isModelGroup()):
+
+ self.mgContent = complex.content.content.content.content
+ self.attrComponents = self._setAttributes(
+ complex.content.content.getAttributeContent()
+ )
+ return
+
+ if (is_complex and complex.content.content.isRestriction() and
+ complex.content.content.content is not None and
+ complex.content.content.content.isModelGroup()):
+
+ self.mgContent = complex.content.content.content.content
+ self.attrComponents = self._setAttributes(
+ complex.content.content.getAttributeContent()
+ )
+ return
+
+ if is_complex:
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ if complex.content.isModelGroup():
+ self.mgContent = complex.content.content
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ # TODO: Scary Fallthru
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+
+
+class ElementGlobalDefContainer(TypecodeContainerBase):
+ type = DEC
+ logger = _GetLogger("ElementGlobalDefContainer")
+
+ def _setContent(self):
+ '''GED defines element name, so also define typecode aname
+ '''
+ kw = KW.copy()
+ try:
+ kw.update(dict(klass=self.getClassName(),
+ element='ElementDeclaration',
+ literal=self.literalTag(),
+ schema=self.schemaTag(),
+ init=self.simpleConstructor(),
+ ns=self.ns, name=self.name,
+ aname=self.getAttributeName(self.name),
+ baseslogic=self.getBasesLogic(ID3),
+ #ofwhat=self.getTypecodeList(),
+ #atypecode=self.attribute_typecode,
+ #pyclass=self.getPyClass(),
+ alias=NAD.getAlias(self.sKlassNS),
+ subclass=type_class_name(self.sKlass),
+ ))
+ except Exception, ex:
+ args = ['Failure processing an element w/local complexType: %s' %(
+ self._item.getItemTrace())]
+ args += ex.args
+ ex.args = tuple(args)
+ raise
+
+ if self.local:
+ kw['element'] = 'LocalElementDeclaration'
+
+ element = [
+ '%(ID1)sclass %(klass)s(%(element)s):',
+ '%(ID2)s%(literal)s',
+ '%(ID2)s%(schema)s',
+ '%(ID2)s%(init)s',
+ '%(ID3)skw["pname"] = ("%(ns)s","%(name)s")',
+ '%(ID3)skw["aname"] = "%(aname)s"',
+ '%(baseslogic)s',
+ '%(ID3)s%(alias)s.%(subclass)s.__init__(self, **kw)',
+ '%(ID3)sif self.pyclass is not None: self.pyclass.__name__ = "%(klass)s_Holder"',
+ ]
+
+ self.writeArray(map(lambda l: l %kw, element))
+
+ def setUp(self, element):
+ # Save for debugging
+ self._item = element
+ self.local = element.isLocal()
+ self.name = element.getAttribute('name')
+ self.ns = element.getTargetNamespace()
+ tp = element.getTypeDefinition('type')
+ self.sKlass = tp.getAttribute('name')
+ self.sKlassNS = tp.getTargetNamespace()
+
+
+class ComplexTypeComplexContentContainer(TypecodeContainerBase, AttributeMixIn):
+ '''Represents ComplexType with ComplexContent.
+ '''
+ type = DEF
+ logger = _GetLogger("ComplexTypeComplexContentContainer")
+
+ def __init__(self, do_extended=False):
+ TypecodeContainerBase.__init__(self, do_extended=do_extended)
+
+ def setUp(self, tp):
+ '''complexContent/[extension,restriction]
+ restriction
+ extension
+ extType -- used in figuring attrs for extensions
+ '''
+ self._item = tp
+ assert tp.content.isComplex() is True and \
+ (tp.content.content.isRestriction() or tp.content.content.isExtension() is True),\
+ 'expecting complexContent/[extension,restriction]'
+
+ self.extType = None
+ self.restriction = False
+ self.extension = False
+ self._kw_array = None
+ self._is_array = False
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+
+ # xxx: what is this for?
+ #self.attribute_typecode = 'attributes'
+
+ derivation = tp.content.content
+ # Defined in Schema instance?
+ try:
+ base = derivation.getTypeDefinition('base')
+ except XMLSchema.SchemaError, ex:
+ base = None
+
+ # anyType, arrayType, etc...
+ if base is None:
+ base = derivation.getAttributeQName('base')
+ if base is None:
+ raise ContainerError, 'Unsupported derivation: %s'\
+ %derivation.getItemTrace()
+
+ if base != (SOAP.ENC,'Array') and base != (SCHEMA.XSD3,'anyType'):
+ raise ContainerError, 'Unsupported base(%s): %s' %(
+ base, derivation.getItemTrace()
+ )
+
+ if base == (SOAP.ENC,'Array'):
+ # SOAP-ENC:Array expecting arrayType attribute reference
+ self.logger.debug("Derivation of soapenc:Array")
+ self._is_array = True
+ self._kw_array = {'atype':None, 'id3':ID3, 'ofwhat':None}
+ self.sKlass = BTI.get_typeclass(base[1], base[0])
+ self.sKlassNS = base[0]
+ attr = None
+ for a in derivation.getAttributeContent():
+ assert a.isAttribute() is True,\
+ 'only attribute content expected: %s' %a.getItemTrace()
+
+ if a.isReference() is True:
+ if a.getAttribute('ref') == (SOAP.ENC,'arrayType'):
+ self._kw_array['atype'] = a.getAttributeQName((WSDL.BASE, 'arrayType'))
+ attr = a
+ break
+
+ qname = self._kw_array.get('atype')
+ if attr is not None:
+ qname = self._kw_array.get('atype')
+ ncname = qname[1].strip('[]')
+ namespace = qname[0]
+ try:
+ ofwhat = attr.getSchemaItem(XMLSchema.TYPES, namespace, ncname)
+ except XMLSchema.SchemaError, ex:
+ ofwhat = None
+
+ if ofwhat is None:
+ self._kw_array['ofwhat'] = BTI.get_typeclass(ncname, namespace)
+ else:
+ self._kw_array['ofwhat'] = GetClassNameFromSchemaItem(ofwhat, do_extended=self.do_extended)
+
+ if self._kw_array['ofwhat'] is None:
+ raise ContainerError, 'For Array could not resolve ofwhat typecode(%s,%s): %s'\
+ %(namespace, ncname, derivation.getItemTrace())
+
+ self.logger.debug('Attribute soapenc:arrayType="%s"' %
+ str(self._kw_array['ofwhat']))
+
+ elif isinstance(base, XMLSchema.XMLSchemaComponent):
+ self.sKlass = base.getAttribute('name')
+ self.sKlassNS = base.getTargetNamespace()
+ else:
+ # TypeDescriptionComponent
+ self.sKlass = base.getName()
+ self.sKlassNS = base.getTargetNamespace()
+
+ attrs = []
+ if derivation.isRestriction():
+ self.restriction = True
+ self.extension = False
+ # derivation.getAttributeContent subset of tp.getAttributeContent
+ attrs += derivation.getAttributeContent() or ()
+ else:
+ self.restriction = False
+ self.extension = True
+ attrs += tp.getAttributeContent() or ()
+ if isinstance(derivation, XMLSchema.XMLSchemaComponent):
+ attrs += derivation.getAttributeContent() or ()
+
+ # XXX: not sure what this is doing
+ if attrs:
+ self.extType = derivation
+
+ if derivation.content is not None \
+ and derivation.content.isModelGroup():
+ group = derivation.content
+ if group.isReference():
+ group = group.getModelGroupReference()
+ self.mgContent = group.content
+ elif derivation.content:
+ raise Wsdl2PythonError, \
+ 'expecting model group, not: %s' %derivation.content.getItemTrace()
+ else:
+ self.mgContent = ()
+
+ self.attrComponents = self._setAttributes(tuple(attrs))
+
+ def _setContent(self):
+ '''JRB What is the difference between instance data
+ ns, name, -- type definition?
+ sKlass, sKlassNS? -- element declaration?
+ '''
+ kw = KW.copy()
+ definition = []
+ if self._is_array:
+ # SOAP-ENC:Array
+ if _is_xsd_or_soap_ns(self.sKlassNS) is False and self.sKlass == 'Array':
+ raise ContainerError, 'unknown type: (%s,%s)'\
+ %(self.sKlass, self.sKlassNS)
+
+ # No need to xsi:type array items since specify with
+ # SOAP-ENC:arrayType attribute.
+ definition += [\
+ '%sclass %s(ZSI.TC.Array, TypeDefinition):' % (ID1, self.getClassName()),
+ '%s#complexType/complexContent base="SOAP-ENC:Array"' %(ID2),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%(id3)sofwhat = %(ofwhat)s(None, typed=False)' %self._kw_array,
+ '%(id3)satype = %(atype)s' %self._kw_array,
+ '%s%s.__init__(self, atype, ofwhat, pname=pname, childnames=\'item\', **kw)'
+ %(ID3, self.sKlass),
+ ]
+ self.writeArray(definition)
+ return
+
+ definition += [\
+ '%sclass %s(TypeDefinition):' % (ID1, self.getClassName()),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%s%s' % (ID3, self.nsuriLogic()),
+ '%sTClist = [%s]' % (ID3, self.getTypecodeList()),
+ ]
+
+ definition.append(
+ '%(ID3)sattributes = %(atc)s = attributes or {}' %{
+ 'ID3':ID3, 'atc':self.attribute_typecode}
+ )
+
+ #
+ # Special case: anyType restriction
+ isAnyType = (self.sKlassNS, self.sKlass) == (SCHEMA.XSD3, 'anyType')
+ if isAnyType:
+ del definition[0]
+ definition.insert(0,
+ '%sclass %s(ZSI.TC.ComplexType, TypeDefinition):' % (
+ ID1, self.getClassName())
+ )
+ definition.insert(1,
+ '%s#complexType/complexContent restrict anyType' %(
+ ID2)
+ )
+
+ # derived type support
+ definition.append('%sif extend: TClist += ofwhat'%(ID3))
+ definition.append('%sif restrict: TClist = ofwhat' %(ID3))
+ if len(self.attrComponents) > 0:
+ definition.append('%selse:' %(ID3))
+ for l in self.attrComponents:
+ definition.append('%s%s'%(ID4, l))
+
+ if isAnyType:
+ definition.append(\
+ '%sZSI.TC.ComplexType.__init__(self, None, TClist, pname=pname, **kw)' %(
+ ID3),
+ )
+
+ # pyclass class definition
+ definition += self.getPyClassDefinition()
+ kw['pyclass'] = self.getPyClass()
+ definition.append('%(ID3)sself.pyclass = %(pyclass)s' %kw)
+ self.writeArray(definition)
+ return
+
+ for l in self.attrComponents:
+ definition.append('%s%s'%(ID3, l))
+
+ definition.append('%s' % self.getBasesLogic(ID3))
+ prefix = NAD.getAlias(self.sKlassNS)
+ typeClassName = type_class_name(self.sKlass)
+ if self.restriction:
+ definition.append(\
+ '%s%s.%s.__init__(self, pname, ofwhat=TClist, restrict=True, **kw)' %(
+ ID3, prefix, typeClassName),
+ )
+ definition.insert(1, '%s#complexType/complexContent restriction' %ID2)
+ self.writeArray(definition)
+ return
+
+ if self.extension:
+ definition.append(\
+ '%s%s.%s.__init__(self, pname, ofwhat=TClist, extend=True, attributes=attributes, **kw)'%(
+ ID3, prefix, typeClassName),
+ )
+ definition.insert(1, '%s#complexType/complexContent extension' %(ID2))
+ self.writeArray(definition)
+ return
+
+ raise Wsdl2PythonError,\
+ 'ComplexContent must be a restriction or extension'
+
+ def pnameConstructor(self, superclass=None):
+ if superclass:
+ return '%s.__init__(self, pname, ofwhat=(), extend=False, restrict=False, attributes=None, **kw)' % superclass
+
+ return 'def __init__(self, pname, ofwhat=(), extend=False, restrict=False, attributes=None, **kw):'
+
+
+class ComplexTypeContainer(TypecodeContainerBase, AttributeMixIn):
+ '''Represents a global complexType definition.
+ '''
+ type = DEF
+ logger = _GetLogger("ComplexTypeContainer")
+
+ def setUp(self, tp, empty=False):
+ '''Problematic, loose all model group information.
+ <all>, <choice>, <sequence> ..
+
+ tp -- type definition
+ empty -- no model group, just use as a dummy holder.
+ '''
+ self._item = tp
+
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ self.mixed = tp.isMixed()
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(tp.getAttributeContent())
+
+ # Save reference to type for debugging
+ self._item = tp
+
+ if empty:
+ return
+
+ model = tp.content
+ if model.isReference():
+ model = model.getModelGroupReference()
+
+ if model is None:
+ return
+
+ if model.content is None:
+ return
+
+ # sequence, all or choice
+ #self.mgContent = model.content
+ self.mgContent = model
+
+ def _setContent(self):
+ try:
+ definition = [
+ '%sclass %s(ZSI.TCcompound.ComplexType, TypeDefinition):'
+ % (ID1, self.getClassName()),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ #'%s' % self.getElements(),
+ '%s%s' % (ID3, self.nsuriLogic()),
+ '%sTClist = [%s]' % (ID3, self.getTypecodeList()),
+ ]
+ except Exception, ex:
+ args = ["Failure processing %s" %self._item.getItemTrace()]
+ args += ex.args
+ ex.args = tuple(args)
+ raise
+
+ definition.append('%s%s = attributes or {}' %(ID3,
+ self.attribute_typecode))
+ # IF EXTEND
+ definition.append('%sif extend: TClist += ofwhat'%(ID3))
+ # IF RESTRICT
+ definition.append('%sif restrict: TClist = ofwhat' %(ID3))
+ # ELSE
+ if len(self.attrComponents) > 0:
+ definition.append('%selse:' %(ID3))
+ for l in self.attrComponents: definition.append('%s%s'%(ID4, l))
+
+ definition.append(\
+ '%sZSI.TCcompound.ComplexType.__init__(self, None, TClist, pname=pname, inorder=0, %s**kw)' \
+ %(ID3, self.getExtraFlags())
+ )
+
+ # pyclass class definition
+ definition += self.getPyClassDefinition()
+
+ # set pyclass
+ kw = KW.copy()
+ kw['pyclass'] = self.getPyClass()
+ definition.append('%(ID3)sself.pyclass = %(pyclass)s' %kw)
+ self.writeArray(definition)
+
+ def pnameConstructor(self, superclass=None):
+ ''' TODO: Logic is a little tricky. If superclass is ComplexType this is not used.
+ '''
+ if superclass:
+ return '%s.__init__(self, pname, ofwhat=(), attributes=None, extend=False, restrict=False, **kw)' % superclass
+
+ return 'def __init__(self, pname, ofwhat=(), attributes=None, extend=False, restrict=False, **kw):'
+
+
+class SimpleTypeContainer(TypecodeContainerBase):
+ type = DEF
+ logger = _GetLogger("SimpleTypeContainer")
+
+ def __init__(self):
+ '''
+ Instance Data From TypecodeContainerBase NOT USED...
+ mgContent
+ '''
+ TypecodeContainerBase.__init__(self)
+
+ def setUp(self, tp):
+ raise NotImplementedError, 'abstract method not implemented'
+
+ def _setContent(self, tp):
+ raise NotImplementedError, 'abstract method not implemented'
+
+ def getPythonType(self):
+ pyclass = eval(str(self.sKlass))
+ if issubclass(pyclass, ZSI.TC.String):
+ return 'str'
+ if issubclass(pyclass, ZSI.TC.Ilong) or issubclass(pyclass, ZSI.TC.IunsignedLong):
+ return 'long'
+ if issubclass(pyclass, ZSI.TC.Boolean) or issubclass(pyclass, ZSI.TC.Integer):
+ return 'int'
+ if issubclass(pyclass, ZSI.TC.Decimal):
+ return 'float'
+ if issubclass(pyclass, ZSI.TC.Gregorian) or issubclass(pyclass, ZSI.TC.Duration):
+ return 'tuple'
+ return None
+
+ def getPyClassDefinition(self):
+ definition = []
+ pt = self.getPythonType()
+ if pt is not None:
+ definition.append('%sclass %s(%s):' %(ID3,self.getPyClass(),pt))
+ definition.append('%stypecode = self' %ID4)
+ return definition
+
+
+class RestrictionContainer(SimpleTypeContainer):
+ '''
+ simpleType/restriction
+ '''
+ logger = _GetLogger("RestrictionContainer")
+
+ def setUp(self, tp):
+ self._item = tp
+
+ assert tp.isSimple() is True and tp.isDefinition() is True and \
+ tp.content.isRestriction() is True,\
+ 'expecting simpleType restriction, not: %s' %tp.getItemTrace()
+
+ if tp.content is None:
+ raise Wsdl2PythonError, \
+ 'empty simpleType defintion: %s' %tp.getItemTrace()
+
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ self.sKlass = None
+
+ base = tp.content.getAttribute('base')
+ if base is not None:
+ try:
+ item = tp.content.getTypeDefinition('base')
+ except XMLSchema.SchemaError, ex:
+ pass
+
+ if item is None:
+ self.sKlass = BTI.get_typeclass(base.getName(), base.getTargetNamespace())
+ if self.sKlass is not None:
+ return
+
+ raise Wsdl2PythonError('no built-in type nor schema instance type for base attribute("%s","%s"): %s' %(
+ base.getTargetNamespace(), base.getName(), tp.getItemTrace()))
+
+ raise Wsdl2PythonError, \
+ 'Not Supporting simpleType/Restriction w/User-Defined Base: %s %s' %(tp.getItemTrace(),item.getItemTrace())
+
+ sc = tp.content.getSimpleTypeContent()
+ if sc is not None and True is sc.isSimple() is sc.isLocal() is sc.isDefinition():
+ base = None
+ if sc.content.isRestriction() is True:
+ try:
+ item = tp.content.getTypeDefinition('base')
+ except XMLSchema.SchemaError, ex:
+ pass
+
+ if item is None:
+ base = sc.content.getAttribute('base')
+ if base is not None:
+ self.sKlass = BTI.get_typeclass(base.getTargetNamespace(), base.getName())
+ return
+ raise Wsdl2PythonError, \
+ 'Not Supporting simpleType/Restriction w/User-Defined Base: '\
+ %item.getItemTrace()
+
+ raise Wsdl2PythonError, \
+ 'Not Supporting simpleType/Restriction w/User-Defined Base: '\
+ %item.getItemTrace()
+
+ if sc.content.isList() is True:
+ raise Wsdl2PythonError, \
+ 'iction base in subtypes: %s'\
+ %sc.getItemTrace()
+
+ if sc.content.isUnion() is True:
+ raise Wsdl2PythonError, \
+ 'could not get restriction base in subtypes: %s'\
+ %sc.getItemTrace()
+
+ return
+
+ raise Wsdl2PythonError, 'No Restriction @base/simpleType: %s' %tp.getItemTrace()
+
+ def _setContent(self):
+
+ definition = [
+ '%sclass %s(%s, TypeDefinition):' %(ID1, self.getClassName(),
+ self.sKlass),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ ]
+ if self.getPythonType() is None:
+ definition.append('%s%s.__init__(self, pname, **kw)' %(ID3,
+ self.sKlass))
+ else:
+ definition.append('%s%s.__init__(self, pname, pyclass=None, **kw)' \
+ %(ID3, self.sKlass,))
+
+ # pyclass class definition
+ definition += self.getPyClassDefinition()
+
+ # set pyclass
+ kw = KW.copy()
+ kw['pyclass'] = self.getPyClass()
+ definition.append('%(ID3)sself.pyclass = %(pyclass)s' %kw)
+
+ self.writeArray(definition)
+
+
+class ComplexTypeSimpleContentContainer(SimpleTypeContainer, AttributeMixIn):
+ '''Represents a ComplexType with simpleContent.
+ '''
+ type = DEF
+ logger = _GetLogger("ComplexTypeSimpleContentContainer")
+
+ def setUp(self, tp):
+ '''tp -- complexType/simpleContent/[Exention,Restriction]
+ '''
+ self._item = tp
+
+ assert tp.isComplex() is True and tp.content.isSimple() is True,\
+ 'expecting complexType/simpleContent not: %s' %tp.content.getItemTrace()
+
+ simple = tp.content
+ dv = simple.content
+ assert dv.isExtension() is True or dv.isRestriction() is True,\
+ 'expecting complexType/simpleContent/[Extension,Restriction] not: %s' \
+ %tp.content.getItemTrace()
+
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ # TODO: Why is this being set?
+ self.content.attributeContent = dv.getAttributeContent()
+
+ base = dv.getAttribute('base')
+ if base is not None:
+ self.sKlass = BTI.get_typeclass( base[1], base[0] )
+ if not self.sKlass:
+ self.sKlass,self.sKlassNS = base[1], base[0]
+
+ self.attrComponents = self._setAttributes(
+ self.content.attributeContent
+ )
+ return
+
+ raise Wsdl2PythonError,\
+ 'simple content derivation bad base attribute: ' %tp.getItemTrace()
+
+ def _setContent(self):
+ # TODO: Add derivation logic to constructors.
+ if type(self.sKlass) in (types.ClassType, type):
+ definition = [
+ '%sclass %s(%s, TypeDefinition):' \
+ % (ID1, self.getClassName(), self.sKlass),
+ '%s# ComplexType/SimpleContent derivation of built-in type' %ID2,
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%sif getattr(self, "attribute_typecode_dict", None) is None: %s = {}' %(
+ ID3, self.attribute_typecode),
+ ]
+
+ for l in self.attrComponents:
+ definition.append('%s%s'%(ID3, l))
+
+ definition.append('%s%s.__init__(self, pname, **kw)' %(ID3, self.sKlass))
+ if self.getPythonType() is not None:
+ definition += self.getPyClassDefinition()
+ kw = KW.copy()
+ kw['pyclass'] = self.getPyClass()
+ definition.append('%(ID3)sself.pyclass = %(pyclass)s' %kw)
+
+ self.writeArray(definition)
+ return
+
+ definition = [
+ '%sclass %s(TypeDefinition):' % (ID1, self.getClassName()),
+ '%s# ComplexType/SimpleContent derivation of user-defined type' %ID2,
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%s%s' % (ID3, self.nsuriLogic()),
+ '%s' % self.getBasesLogic(ID3),
+ '%sif getattr(self, "attribute_typecode_dict", None) is None: %s = {}' %(
+ ID3, self.attribute_typecode),
+ ]
+
+ for l in self.attrComponents:
+ definition.append('%s%s'%(ID3, l))
+
+ definition.append('%s%s.%s.__init__(self, pname, **kw)' %(
+ ID3, NAD.getAlias(self.sKlassNS), type_class_name(self.sKlass)))
+
+ self.writeArray(definition)
+
+ def getPyClassDefinition(self):
+ definition = []
+ pt = self.getPythonType()
+ if pt is not None:
+ definition.append('%sclass %s(%s):' %(ID3,self.getPyClass(),pt))
+ if self.metaclass is not None:
+ definition.append('%s__metaclass__ = %s' %(ID4, self.metaclass))
+
+ definition.append('%stypecode = self' %ID4)
+ return definition
+
+
+
+
+
+class UnionContainer(SimpleTypeContainer):
+ '''SimpleType Union
+ '''
+ type = DEF
+ logger = _GetLogger("UnionContainer")
+
+ def __init__(self):
+ SimpleTypeContainer.__init__(self)
+ self.memberTypes = None
+
+ def setUp(self, tp):
+ self._item = tp
+
+ if tp.content.isUnion() is False:
+ raise ContainerError, 'content must be a Union: %s' %tp.getItemTrace()
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ self.sKlass = 'ZSI.TC.Union'
+ self.memberTypes = tp.content.getAttribute('memberTypes')
+
+ def _setContent(self):
+ definition = [
+ '%sclass %s(%s, TypeDefinition):' \
+ % (ID1, self.getClassName(), self.sKlass),
+ '%smemberTypes = %s' % (ID2, self.memberTypes),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%s%s' % (ID3, self.pnameConstructor(self.sKlass)),
+ ]
+
+ # TODO: Union pyclass is None
+ self.writeArray(definition)
+
+
+class ListContainer(SimpleTypeContainer):
+ '''SimpleType List
+ '''
+ type = DEF
+ logger = _GetLogger("ListContainer")
+
+ def setUp(self, tp):
+ self._item = tp
+
+ if tp.content.isList() is False:
+ raise ContainerError, 'content must be a List: %s' %tp.getItemTrace()
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ self.sKlass = 'ZSI.TC.List'
+ self.itemType = tp.content.getAttribute('itemType')
+
+ def _setContent(self):
+ definition = [
+ '%sclass %s(%s, TypeDefinition):' \
+ % (ID1, self.getClassName(), self.sKlass),
+ '%sitemType = %s' % (ID2, self.itemType),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%s%s' % (ID3, self.pnameConstructor(self.sKlass)),
+ ]
+ self.writeArray(definition)
+
+
diff --git a/ZSI/generate/pyclass.py b/ZSI/generate/pyclass.py
new file mode 100644
index 0000000..54dce2f
--- /dev/null
+++ b/ZSI/generate/pyclass.py
@@ -0,0 +1,285 @@
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+import pydoc, sys, warnings
+from ZSI import TC
+
+# If function.__name__ is read-only, fail
+def _x(): return
+try:
+ _x.func_name = '_y'
+except:
+ raise RuntimeError,\
+ 'use python-2.4 or later, cannot set function names in python "%s"'\
+ %sys.version
+del _x
+
+
+#def GetNil(typecode=None):
+# """returns the nilled element, use to set an element
+# as nilled for immutable instance.
+# """
+#
+# nil = TC.Nilled()
+# if typecode is not None: nil.typecode = typecode
+# return nil
+#
+#
+#def GetNilAsSelf(cls, typecode=None):
+# """returns the nilled element with typecode specified,
+# use returned instance to set this element as nilled.
+#
+# Key Word Parameters:
+# typecode -- use to specify a derived type or wildcard as nilled.
+# """
+# if typecode is not None and not isinstance(typecode, TC.TypeCode):
+# raise TypeError, "Expecting a TypeCode instance"
+#
+# nil = TC.Nilled()
+# nil.typecode = typecode or cls.typecode
+# return nil
+
+
+class pyclass_type(type):
+ """Stability: Unstable
+
+ type for pyclasses used with typecodes. expects the typecode to
+ be available in the classdict. creates python properties for accessing
+ and setting the elements specified in the ofwhat list, and factory methods
+ for constructing the elements.
+
+ Known Limitations:
+ 1)Uses XML Schema element names directly to create method names,
+ using characters in this set will cause Syntax Errors:
+
+ (NCNAME)-(letter U digit U "_")
+
+ """
+ def __new__(cls, classname, bases, classdict):
+ """
+ """
+ #import new
+ typecode = classdict.get('typecode')
+ assert typecode is not None, 'MUST HAVE A TYPECODE.'
+
+ # Assume this means immutable type. ie. str
+ if len(bases) > 0:
+ #classdict['new_Nill'] = classmethod(GetNilAsSelf)
+ pass
+ # Assume this means mutable type. ie. ofwhat.
+ else:
+ assert hasattr(typecode, 'ofwhat'), 'typecode has no ofwhat list??'
+ assert hasattr(typecode, 'attribute_typecode_dict'),\
+ 'typecode has no attribute_typecode_dict??'
+
+ #classdict['new_Nill'] = staticmethod(GetNil)
+
+ if typecode.mixed:
+ get,set = cls.__create_text_functions_from_what(typecode)
+
+ if classdict.has_key(get.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %get.__name__
+
+ if classdict.has_key(set.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %set.__name__
+
+ classdict[get.__name__] = get
+ classdict[set.__name__] = set
+
+ for what in typecode.ofwhat:
+ get,set,new_func = cls.__create_functions_from_what(what)
+
+ if classdict.has_key(get.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %get.__name__
+
+ classdict[get.__name__] = get
+ if classdict.has_key(set.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %set.__name__
+
+ classdict[set.__name__] = set
+ if new_func is not None:
+ if classdict.has_key(new_func.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %new_func.__name__
+
+ classdict[new_func.__name__] = new_func
+
+ assert not classdict.has_key(what.pname),\
+ 'collision with pname="%s", bail..' %what.pname
+
+ pname = what.pname
+ if pname is None and isinstance(what, TC.AnyElement): pname = 'any'
+ assert pname is not None, 'Element with no name: %s' %what
+
+ # TODO: for pname if keyword just uppercase first letter.
+ #if pydoc.Helper.keywords.has_key(pname):
+ pname = pname[0].upper() + pname[1:]
+ assert not pydoc.Helper.keywords.has_key(pname), 'unexpected keyword: %s' %pname
+
+ classdict[pname] =\
+ property(get, set, None,
+ 'property for element (%s,%s), minOccurs="%s" maxOccurs="%s" nillable="%s"'\
+ %(what.nspname,what.pname,what.minOccurs,what.maxOccurs,what.nillable)
+ )
+
+ #
+ # mutable type <complexType> complexContent | modelGroup
+ # or immutable type <complexType> simpleContent (float, str, etc)
+ #
+ if hasattr(typecode, 'attribute_typecode_dict'):
+ attribute_typecode_dict = typecode.attribute_typecode_dict or {}
+ for key,what in attribute_typecode_dict.items():
+ get,set = cls.__create_attr_functions_from_what(key, what)
+ if classdict.has_key(get.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %get.__name__
+
+ if classdict.has_key(set.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %set.__name__
+
+ classdict[get.__name__] = get
+ classdict[set.__name__] = set
+
+ return type.__new__(cls,classname,bases,classdict)
+
+ def __create_functions_from_what(what):
+ if not callable(what):
+ def get(self):
+ return getattr(self, what.aname)
+
+ if what.maxOccurs > 1:
+ def set(self, value):
+ if not (value is None or hasattr(value, '__iter__')):
+ raise TypeError, 'expecting an iterable instance'
+ setattr(self, what.aname, value)
+ else:
+ def set(self, value):
+ setattr(self, what.aname, value)
+ else:
+ def get(self):
+ return getattr(self, what().aname)
+
+ if what.maxOccurs > 1:
+ def set(self, value):
+ if not (value is None or hasattr(value, '__iter__')):
+ raise TypeError, 'expecting an iterable instance'
+ setattr(self, what().aname, value)
+ else:
+ def set(self, value):
+ setattr(self, what().aname, value)
+
+ #
+ # new factory function
+ # if pyclass is None, skip
+ #
+ if not callable(what) and getattr(what, 'pyclass', None) is None:
+ new_func = None
+ elif (isinstance(what, TC.ComplexType) or
+ isinstance(what, TC.Array)):
+
+ def new_func(self):
+ '''returns a mutable type
+ '''
+ return what.pyclass()
+
+ elif not callable(what):
+
+ def new_func(self, value):
+ '''value -- initialize value
+ returns an immutable type
+ '''
+ return what.pyclass(value)
+
+ elif (issubclass(what.klass, TC.ComplexType) or
+ issubclass(what.klass, TC.Array)):
+
+ def new_func(self):
+ '''returns a mutable type or None (if no pyclass).
+ '''
+ p = what().pyclass
+ if p is None: return
+ return p()
+
+ else:
+
+ def new_func(self, value=None):
+ '''if simpleType provide initialization value, else
+ if complexType value should be left as None.
+ Parameters:
+ value -- initialize value or None
+
+ returns a mutable instance (value is None)
+ or an immutable instance or None (if no pyclass)
+ '''
+ p = what().pyclass
+ if p is None: return
+ if value is None: return p()
+ return p(value)
+
+ #TODO: sub all illegal characters in set
+ # (NCNAME)-(letter U digit U "_")
+ if new_func is not None:
+ new_func.__name__ = 'new_%s' %what.pname
+ get.func_name = 'get_element_%s' %what.pname
+ set.func_name = 'set_element_%s' %what.pname
+ return get,set,new_func
+ __create_functions_from_what = staticmethod(__create_functions_from_what)
+
+ def __create_attr_functions_from_what(key, what):
+
+ def get(self):
+ '''returns attribute value for attribute %s, else None.
+ ''' %str(key)
+ return getattr(self, what.attrs_aname, {}).get(key, None)
+
+ def set(self, value):
+ '''set value for attribute %s.
+ value -- initialize value, immutable type
+ ''' %str(key)
+ if not hasattr(self, what.attrs_aname):
+ setattr(self, what.attrs_aname, {})
+ getattr(self, what.attrs_aname)[key] = value
+
+ #TODO: sub all illegal characters in set
+ # (NCNAME)-(letter U digit U "_")
+ if type(key) in (tuple, list):
+ get.__name__ = 'get_attribute_%s' %key[1]
+ set.__name__ = 'set_attribute_%s' %key[1]
+ else:
+ get.__name__ = 'get_attribute_%s' %key
+ set.__name__ = 'set_attribute_%s' %key
+
+ return get,set
+ __create_attr_functions_from_what = \
+ staticmethod(__create_attr_functions_from_what)
+
+ def __create_text_functions_from_what(what):
+
+ def get(self):
+ '''returns text content, else None.
+ '''
+ return getattr(self, what.mixed_aname, None)
+
+ get.im_func = 'get_text'
+
+ def set(self, value):
+ '''set text content.
+ value -- initialize value, immutable type
+ '''
+ setattr(self, what.mixed_aname, value)
+
+ get.im_func = 'set_text'
+
+ return get,set
+ __create_text_functions_from_what = \
+ staticmethod(__create_text_functions_from_what)
+
+
+
diff --git a/ZSI/generate/utility.py b/ZSI/generate/utility.py
new file mode 100644
index 0000000..d7c5073
--- /dev/null
+++ b/ZSI/generate/utility.py
@@ -0,0 +1,159 @@
+############################################################################
+# Monte M. Goode, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+# utility classes used by new generator - mostly 'sugar' classes
+# that are actually imported by the generated code. also includes
+# utilities used by wsdl2python itself.
+
+# $Id: utility.py 1226 2006-05-26 18:11:19Z boverhof $
+
+import re
+from ZSI import EvaluateException
+from ZSI.TCcompound import Struct
+from ZSI.generate import WsdlGeneratorError, Wsdl2PythonError
+from ZSI.wstools.Utility import SplitQName
+from ZSI.wstools.Namespaces import SCHEMA
+
+NCName_to_ModuleName = lambda name: re.sub('\.', '_', TextProtect(name))
+NCName_to_ClassName = lambda name: re.sub('\.', '_', TextProtect(name))
+TextProtect = lambda s: re.sub('[-./:# ]', '_', s)
+TextProtectAttributeName = lambda name: TextProtect('_%s' %name)
+Namespace2ModuleName = lambda ns: TextProtect(ns.lstrip('http://')).rstrip('_')
+
+
+def GetModuleBaseNameFromWSDL(wsdl):
+ """By default try to construct a reasonable base name for all
+ generated modules. Otherwise return None.
+ """
+ base_name = wsdl.name or wsdl.services[0].name
+ base_name = SplitQName(base_name)[1]
+ if base_name is None:
+ return None
+ return NCName_to_ModuleName(base_name)
+
+namespace_name = lambda cls, ns: 'ns%s' % len(cls.alias_list)
+
+class NamespaceAliasDict:
+ """a lookup table to store relevant namespaces and their aliases"""
+ alias_dict = {}
+ alias_list = []
+
+ def add(cls, ns):
+ if cls.alias_dict.has_key(ns):
+ return
+ cls.alias_dict[ns] = (Namespace2ModuleName(ns), '%s' % namespace_name(cls,ns))
+ cls.alias_list.append(ns)
+ add = classmethod(add)
+
+ def getModuleName(cls, ns):
+ if cls.alias_dict.has_key(ns):
+ return cls.alias_dict[ns][0]
+
+ msg = 'failed to find import for schema "%s"'%ns +\
+ 'possibly missing @schemaLocation attribute.'
+ if ns in SCHEMA.XSD_LIST:
+ msg = 'missing built-in typecode for schema "%s"' %ns
+
+ raise WsdlGeneratorError, msg
+
+ getModuleName = classmethod(getModuleName)
+
+ def getAlias(cls, ns):
+ if cls.alias_dict.has_key(ns):
+ return cls.alias_dict[ns][1]
+
+ msg = 'failed to find import for schema "%s"'%ns +\
+ 'possibly missing @schemaLocation attribute.'
+ if ns in SCHEMA.XSD_LIST:
+ msg = 'missing built-in typecode for schema "%s"' %ns
+
+ raise WsdlGeneratorError, msg
+
+ getAlias = classmethod(getAlias)
+
+ def getNSList(cls):
+ return tuple(cls.alias_list)
+ getNSList = classmethod(getNSList)
+
+
+class StringWriter:
+ """generator util"""
+ def __init__(self, val=None):
+ self.data = []
+ if val:
+ self.data.append(val)
+
+ def set(self, val):
+ if self.data:
+ # in some cases the empty list reassignment fails, so....
+ self.data = None
+ self.data = []
+
+ self.data.append(val)
+
+ def write(self, val):
+ self.data.append(val)
+
+ def getvalue(self):
+ if self.data:
+ return ''.join(self.data)
+ else:
+ return ''
+
+ def __iadd__(self, val):
+ self.data.append(val)
+ return self
+
+ def __str__(self):
+ return self.getvalue()
+
+
+# ---- generated code utils
+
+class MessageContainer:
+ """generator util - used by address.py"""
+ pass
+
+# Extract sub names from message parts so they can be used when mapping
+# a message's contents to a function's arguments.
+# Args is a list of Message Parts. i.e.: op.getInputMessage().parts.values()
+def GetPartsSubNames(args, wsdl):
+ do_extended = True
+ from wsdl2python import WriteServiceModule, SchemaDescription
+ wsm = WriteServiceModule(wsdl, do_extended=do_extended)
+ wsm.gatherNamespaces()
+ toReturn = []
+ for arg in args:
+ argSubnames = []
+ for l in wsm.usedNamespaces.values():
+ for schema in l:
+ sd = SchemaDescription(do_extended=do_extended)
+ sd.fromSchema(schema)
+ argNamespace = arg.element[0]
+ if (sd.targetNamespace == argNamespace):
+ for i in sd.items:
+ # arg.name is the part name, but we want it's type
+ argElementType = arg.element[1]
+ if str(argElementType) == str(i.content.name):
+ argSubnames = []
+ # I'm not sure when the name attribute was dropped
+ # but at some point, or in some circumstance it's not
+ # there, but instead a ref attribute is there which is
+ # tuple of (namespace, name). This hack fixes things,
+ # but I'm not sure why this happens or has happened.
+ # IRJ - 2005-05-25
+ if i.content.mgContent != None:
+ for c in i.content.mgContent:
+ nValue = "None"
+ if c.isWildCard():
+ nValue="any"
+ elif c.attributes.has_key("name"):
+ nValue = c.attributes["name"]
+ elif c.attributes.has_key("ref"):
+ nValue = c.attributes["ref"][1]
+ argSubnames.append(nValue)
+
+ toReturn.append(argSubnames)
+ return toReturn
diff --git a/ZSI/generate/wsdl2dispatch.py b/ZSI/generate/wsdl2dispatch.py
new file mode 100755
index 0000000..d08c2ed
--- /dev/null
+++ b/ZSI/generate/wsdl2dispatch.py
@@ -0,0 +1,531 @@
+#!/usr/bin/env python
+from cStringIO import StringIO
+import ZSI, string, sys, getopt, urlparse, types, warnings
+from ZSI.wstools import WSDLTools
+from ZSI.generate.wsdl2python import WriteServiceModule, MessageTypecodeContainer
+from ZSI.ServiceContainer import ServiceSOAPBinding, SimpleWSResource
+from ZSI.generate.utility import TextProtect, GetModuleBaseNameFromWSDL, NCName_to_ClassName, GetPartsSubNames, TextProtectAttributeName
+from ZSI.generate import WsdlGeneratorError, Wsdl2PythonError
+from ZSI.generate.wsdl2python import SchemaDescription
+
+
+# Split last token
+rsplit = lambda x,sep,: (x[:x.rfind(sep)], x[x.rfind(sep)+1:],)
+if sys.version_info[0:2] == (2, 4, 0, 'final', 0)[0:2]:
+ rsplit = lambda x,sep,: x.rsplit(sep, 1)
+
+class SOAPService:
+ def __init__(self, service):
+ self.classdef = StringIO()
+ self.initdef = StringIO()
+ self.location = ''
+ self.methods = []
+
+ def newMethod(self):
+ '''name -- operation name
+ '''
+ self.methods.append(StringIO())
+ return self.methods[-1]
+
+class ServiceModuleWriter:
+ '''Creates a skeleton for a SOAP service instance.
+ '''
+ indent = ' '*4
+ server_module_suffix = '_services_server'
+ func_aname = TextProtectAttributeName
+ func_aname = staticmethod(func_aname)
+ separate_messages = False # Whether to write message definitions into a separate file.
+
+ def __init__(self, base=ServiceSOAPBinding, prefix='soap', service_class=SOAPService, do_extended=False):
+ '''
+ parameters:
+ base -- either a class definition, or a str representing a qualified class name.
+ prefix -- method prefix.
+ '''
+ self.wsdl = None
+ self.base_class = base
+ self.method_prefix = prefix
+ self._service_class = SOAPService
+
+ self.header = None
+ self.imports = None
+ self._services = None
+ self.client_module_path = None
+ self.client_module_name = None
+ self.messages_module_name = None
+ self.do_extended = do_extended
+
+ def reset(self):
+ self.header = StringIO()
+ self.imports = StringIO()
+ self._services = {}
+
+ def _getBaseClassName(self):
+ '''return base class name, do not override.
+ '''
+ if type(self.base_class) is types.ClassType:
+ return self.base_class.__name__
+ return rsplit(self.base_class, '.')[-1]
+
+ def _getBaseClassModule(self):
+ '''return base class module, do not override.
+ '''
+ if type(self.base_class) is types.ClassType:
+ return self.base_class.__module__
+ if self.base_class.find('.') >= 0:
+ return rsplit(self.base_class, '.')[0]
+ return None
+
+ def getIndent(self, level=1):
+ '''return indent.
+ '''
+ assert 0 < level < 10, 'bad indent level %d' %level
+ return self.indent*level
+
+ def getMethodName(self, method):
+ '''return method name.
+ '''
+ return '%s_%s' %(self.method_prefix, TextProtect(method))
+
+ def getClassName(self, name):
+ '''return class name.
+ '''
+ return NCName_to_ClassName(name)
+
+ def setClientModuleName(self, name):
+ self.client_module_name = name
+
+ def getClientModuleName(self):
+ '''return module name.
+ '''
+ assert self.wsdl is not None, 'initialize, call fromWSDL'
+ if self.client_module_name is not None:
+ return self.client_module_name
+
+ wsm = WriteServiceModule(self.wsdl, do_extended=self.do_extended)
+ return wsm.getClientModuleName()
+
+ def getMessagesModuleName(self):
+ '''return module name.
+ '''
+ assert self.wsdl is not None, 'initialize, call fromWSDL'
+ if self.messages_module_name is not None:
+ return self.messages_module_name
+
+ wsm = WriteServiceModule(self.wsdl, do_extended=self.do_extended)
+ return wsm.getMessagesModuleName()
+
+ def getServiceModuleName(self):
+ '''return module name.
+ '''
+ name = GetModuleBaseNameFromWSDL(self.wsdl)
+ if not name:
+ raise WsdlGeneratorError, 'could not determine a service name'
+
+ if self.server_module_suffix is None:
+ return name
+ return '%s%s' %(name, self.server_module_suffix)
+
+ def getClientModulePath(self):
+ return self.client_module_path
+
+ def setClientModulePath(self, path):
+ '''setup module path to where client module before calling fromWSDL.
+ '''
+ self.client_module_path = path
+
+ def setUpClassDef(self, service):
+ '''set class definition and class variables.
+ service -- ServiceDescription instance
+ '''
+ assert isinstance(service, WSDLTools.Service) is True,\
+ 'expecting WSDLTools.Service instance.'
+
+ s = self._services[service.name].classdef
+ print >>s, 'class %s(%s):' %(self.getClassName(service.name), self._getBaseClassName())
+ print >>s, '%ssoapAction = {}' % self.getIndent(level=1)
+ print >>s, '%sroot = {}' % self.getIndent(level=1)
+ print >>s, "%s_wsdl = \"\"\"%s\"\"\"" % (self.getIndent(level=1), self.raw_wsdl)
+
+ def setUpImports(self):
+ '''set import statements
+ '''
+ path = self.getClientModulePath()
+ i = self.imports
+ if path is None:
+ if self.separate_messages:
+ print >>i, 'from %s import *' %self.getMessagesModuleName()
+ else:
+ print >>i, 'from %s import *' %self.getClientModuleName()
+ else:
+ if self.separate_messages:
+ print >>i, 'from %s.%s import *' %(path, self.getMessagesModuleName())
+ else:
+ print >>i, 'from %s.%s import *' %(path, self.getClientModuleName())
+
+ mod = self._getBaseClassModule()
+ name = self._getBaseClassName()
+ if mod is None:
+ print >>i, 'import %s' %name
+ else:
+ print >>i, 'from %s import %s' %(mod, name)
+
+ def setUpInitDef(self, service):
+ '''set __init__ function
+ '''
+ assert isinstance(service, WSDLTools.Service), 'expecting WSDLTools.Service instance.'
+ sd = self._services[service.name]
+ d = sd.initdef
+
+ if sd.location is not None:
+ scheme,netloc,path,params,query,fragment = urlparse.urlparse(sd.location)
+ print >>d, '%sdef __init__(self, post=\'%s\', **kw):' %(self.getIndent(level=1), path)
+ else:
+ print >>d, '%sdef __init__(self, post, **kw):' %self.getIndent(level=1)
+
+ print >>d, '%s%s.__init__(self, post)' %(self.getIndent(level=2),self._getBaseClassName())
+
+
+ def mangle(self, name):
+ return TextProtect(name)
+
+ def getAttributeName(self, name):
+ return self.func_aname(name)
+
+ def setUpMethods(self, port):
+ '''set up all methods representing the port operations.
+ Parameters:
+ port -- Port that defines the operations.
+ '''
+ assert isinstance(port, WSDLTools.Port), \
+ 'expecting WSDLTools.Port not: ' %type(port)
+
+ sd = self._services.get(port.getService().name)
+ assert sd is not None, 'failed to initialize.'
+
+ binding = port.getBinding()
+ portType = port.getPortType()
+ action_in = ''
+ for bop in binding.operations:
+ try:
+ op = portType.operations[bop.name]
+ except KeyError, ex:
+ raise WsdlGeneratorError,\
+ 'Port(%s) PortType(%s) missing operation(%s) defined in Binding(%s)' \
+ %(port.name,portType.name,bop.name,binding.name)
+
+ for ext in bop.extensions:
+ if isinstance(ext, WSDLTools.SoapOperationBinding):
+ action_in = ext.soapAction
+ break
+ else:
+ warnings.warn('Port(%s) operation(%s) defined in Binding(%s) missing soapAction' \
+ %(port.name,op.name,binding.name)
+ )
+
+ msgin = op.getInputMessage()
+ msgin_name = TextProtect(msgin.name)
+ method_name = self.getMethodName(op.name)
+
+ m = sd.newMethod()
+ print >>m, '%sdef %s(self, ps):' %(self.getIndent(level=1), method_name)
+ if msgin is not None:
+ print >>m, '%sself.request = ps.Parse(%s.typecode)' %(self.getIndent(level=2), msgin_name)
+ else:
+ print >>m, '%s# NO input' %self.getIndent(level=2)
+
+ msgout = op.getOutputMessage()
+
+ if self.do_extended:
+ input_args = msgin.parts.values()
+ iargs = ["%s" % x.name for x in input_args]
+ if msgout is not None:
+ output_args = msgout.parts.values()
+ else:
+ output_args = []
+ oargs = ["%s" % x.name for x in output_args]
+ if output_args:
+ if len(output_args) > 1:
+ print "Message has more than one return value (Bad Design)."
+ output_args = "(%s)" % output_args
+ else:
+ output_args = ""
+ # Get arg list
+ iSubNames = GetPartsSubNames(msgin.parts.values(), self.wsdl)
+ for i in range( len(iargs) ): # should only be one part to messages here anyway
+ argSubNames = iSubNames[i]
+ if len(argSubNames) > 0:
+ subNamesStr = "self.request." + ", self.request.".join(map(self.getAttributeName, argSubNames))
+ if len(argSubNames) > 1:
+ subNamesStr = "(" + subNamesStr + ")"
+ print >>m, "%s%s = %s" % (self.getIndent(level=2), iargs[i], subNamesStr)
+
+ print >>m, "\n%s# If we have an implementation object use it" % (self.getIndent(level=2))
+ print >>m, "%sif hasattr(self,'impl'):" % (self.getIndent(level=2))
+
+ iargsStrList = []
+ for arg in iargs:
+ argSubNames = iSubNames[i]
+ if len(argSubNames) > 0:
+ if len(argSubNames) > 1:
+ for i in range(len(argSubNames)):
+ iargsStrList.append( arg + "[%i]" % i )
+ else:
+ iargsStrList.append( arg )
+ iargsStr = ",".join(iargsStrList)
+ oargsStr = ", ".join(oargs)
+ if len(oargsStr) > 0:
+ oargsStr += " = "
+ print >>m, "%s%sself.impl.%s(%s)" % (self.getIndent(level=3), oargsStr, op.name, iargsStr)
+
+ if msgout is not None:
+ msgout_name = TextProtect(msgout.name)
+ if self.do_extended:
+ print >>m, '\n%sresult = %s()' %(self.getIndent(level=2), msgout_name)
+ oSubNames = GetPartsSubNames(msgout.parts.values(), self.wsdl)
+ if (len(oSubNames) > 0) and (len(oSubNames[0]) > 0):
+ print >>m, "%s# If we have an implementation object, copy the result " % (self.getIndent(level=2))
+ print >>m, "%sif hasattr(self,'impl'):" % (self.getIndent(level=2))
+ # copy result's members
+ for i in range( len(oargs) ): # should only be one part messages here anyway
+ oargSubNames = oSubNames[i]
+ if len(oargSubNames) > 1:
+ print >>m, '%s# Should have a tuple of %i args' %(self.getIndent(level=3), len(oargSubNames))
+ for j in range(len(oargSubNames)):
+ oargSubName = oargSubNames[j]
+ print >>m, '%sresult.%s = %s[%i]' %(self.getIndent(level=3), self.getAttributeName(oargSubName), oargs[i], j)
+ elif len(oargSubNames) == 1:
+ oargSubName = oargSubNames[0]
+ print >>m, '%sresult.%s = %s' %(self.getIndent(level=3), self.getAttributeName(oargSubName), oargs[i])
+ else:
+ raise Exception("The subnames within message " + msgout_name + "'s part were not found. Message is the output of operation " + op.name)
+ print >>m, '%sreturn result' %(self.getIndent(level=2))
+ else:
+ print >>m, '%sreturn %s()' %(self.getIndent(level=2), msgout_name)
+ else:
+ print >>m, '%s# NO output' % self.getIndent(level=2)
+ print >>m, '%sreturn None' % self.getIndent(level=2)
+
+ print >>m, ''
+ print >>m, '%ssoapAction[\'%s\'] = \'%s\'' %(self.getIndent(level=1), action_in, method_name)
+ print >>m, '%sroot[(%s.typecode.nspname,%s.typecode.pname)] = \'%s\'' \
+ %(self.getIndent(level=1), msgin_name, msgin_name, method_name)
+
+ return
+
+ def setUpHeader(self):
+ print >>self.header, '##################################################'
+ print >>self.header, '# %s.py' %self.getServiceModuleName()
+ print >>self.header, '# Generated by %s' %(self.__class__)
+ print >>self.header, '#'
+ print >>self.header, '##################################################'
+
+ def write(self, fd=sys.stdout):
+ '''write out to file descriptor,
+ should not need to override.
+ '''
+ print >>fd, self.header.getvalue()
+ print >>fd, self.imports.getvalue()
+ for k,v in self._services.items():
+ print >>fd, v.classdef.getvalue()
+ print >>fd, v.initdef.getvalue()
+ for s in v.methods:
+ print >>fd, s.getvalue()
+
+ def fromWSDL(self, wsdl):
+ '''setup the service description from WSDL,
+ should not need to override.
+ '''
+ assert isinstance(wsdl, WSDLTools.WSDL), 'expecting WSDL instance'
+
+ if len(wsdl.services) == 0:
+ raise WsdlGeneratorError, 'No service defined'
+
+ self.reset()
+ self.wsdl = wsdl
+ self.raw_wsdl = wsdl.document.toxml().replace("\"", "\\\"")
+ self.setUpHeader()
+ self.setUpImports()
+ for service in wsdl.services:
+ sd = self._service_class(service.name)
+ self._services[service.name] = sd
+
+ for port in service.ports:
+ for e in port.extensions:
+ if isinstance(e, WSDLTools.SoapAddressBinding):
+ sd.location = e.location
+
+ self.setUpMethods(port)
+
+ self.setUpClassDef(service)
+ self.setUpInitDef(service)
+
+
+class WSAServiceModuleWriter(ServiceModuleWriter):
+ '''Creates a skeleton for a WS-Address service instance.
+ '''
+ def __init__(self, base=SimpleWSResource, prefix='wsa', service_class=SOAPService, strict=True):
+ '''
+ Parameters:
+ strict -- check that soapAction and input ws-action do not collide.
+ '''
+ ServiceModuleWriter.__init__(self, base, prefix, service_class)
+ self.strict = strict
+
+ def createMethodBody(msgInName, msgOutName, **kw):
+ '''return a tuple of strings containing the body of a method.
+ msgInName -- None or a str
+ msgOutName -- None or a str
+ '''
+ body = []
+ if msgInName is not None:
+ body.append('self.request = ps.Parse(%s.typecode)' %msgInName)
+
+ if msgOutName is not None:
+ body.append('return %s()' %msgOutName)
+ else:
+ body.append('return None')
+
+ return tuple(body)
+ createMethodBody = staticmethod(createMethodBody)
+
+ def setUpClassDef(self, service):
+ '''use soapAction dict for WS-Action input, setup wsAction
+ dict for grabbing WS-Action output values.
+ '''
+ assert isinstance(service, WSDLTools.Service), 'expecting WSDLTools.Service instance'
+
+ s = self._services[service.name].classdef
+ print >>s, 'class %s(%s):' %(self.getClassName(service.name), self._getBaseClassName())
+ print >>s, '%ssoapAction = {}' % self.getIndent(level=1)
+ print >>s, '%swsAction = {}' % self.getIndent(level=1)
+ print >>s, '%sroot = {}' % self.getIndent(level=1)
+
+ def setUpMethods(self, port):
+ '''set up all methods representing the port operations.
+ Parameters:
+ port -- Port that defines the operations.
+ '''
+ assert isinstance(port, WSDLTools.Port), \
+ 'expecting WSDLTools.Port not: ' %type(port)
+
+ binding = port.getBinding()
+ portType = port.getPortType()
+ service = port.getService()
+ s = self._services[service.name]
+ for bop in binding.operations:
+ try:
+ op = portType.operations[bop.name]
+ except KeyError, ex:
+ raise WsdlGeneratorError,\
+ 'Port(%s) PortType(%s) missing operation(%s) defined in Binding(%s)' \
+ %(port.name, portType.name, op.name, binding.name)
+
+ soap_action = wsaction_in = wsaction_out = None
+ if op.input is not None:
+ wsaction_in = op.getInputAction()
+ if op.output is not None:
+ wsaction_out = op.getOutputAction()
+
+ for ext in bop.extensions:
+ if isinstance(ext, WSDLTools.SoapOperationBinding) is False: continue
+ soap_action = ext.soapAction
+ if wsaction_in is None: break
+ if wsaction_in == soap_action: break
+ if self.strict is False:
+ warnings.warn(\
+ 'Port(%s) operation(%s) in Binding(%s) soapAction(%s) != WS-Action(%s)' \
+ %(port.name, op.name, binding.name, soap_action, wsaction_in),
+ )
+ break
+ raise WsdlGeneratorError,\
+ 'Port(%s) operation(%s) in Binding(%s) soapAction(%s) MUST match WS-Action(%s)' \
+ %(port.name, op.name, binding.name, soap_action, wsaction_in)
+
+ method_name = self.getMethodName(op.name)
+
+ m = s.newMethod()
+ print >>m, '%sdef %s(self, ps, address):' %(self.getIndent(level=1), method_name)
+
+ msgin_name = msgout_name = None
+ msgin,msgout = op.getInputMessage(),op.getOutputMessage()
+ if msgin is not None:
+ msgin_name = TextProtect(msgin.name)
+ if msgout is not None:
+ msgout_name = TextProtect(msgout.name)
+
+ indent = self.getIndent(level=2)
+ for l in self.createMethodBody(msgin_name, msgout_name):
+ print >>m, indent + l
+
+ print >>m, ''
+ print >>m, '%ssoapAction[\'%s\'] = \'%s\'' %(self.getIndent(level=1), wsaction_in, method_name)
+ print >>m, '%swsAction[\'%s\'] = \'%s\'' %(self.getIndent(level=1), method_name, wsaction_out)
+ print >>m, '%sroot[(%s.typecode.nspname,%s.typecode.pname)] = \'%s\'' \
+ %(self.getIndent(level=1), msgin_name, msgin_name, method_name)
+
+
+class DelAuthServiceModuleWriter(ServiceModuleWriter):
+ ''' Includes the generation of lines to call an authorization method on the server side
+ if an authorization function has been defined.
+ '''
+ def __init__(self, base=ServiceSOAPBinding, prefix='soap', service_class=SOAPService, do_extended=False):
+ ServiceModuleWriter.__init__(self, base=base, prefix=prefix, service_class=service_class, do_extended=do_extended)
+
+ def fromWSDL(self, wsdl):
+ ServiceModuleWriter.fromWSDL(self, wsdl)
+ for service in wsdl.services:
+ self.setUpAuthDef(service)
+
+ def setUpInitDef(self, service):
+ ServiceModuleWriter.setUpInitDef(self, service)
+ sd = self._services[service.name]
+ d = sd.initdef
+ print >>d, '%sif kw.has_key(\'impl\'):' % self.getIndent(level=2)
+ print >>d, '%sself.impl = kw[\'impl\']' % self.getIndent(level=3)
+
+ print >>d, '%sself.auth_method_name = None' % self.getIndent(level=2)
+ print >>d, '%sif kw.has_key(\'auth_method_name\'):' % self.getIndent(level=2)
+ print >>d, '%sself.auth_method_name = kw[\'auth_method_name\']' % self.getIndent(level=3)
+
+ def setUpAuthDef(self, service):
+ '''set __auth__ function
+ '''
+ sd = self._services[service.name]
+ e = sd.initdef
+ print >>e, "%sdef authorize(self, auth_info, post, action):" % self.getIndent(level=1)
+ print >>e, "%sif self.auth_method_name and hasattr(self.impl, self.auth_method_name):" % self.getIndent(level=2)
+ print >>e, "%sreturn getattr(self.impl, self.auth_method_name)(auth_info, post, action)" % self.getIndent(level=3)
+ print >>e, "%selse:" % self.getIndent(level=2)
+ print >>e, "%sreturn 1" % self.getIndent(level=3)
+
+class DelAuthWSAServiceModuleWriter(WSAServiceModuleWriter):
+ def __init__(self, base=SimpleWSResource, prefix='wsa', service_class=SOAPService, strict=True):
+ WSAServiceModuleWriter.__init__(self, base=base, prefix=prefix, service_class=service_class, strict=strict)
+
+ def fromWSDL(self, wsdl):
+ WSAServiceModuleWriter.fromWSDL(self, wsdl)
+ for service in wsdl.services:
+ self.setUpAuthDef(service)
+
+ def setUpInitDef(self, service):
+ WSAServiceModuleWriter.setUpInitDef(self, service)
+ sd = self._services[service.name]
+ d = sd.initdef
+ print >>d, '%sif kw.has_key(\'impl\'):' % self.getIndent(level=2)
+ print >>d, '%sself.impl = kw[\'impl\']' % self.getIndent(level=3)
+
+ print >>d, '%sif kw.has_key(\'auth_method_name\'):' % self.getIndent(level=2)
+ print >>d, '%sself.auth_method_name = kw[\'auth_method_name\']' % self.getIndent(level=3)
+
+ def setUpAuthDef(self, service):
+ '''set __auth__ function
+ '''
+ sd = self._services[service.name]
+ e = sd.initdef
+ print >>e, "%sdef authorize(self, auth_info, post, action):" % self.getIndent(level=1)
+ print >>e, "%sif self.auth_method_name and hasattr(self.impl, self.auth_method_name):" % self.getIndent(level=2)
+ print >>e, "%sreturn getattr(self.impl, self.auth_method_name)(auth_info, post, action)" % self.getIndent(level=3)
+ print >>e, "%selse:" % self.getIndent(level=2)
+ print >>e, "%sreturn 1" % self.getIndent(level=3)
+
diff --git a/ZSI/generate/wsdl2python.py b/ZSI/generate/wsdl2python.py
new file mode 100644
index 0000000..d028955
--- /dev/null
+++ b/ZSI/generate/wsdl2python.py
@@ -0,0 +1,536 @@
+############################################################################
+# Monte M. Goode, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+# main generator engine for new generation generator
+
+# $Id: wsdl2python.py 1203 2006-05-03 00:13:20Z boverhof $
+
+import os
+from ZSI import _get_idstr
+from ZSI.wstools.logging import getLogger as _GetLogger
+from ZSI.wstools import WSDLTools
+from ZSI.wstools.WSDLTools import SoapAddressBinding,\
+ SoapBodyBinding, SoapBinding,MimeContentBinding,\
+ HttpUrlEncodedBinding
+from ZSI.wstools.XMLSchema import SchemaReader, ElementDeclaration, SchemaError
+from ZSI.typeinterpreter import BaseTypeInterpreter
+from ZSI.generate import WsdlGeneratorError, Wsdl2PythonError
+from containers import *
+from ZSI.generate import utility
+from ZSI.generate.utility import NamespaceAliasDict as NAD
+from ZSI.generate.utility import GetModuleBaseNameFromWSDL
+
+"""
+classes:
+ WriteServiceModule
+ -- composes/writes out client stubs and types module.
+
+ ServiceDescription
+ -- represents a single WSDL service.
+
+ MessageWriter
+ -- represents a single WSDL Message and associated bindings
+ of the port/binding.
+
+ SchemaDescription
+ -- generates classes for defs and decs in the schema instance.
+
+ TypeWriter
+ -- represents a type definition.
+
+ ElementWriter
+ -- represents a element declaration.
+
+"""
+
+class WriteServiceModule:
+ """top level driver class invoked by wsd2py
+ class variables:
+ client_module_suffix -- suffix of client module.
+ types_module_suffix -- suffix of types module.
+ """
+ client_module_suffix = '_services'
+ messages_module_suffix = '_messages'
+ types_module_suffix = '_services_types'
+ logger = _GetLogger("WriteServiceModule")
+
+ def __init__(self, wsdl, addressing=False, notification=False,
+ do_extended=False, extPyClasses=None, configParser = None):
+ self._wsdl = wsdl
+ self._addressing = addressing
+ self._notification = notification
+ self._configParser = configParser
+ self.usedNamespaces = None
+ self.services = []
+ self.client_module_path = None
+ self.types_module_name = None
+ self.types_module_path = None
+ self.messages_module_path = None # used in extended generation
+ self.do_extended = do_extended
+ self.extPyClasses = extPyClasses
+
+ def getClientModuleName(self):
+ """client module name.
+ """
+ name = GetModuleBaseNameFromWSDL(self._wsdl)
+ if not name:
+ raise WsdlGeneratorError, 'could not determine a service name'
+
+ if self.client_module_suffix is None:
+ return name
+
+ return '%s%s' %(name, self.client_module_suffix)
+
+ def getMessagesModuleName(self):
+ name = GetModuleBaseNameFromWSDL(self._wsdl)
+ if not name:
+ raise WsdlGeneratorError, 'could not determine a service name'
+
+ if self.messages_module_suffix is None:
+ return name
+
+ if len(self.messages_module_suffix) == 0:
+ return self.getClientModuleName()
+
+ return '%s%s' %(name, self.messages_module_suffix)
+
+ def setTypesModuleName(self, name):
+ self.types_module_name = name
+
+ def getTypesModuleName(self):
+ """types module name.
+ """
+ if self.types_module_name is not None:
+ return self.types_module_name
+
+ name = GetModuleBaseNameFromWSDL(self._wsdl)
+ if not name:
+ raise WsdlGeneratorError, 'could not determine a service name'
+
+ if self.types_module_suffix is None:
+ return name
+
+ return '%s%s' %(name, self.types_module_suffix)
+
+ def setClientModulePath(self, path):
+ """setup module path to where client module before calling fromWsdl.
+ module path to types module eg. MyApp.client
+ """
+ self.client_module_path = path
+
+ def getTypesModulePath(self):
+ """module path to types module eg. MyApp.types
+ """
+ return self.types_module_path
+
+ def getMessagesModulePath(self):
+ '''module path to messages module
+ same as types path
+ '''
+ return self.messages_module_path
+
+ def setTypesModulePath(self, path):
+ """setup module path to where service module before calling fromWsdl.
+ module path to types module eg. MyApp.types
+ """
+ self.types_module_path = path
+
+ def setMessagesModulePath(self, path):
+ """setup module path to where message module before calling fromWsdl.
+ module path to types module eg. MyApp.types
+ """
+ self.messages_module_path = path
+
+ def gatherNamespaces(self):
+ '''This method must execute once.. Grab all schemas
+ representing each targetNamespace.
+ '''
+ if self.usedNamespaces is not None:
+ return
+
+ self.logger.debug('gatherNamespaces')
+ self.usedNamespaces = {}
+
+ # Add all schemas defined in wsdl
+ # to used namespace and to the Alias dict
+ for schema in self._wsdl.types.values():
+ tns = schema.getTargetNamespace()
+ self.logger.debug('Register schema(%s) -- TNS(%s)'\
+ %(_get_idstr(schema), tns),)
+ if self.usedNamespaces.has_key(tns) is False:
+ self.usedNamespaces[tns] = []
+ self.usedNamespaces[tns].append(schema)
+ NAD.add(tns)
+
+ # Add all xsd:import schema instances
+ # to used namespace and to the Alias dict
+ for k,v in SchemaReader.namespaceToSchema.items():
+ self.logger.debug('Register schema(%s) -- TNS(%s)'\
+ %(_get_idstr(v), k),)
+ if self.usedNamespaces.has_key(k) is False:
+ self.usedNamespaces[k] = []
+ self.usedNamespaces[k].append(v)
+ NAD.add(k)
+
+ def writeClient(self, fd, sdClass=None, msg_fd=None, **kw):
+ """write out client module to file descriptor.
+ Parameters and Keywords arguments:
+ fd -- file descriptor
+ msg_fd -- optional messsages module file descriptor
+ sdClass -- service description class name
+ imports -- list of imports
+ readerclass -- class name of ParsedSoap reader
+ writerclass -- class name of SoapWriter writer
+ """
+ sdClass = sdClass or ServiceDescription
+ assert issubclass(sdClass, ServiceDescription), \
+ 'parameter sdClass must subclass ServiceDescription'
+
+ header = '%s \n# %s.py \n# generated by %s\n%s\n\n'\
+ %('#'*50, self.getClientModuleName(), self.__module__, '#'*50)
+ fd.write(header)
+
+ if msg_fd is not None:
+ # TODO: Why is this not getMessagesModuleName?
+ msg_filename = str(self.getClientModuleName()).replace("client", "messages")
+ if self.getMessagesModulePath() is not None:
+ msg_filename = os.path.join(self.getMessagesModulePath(), msg_filename)
+ messages_header = header.replace("client", "messages")
+ msg_fd.write(messages_header)
+
+ self.services = []
+ for service in self._wsdl.services:
+ sd = sdClass(self._addressing, do_extended=self.do_extended, wsdl=self._wsdl)
+ if len(self._wsdl.types) > 0:
+ sd.setTypesModuleName(self.getTypesModuleName(), self.getTypesModulePath())
+ sd.setMessagesModuleName(self.getMessagesModuleName(), self.getMessagesModulePath())
+
+ self.gatherNamespaces()
+ sd.fromWsdl(service, **kw)
+ sd.write(fd,msg_fd)
+ self.services.append(sd)
+
+ def writeTypes(self, fd):
+ """write out types module to file descriptor.
+ """
+ header = '%s \n# %s.py \n# generated by %s\n%s\n\n'\
+ %('#'*50, self.getTypesModuleName(), self.__module__, '#'*50)
+ fd.write(header)
+ print >>fd, TypesHeaderContainer()
+ self.gatherNamespaces()
+ for l in self.usedNamespaces.values():
+ sd = SchemaDescription(do_extended=self.do_extended, extPyClasses=self.extPyClasses)
+ for schema in l:
+ sd.fromSchema(schema)
+ sd.write(fd)
+
+
+class ServiceDescription:
+ """client interface - locator, port, etc classes"""
+ separate_messages = False
+ logger = _GetLogger("ServiceDescription")
+
+ def __init__(self, addressing=False, do_extended=False, wsdl=None):
+ self.typesModuleName = None
+ self.messagesModuleName = None
+ self.wsAddressing = addressing
+ self.imports = ServiceHeaderContainer()
+ self.messagesImports = ServiceHeaderContainer()
+ self.locator = ServiceLocatorContainer()
+ self.methods = []
+ self.messages = []
+ self.do_extended=do_extended
+ self._wsdl = wsdl # None unless do_extended == True
+
+ def setTypesModuleName(self, name, modulePath=None):
+ """The types module to be imported.
+ Parameters
+ name -- name of types module
+ modulePath -- optional path where module is located.
+ """
+ self.typesModuleName = '%s' %name
+ if modulePath is not None:
+ self.typesModuleName = '%s.%s' %(modulePath,name)
+
+ def setMessagesModuleName(self, name, modulePath=None):
+ '''The types module to be imported.
+ Parameters
+ name -- name of types module
+ modulePath -- optional path where module is located.
+ '''
+ self.messagesModuleName = '%s' %name
+ if modulePath is not None:
+ self.messagesModuleName = '%s.%s' %(modulePath,name)
+
+ def fromWsdl(self, service, **kw):
+ self.imports.setTypesModuleName(self.typesModuleName)
+ if self.separate_messages:
+ self.messagesImports.setMessagesModuleName(self.messagesModuleName)
+ self.imports.appendImport(kw.get('imports', []))
+
+ self.locator.setUp(service)
+
+ for port in service.ports:
+ sop_container = ServiceOperationsClassContainer(useWSA=self.wsAddressing, do_extended=self.do_extended, wsdl=self._wsdl)
+ try:
+ sop_container.setUp(port)
+ except Wsdl2PythonError, ex:
+ self.logger.warning('Skipping port(%s)' %port.name)
+ if len(ex.args):
+ self.logger.warning(ex.args[0])
+ else:
+ sop_container.setReaderClass(kw.get('readerclass'))
+ sop_container.setWriterClass(kw.get('writerclass'))
+ for soc in sop_container.operations:
+ if soc.hasInput() is True:
+ mw = MessageWriter(do_extended=self.do_extended)
+ mw.setUp(soc, port, input=True)
+ self.messages.append(mw)
+
+ if soc.hasOutput() is True:
+ mw = MessageWriter(do_extended=self.do_extended)
+ mw.setUp(soc, port, input=False)
+ self.messages.append(mw)
+
+ self.methods.append(sop_container)
+
+
+ def write(self, fd, msg_fd=None):
+ """write out module to file descriptor.
+ fd -- file descriptor to write out service description.
+ msg_fd -- optional file descriptor for messages module.
+ """
+ if msg_fd != None:
+ print >>fd, self.messagesImports
+ print >>msg_fd, self.imports
+ else:
+ print >>fd, self.imports
+ print >>fd, self.locator
+ for m in self.methods:
+ print >>fd, m
+
+ if msg_fd != None:
+ for m in self.messages:
+ print >>msg_fd, m
+ else:
+ for m in self.messages:
+ print >>fd, m
+
+
+class MessageWriter:
+ logger = _GetLogger("MessageWriter")
+
+ def __init__(self, do_extended=False):
+ """Representation of a WSDL Message and associated WSDL Binding.
+ operation --
+ boperation --
+ input --
+ rpc --
+ literal --
+ simple --
+ """
+ self.content = None
+ self.do_extended = do_extended
+
+ def __str__(self):
+ if not self.content:
+ raise Wsdl2PythonError, 'Must call setUp.'
+ return self.content.getvalue()
+
+ def setUp(self, soc, port, input=False):
+ assert isinstance(soc, ServiceOperationContainer),\
+ 'expecting a ServiceOperationContainer instance'
+ assert isinstance(port, WSDLTools.Port),\
+ 'expecting a WSDL.Port instance'
+
+ rpc,literal = soc.isRPC(), soc.isLiteral(input)
+ kw,klass = {}, None
+
+ if rpc and literal:
+ klass = ServiceRPCLiteralMessageContainer
+ elif not rpc and literal:
+ kw['do_extended'] = self.do_extended
+ klass = ServiceDocumentLiteralMessageContainer
+ elif rpc and not literal:
+ klass = ServiceRPCEncodedMessageContainer
+ else:
+ raise WsdlGeneratorError, 'doc/enc not supported.'
+
+ self.content = klass(**kw)
+ self.content.setUp(port, soc, input)
+
+
+class SchemaDescription:
+ """generates classes for defs and decs in the schema instance.
+ """
+ logger = _GetLogger("SchemaDescription")
+
+ def __init__(self, do_extended=False, extPyClasses=None):
+ self.classHead = NamespaceClassHeaderContainer()
+ self.classFoot = NamespaceClassFooterContainer()
+ self.items = []
+ self.__types = []
+ self.__elements = []
+ self.targetNamespace = None
+ self.do_extended=do_extended
+ self.extPyClasses = extPyClasses
+
+ def fromSchema(self, schema):
+ ''' Can be called multiple times, but will not redefine a
+ previously defined type definition or element declaration.
+ '''
+ ns = schema.getTargetNamespace()
+ assert self.targetNamespace is None or self.targetNamespace == ns,\
+ 'SchemaDescription instance represents %s, not %s'\
+ %(self.targetNamespace, ns)
+
+ if self.targetNamespace is None:
+ self.targetNamespace = ns
+
+ self.classHead.ns = self.classFoot.ns = ns
+ for item in [t for t in schema.types if t.getAttributeName() not in self.__types]:
+ self.__types.append(item.getAttributeName())
+ self.items.append(TypeWriter(do_extended=self.do_extended, extPyClasses=self.extPyClasses))
+ self.items[-1].fromSchemaItem(item)
+
+ for item in [e for e in schema.elements if e.getAttributeName() not in self.__elements]:
+ self.__elements.append(item.getAttributeName())
+ self.items.append(ElementWriter(do_extended=self.do_extended))
+ self.items[-1].fromSchemaItem(item)
+
+ def getTypes(self):
+ return self.__types
+
+ def getElements(self):
+ return self.__elements
+
+ def write(self, fd):
+ """write out to file descriptor.
+ """
+ print >>fd, self.classHead
+ for t in self.items:
+ print >>fd, t
+ print >>fd, self.classFoot
+
+class SchemaItemWriter:
+ """contains/generates a single declaration"""
+ logger = _GetLogger("SchemaItemWriter")
+
+ def __init__(self, do_extended=False, extPyClasses=None):
+ self.content = None
+ self.do_extended=do_extended
+ self.extPyClasses=extPyClasses
+
+ def __str__(self):
+ '''this appears to set up whatever is in self.content.localElements,
+ local elements simpleType|complexType.
+ '''
+ assert self.content is not None, 'Must call fromSchemaItem to setup.'
+ return str(self.content)
+
+ def fromSchemaItem(self, item):
+ raise NotImplementedError, ''
+
+
+class ElementWriter(SchemaItemWriter):
+ """contains/generates a single declaration"""
+ logger = _GetLogger("ElementWriter")
+
+ def fromSchemaItem(self, item):
+ """set up global elements.
+ """
+ if item.isElement() is False or item.isLocal() is True:
+ raise TypeError, 'expecting global element declaration: %s' %item.getItemTrace()
+
+ local = False
+ qName = item.getAttribute('type')
+ if not qName:
+ etp = item.content
+ local = True
+ else:
+ etp = item.getTypeDefinition('type')
+
+ if etp is None:
+ if local is True:
+ self.content = ElementLocalComplexTypeContainer(do_extended=self.do_extended)
+ else:
+ self.content = ElementSimpleTypeContainer()
+ elif etp.isLocal() is False:
+ self.content = ElementGlobalDefContainer()
+ elif etp.isSimple() is True:
+ self.content = ElementLocalSimpleTypeContainer()
+ elif etp.isComplex():
+ self.content = ElementLocalComplexTypeContainer(do_extended=self.do_extended)
+ else:
+ raise Wsdl2PythonError, "Unknown element declaration: %s" %item.getItemTrace()
+
+ self.logger.debug('ElementWriter setUp container "%r", Schema Item "%s"' %(
+ self.content, item.getItemTrace()))
+
+ self.content.setUp(item)
+
+
+class TypeWriter(SchemaItemWriter):
+ """contains/generates a single definition"""
+ logger = _GetLogger("TypeWriter")
+
+ def fromSchemaItem(self, item):
+ if item.isDefinition() is False or item.isLocal() is True:
+ raise TypeError, \
+ 'expecting global type definition not: %s' %item.getItemTrace()
+
+ self.content = None
+ if item.isSimple():
+ if item.content.isRestriction():
+ self.content = RestrictionContainer()
+ elif item.content.isUnion():
+ self.content = UnionContainer()
+ elif item.content.isList():
+ self.content = ListContainer()
+ else:
+ raise Wsdl2PythonError,\
+ 'unknown simple type definition: %s' %item.getItemTrace()
+
+ self.content.setUp(item)
+ return
+
+ if item.isComplex():
+ kw = {}
+ if item.content is None or item.content.isModelGroup():
+ self.content = \
+ ComplexTypeContainer(\
+ do_extended=self.do_extended,
+ extPyClasses=self.extPyClasses
+ )
+ kw['empty'] = item.content is None
+ elif item.content.isSimple():
+ self.content = ComplexTypeSimpleContentContainer()
+ elif item.content.isComplex():
+ self.content = \
+ ComplexTypeComplexContentContainer(\
+ do_extended=self.do_extended
+ )
+ else:
+ raise Wsdl2PythonError,\
+ 'unknown complex type definition: %s' %item.getItemTrace()
+
+ self.logger.debug('TypeWriter setUp container "%r", Schema Item "%s"' %(
+ self.content, item.getItemTrace()))
+
+ try:
+ self.content.setUp(item, **kw)
+ except Exception, ex:
+ args = ['Failure in setUp: %s' %item.getItemTrace()]
+ args += ex.args
+ ex.args = tuple(args)
+ raise
+
+ return
+
+ raise TypeError,\
+ 'expecting SimpleType or ComplexType: %s' %item.getItemTrace()
+
+
+
diff --git a/ZSI/parse.py b/ZSI/parse.py
new file mode 100644
index 0000000..b559b09
--- /dev/null
+++ b/ZSI/parse.py
@@ -0,0 +1,375 @@
+#! /usr/bin/env python
+# $Header$
+'''SOAP messaging parsing.
+'''
+
+from ZSI import _copyright, _children, _attrs, _child_elements, _stringtypes, \
+ _backtrace, EvaluateException, ParseException, _valid_encoding, \
+ _Node, _find_attr, _resolve_prefix
+from ZSI.TC import AnyElement
+import types
+
+from ZSI.wstools.Namespaces import SOAP, XMLNS
+from ZSI.wstools.Utility import SplitQName
+
+_find_actor = lambda E: E.getAttributeNS(SOAP.ENV, "actor") or None
+_find_mu = lambda E: E.getAttributeNS(SOAP.ENV, "mustUnderstand")
+_find_root = lambda E: E.getAttributeNS(SOAP.ENC, "root")
+_find_id = lambda E: _find_attr(E, 'id')
+
+class ParsedSoap:
+ '''A Parsed SOAP object.
+ Convert the text to a DOM tree and parse SOAP elements.
+ Instance data:
+ reader -- the DOM reader
+ dom -- the DOM object
+ ns_cache -- dictionary (by id(node)) of namespace dictionaries
+ id_cache -- dictionary (by XML ID attr) of elements
+ envelope -- the node holding the SOAP Envelope
+ header -- the node holding the SOAP Header (or None)
+ body -- the node holding the SOAP Body
+ body_root -- the serialization root in the SOAP Body
+ data_elements -- list of non-root elements in the SOAP Body
+ trailer_elements -- list of elements following the SOAP body
+ '''
+ defaultReaderClass = None
+
+ def __init__(self, input, readerclass=None, keepdom=False,
+ trailers=False, resolver=None, envelope=True, **kw):
+ '''Initialize.
+ Keyword arguments:
+ trailers -- allow trailer elments (default is zero)
+ resolver -- function (bound method) to resolve URI's
+ readerclass -- factory class to create a reader
+ keepdom -- do not release the DOM
+ envelope -- look for a SOAP envelope.
+ '''
+
+ self.readerclass = readerclass
+ self.keepdom = keepdom
+ if not self.readerclass:
+ if self.defaultReaderClass != None:
+ self.readerclass = self.defaultReaderClass
+ else:
+ from xml.dom.ext.reader import PyExpat
+ self.readerclass = PyExpat.Reader
+ try:
+ self.reader = self.readerclass()
+ if type(input) in _stringtypes:
+ self.dom = self.reader.fromString(input)
+ else:
+ self.dom = self.reader.fromStream(input)
+ except Exception, e:
+ # Is this in the header? Your guess is as good as mine.
+ #raise ParseException("Can't parse document (" + \
+ # str(e.__class__) + "): " + str(e), 0)
+ raise
+
+ self.ns_cache = {
+ id(self.dom): {
+ 'xml': XMLNS.XML,
+ 'xmlns': XMLNS.BASE,
+ '': ''
+ }
+ }
+ self.trailers, self.resolver, self.id_cache = trailers, resolver, {}
+
+ # Exactly one child element
+ c = [ E for E in _children(self.dom)
+ if E.nodeType == _Node.ELEMENT_NODE]
+ if len(c) == 0:
+ raise ParseException("Document has no Envelope", 0)
+ if len(c) != 1:
+ raise ParseException("Document has extra child elements", 0)
+
+ if envelope is False:
+ self.body_root = c[0]
+ return
+
+ # And that one child must be the Envelope
+ elt = c[0]
+ if elt.localName != "Envelope" \
+ or elt.namespaceURI != SOAP.ENV:
+ raise ParseException('Document has "' + elt.localName + \
+ '" element, not Envelope', 0)
+ self._check_for_legal_children("Envelope", elt)
+ for a in _attrs(elt):
+ name = a.nodeName
+ if name.find(":") == -1 and name not in [ "xmlns", "id" ]:
+ raise ParseException('Unqualified attribute "' + \
+ name + '" in Envelope', 0)
+ self.envelope = elt
+ if not _valid_encoding(self.envelope):
+ raise ParseException("Envelope has invalid encoding", 0)
+
+ # Get Envelope's child elements.
+ c = [ E for E in _children(self.envelope)
+ if E.nodeType == _Node.ELEMENT_NODE ]
+ if len(c) == 0:
+ raise ParseException("Envelope is empty (no Body)", 0)
+
+ # Envelope's first child might be the header; if so, nip it off.
+ elt = c[0]
+ if elt.localName == "Header" \
+ and elt.namespaceURI == SOAP.ENV:
+ self._check_for_legal_children("Header", elt)
+ self._check_for_pi_nodes(_children(elt), 1)
+ self.header = c.pop(0)
+ self.header_elements = _child_elements(self.header)
+ else:
+ self.header, self.header_elements = None, []
+
+ # Now the first child must be the body
+ if len(c) == 0:
+ raise ParseException("Envelope has header but no Body", 0)
+ elt = c.pop(0)
+ if elt.localName != "Body" \
+ or elt.namespaceURI != SOAP.ENV:
+ if self.header:
+ raise ParseException('Header followed by "' + \
+ elt.localName + \
+ '" element, not Body', 0, elt, self.dom)
+ else:
+ raise ParseException('Document has "' + \
+ elt.localName + \
+ '" element, not Body', 0, elt, self.dom)
+ self._check_for_legal_children("Body", elt, 0)
+ self._check_for_pi_nodes(_children(elt), 0)
+ self.body = elt
+ if not _valid_encoding(self.body):
+ raise ParseException("Body has invalid encoding", 0)
+
+ # Trailer elements.
+ if not self.trailers:
+ if len(c):
+ raise ParseException("Element found after Body",
+ 0, elt, self.dom)
+ # Don't set self.trailer_elements = []; if user didn't ask
+ # for trailers we *want* to throw an exception.
+ else:
+ self.trailer_elements = c
+ for elt in self.trailer_elements:
+ if not elt.namespaceURI:
+ raise ParseException('Unqualified trailer element',
+ 0, elt, self.dom)
+
+ # Find the serialization root. Divide the Body children into
+ # root (root=1), no (root=0), maybe (no root attribute).
+ self.body_root, no, maybe = None, [], []
+ for elt in _child_elements(self.body):
+ root = _find_root(elt)
+ if root == "1":
+ if self.body_root:
+ raise ParseException("Multiple seralization roots found",
+ 0, elt, self.dom)
+ self.body_root = elt
+ elif root == "0":
+ no.append(elt)
+ elif not root:
+ maybe.append(elt)
+ else:
+ raise ParseException('Illegal value for root attribute',
+ 0, elt, self.dom)
+
+ # If we didn't find a root, get the first one that didn't
+ # say "not me", unless they all said "not me."
+ if self.body_root is None:
+ if len(maybe):
+ self.body_root = maybe[0]
+ else:
+ raise ParseException('No serialization root found',
+ 0, self.body, self.dom)
+ if not _valid_encoding(self.body_root):
+ raise ParseException("Invalid encoding", 0,
+ elt, self.dom)
+
+ # Now get all the non-roots (in order!).
+ rootid = id(self.body_root)
+ self.data_elements = [ E for E in _child_elements(self.body)
+ if id(E) != rootid ]
+ self._check_for_pi_nodes(self.data_elements, 0)
+
+ def __del__(self):
+ try:
+ if not self.keepdom:
+ self.reader.releaseNode(self.dom)
+ except:
+ pass
+
+ def _check_for_legal_children(self, name, elt, mustqualify=1):
+ '''Check if all children of this node are elements or whitespace-only
+ text nodes.
+ '''
+ inheader = name == "Header"
+ for n in _children(elt):
+ t = n.nodeType
+ if t == _Node.COMMENT_NODE: continue
+ if t != _Node.ELEMENT_NODE:
+ if t == _Node.TEXT_NODE and n.nodeValue.strip() == "":
+ continue
+ raise ParseException("Non-element child in " + name,
+ inheader, elt, self.dom)
+ if mustqualify and not n.namespaceURI:
+ raise ParseException('Unqualified element "' + \
+ n.nodeName + '" in ' + name, inheader, elt, self.dom)
+
+ def _check_for_pi_nodes(self, list, inheader):
+ '''Raise an exception if any of the list descendants are PI nodes.
+ '''
+ list = list[:]
+ while list:
+ elt = list.pop()
+ t = elt.nodeType
+ if t == _Node.PROCESSING_INSTRUCTION_NODE:
+ raise ParseException('Found processing instruction "<?' + \
+ elt.nodeName + '...>"',
+ inheader, elt.parentNode, self.dom)
+ elif t == _Node.DOCUMENT_TYPE_NODE:
+ raise ParseException('Found DTD', inheader,
+ elt.parentNode, self.dom)
+ list += _children(elt)
+
+ def Backtrace(self, elt):
+ '''Return a human-readable "backtrace" from the document root to
+ the specified element.
+ '''
+ return _backtrace(elt, self.dom)
+
+ def FindLocalHREF(self, href, elt, headers=1):
+ '''Find a local HREF in the data elements.
+ '''
+ if href[0] != '#':
+ raise EvaluateException(
+ 'Absolute HREF ("%s") not implemented' % href,
+ self.Backtrace(elt))
+ frag = href[1:]
+ # Already found?
+ e = self.id_cache.get(frag)
+ if e: return e
+ # Do a breadth-first search, in the data first. Most likely
+ # to find multi-ref targets shallow in the data area.
+ list = self.data_elements[:] + [self.body_root]
+ if headers: list.extend(self.header_elements)
+ while list:
+ e = list.pop()
+ if e.nodeType == _Node.ELEMENT_NODE:
+ nodeid = _find_id(e)
+ if nodeid:
+ self.id_cache[nodeid] = e
+ if nodeid == frag: return e
+ list += _children(e)
+ raise EvaluateException('''Can't find node for HREF "%s"''' % href,
+ self.Backtrace(elt))
+
+ def ResolveHREF(self, uri, tc, **keywords):
+ r = getattr(tc, 'resolver', self.resolver)
+ if not r:
+ raise EvaluateException('No resolver for "' + uri + '"')
+ try:
+ if type(uri) == types.UnicodeType: uri = str(uri)
+ retval = r(uri, tc, self, **keywords)
+ except Exception, e:
+ raise EvaluateException('''Can't resolve "''' + uri + '" (' + \
+ str(e.__class__) + "): " + str(e))
+ return retval
+
+ def GetMyHeaderElements(self, actorlist=None):
+ '''Return a list of all elements intended for these actor(s).
+ '''
+ if actorlist is None:
+ actorlist = [None, SOAP.ACTOR_NEXT]
+ else:
+ actorlist = list(actorlist) + [None, SOAP.ACTOR_NEXT]
+ return [ E for E in self.header_elements
+ if _find_actor(E) in actorlist ]
+
+ def GetElementNSdict(self, elt):
+ '''Get a dictionary of all the namespace attributes for the indicated
+ element. The dictionaries are cached, and we recurse up the tree
+ as necessary.
+ '''
+ d = self.ns_cache.get(id(elt))
+ if not d:
+ if elt != self.dom: d = self.GetElementNSdict(elt.parentNode)
+ for a in _attrs(elt):
+ if a.namespaceURI == XMLNS.BASE:
+ if a.localName == "xmlns":
+ d[''] = a.nodeValue
+ else:
+ d[a.localName] = a.nodeValue
+ self.ns_cache[id(elt)] = d
+ return d.copy()
+
+ def GetDomAndReader(self):
+ '''Returns a tuple containing the dom and reader objects. (dom, reader)
+ Unless keepdom is true, the dom and reader objects will go out of scope
+ when the ParsedSoap instance is deleted. If keepdom is true, the reader
+ object is needed to properly clean up the dom tree with
+ reader.releaseNode(dom).
+ '''
+ return (self.dom, self.reader)
+
+ def IsAFault(self):
+ '''Is this a fault message?
+ '''
+ e = self.body_root
+ if not e: return 0
+ return e.namespaceURI == SOAP.ENV and e.localName == 'Fault'
+
+ def Parse(self, how):
+ '''Parse the message.
+ '''
+ if type(how) == types.ClassType: how = how.typecode
+ return how.parse(self.body_root, self)
+
+ def WhatMustIUnderstand(self):
+ '''Return a list of (uri,localname) tuples for all elements in the
+ header that have mustUnderstand set.
+ '''
+ return [ ( E.namespaceURI, E.localName )
+ for E in self.header_elements if _find_mu(E) == "1" ]
+
+ def WhatActorsArePresent(self):
+ '''Return a list of URI's of all the actor attributes found in
+ the header. The special actor "next" is ignored.
+ '''
+ results = []
+ for E in self.header_elements:
+ a = _find_actor(E)
+ if a not in [ None, SOAP.ACTOR_NEXT ]: results.append(a)
+ return results
+
+ def ParseHeaderElements(self, ofwhat):
+ '''Returns a dictionary of pyobjs.
+ ofhow -- list of typecodes w/matching nspname/pname to the header_elements.
+ '''
+ d = {}
+ lenofwhat = len(ofwhat)
+ c, crange = self.header_elements[:], range(len(self.header_elements))
+ for i,what in [ (i, ofwhat[i]) for i in range(lenofwhat) ]:
+ if isinstance(what, AnyElement):
+ raise EvaluateException, 'not supporting <any> as child of SOAP-ENC:Header'
+
+ v = []
+ occurs = 0
+ namespaceURI,tagName = what.nspname,what.pname
+ for j,c_elt in [ (j, c[j]) for j in crange if c[j] ]:
+ prefix,name = SplitQName(c_elt.tagName)
+ nsuri = _resolve_prefix(c_elt, prefix)
+ if tagName == name and namespaceURI == nsuri:
+ pyobj = what.parse(c_elt, self)
+ else:
+ continue
+ v.append(pyobj)
+ c[j] = None
+ if what.minOccurs > len(v) > what.maxOccurs:
+ raise EvaluateException, 'number of occurances(%d) doesnt fit constraints (%d,%s)'\
+ %(len(v),what.minOccurs,what.maxOccurs)
+ if what.maxOccurs == 1:
+ if len(v) == 0: v = None
+ else: v = v[0]
+ d[(what.nspname,what.pname)] = v
+ return d
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/resolvers.py b/ZSI/resolvers.py
new file mode 100644
index 0000000..33a182a
--- /dev/null
+++ b/ZSI/resolvers.py
@@ -0,0 +1,147 @@
+#! /usr/bin/env python
+# $Header$
+'''SOAP messaging parsing.
+'''
+
+from ZSI import _copyright, _child_elements, EvaluateException, TC
+import multifile, mimetools, urllib
+from base64 import decodestring as b64decode
+import cStringIO as StringIO
+
+
+def Opaque(uri, tc, ps, **keywords):
+ '''Resolve a URI and return its content as a string.
+ '''
+ source = urllib.urlopen(uri, **keywords)
+ enc = source.info().getencoding()
+ if enc in ['7bit', '8bit', 'binary']: return source.read()
+
+ data = StringIO.StringIO()
+ mimetools.decode(source, data, enc)
+ return data.getvalue()
+
+
+def XML(uri, tc, ps, **keywords):
+ '''Resolve a URI and return its content as an XML DOM.
+ '''
+ source = urllib.urlopen(uri, **keywords)
+ enc = source.info().getencoding()
+ if enc in ['7bit', '8bit', 'binary']:
+ data = source
+ else:
+ data = StringIO.StringIO()
+ mimetools.decode(source, data, enc)
+ data.seek(0)
+ dom = ps.readerclass().fromStream(data)
+ return _child_elements(dom)[0]
+
+
+class NetworkResolver:
+ '''A resolver that support string and XML.
+ '''
+
+ def __init__(self, prefix=None):
+ self.allowed = prefix or []
+
+ def _check_allowed(self, uri):
+ for a in self.allowed:
+ if uri.startswith(a): return
+ raise EvaluateException("Disallowed URI prefix")
+
+ def Opaque(self, uri, tc, ps, **keywords):
+ self._check_allowed(uri)
+ return Opaque(uri, tc, ps, **keywords)
+
+ def XML(self, uri, tc, ps, **keywords):
+ self._check_allowed(uri)
+ return XML(uri, tc, ps, **keywords)
+
+ def Resolve(self, uri, tc, ps, **keywords):
+ if isinstance(tc, TC.XML):
+ return XML(uri, tc, ps, **keywords)
+ return Opaque(uri, tc, ps, **keywords)
+
+
+class MIMEResolver:
+ '''Multi-part MIME resolver -- SOAP With Attachments, mostly.
+ '''
+
+ def __init__(self, ct, f, next=None, uribase='thismessage:/',
+ seekable=0, **kw):
+ # Get the boundary. It's too bad I have to write this myself,
+ # but no way am I going to import cgi for 10 lines of code!
+ for param in ct.split(';'):
+ a = param.strip()
+ if a.startswith('boundary='):
+ if a[9] in [ '"', "'" ]:
+ boundary = a[10:-1]
+ else:
+ boundary = a[9:]
+ break
+ else:
+ raise ValueError('boundary parameter not found')
+
+ self.id_dict, self.loc_dict, self.parts = {}, {}, []
+ self.next = next
+ self.base = uribase
+
+ mf = multifile.MultiFile(f, seekable)
+ mf.push(boundary)
+ while mf.next():
+ head = mimetools.Message(mf)
+ body = StringIO.StringIO()
+ mimetools.decode(mf, body, head.getencoding())
+ body.seek(0)
+ part = (head, body)
+ self.parts.append(part)
+ key = head.get('content-id')
+ if key:
+ if key[0] == '<' and key[-1] == '>': key = key[1:-1]
+ self.id_dict[key] = part
+ key = head.get('content-location')
+ if key: self.loc_dict[key] = part
+ mf.pop()
+
+ def GetSOAPPart(self):
+ '''Get the SOAP body part.
+ '''
+ head, part = self.parts[0]
+ return StringIO.StringIO(part.getvalue())
+
+ def get(self, uri):
+ '''Get the content for the bodypart identified by the uri.
+ '''
+ if uri.startswith('cid:'):
+ # Content-ID, so raise exception if not found.
+ head, part = self.id_dict[uri[4:]]
+ return StringIO.StringIO(part.getvalue())
+ if self.loc_dict.has_key(uri):
+ head, part = self.loc_dict[uri]
+ return StringIO.StringIO(part.getvalue())
+ return None
+
+ def Opaque(self, uri, tc, ps, **keywords):
+ content = self.get(uri)
+ if content: return content.getvalue()
+ if not self.next: raise EvaluateException("Unresolvable URI " + uri)
+ return self.next.Opaque(uri, tc, ps, **keywords)
+
+ def XML(self, uri, tc, ps, **keywords):
+ content = self.get(uri)
+ if content:
+ dom = ps.readerclass().fromStream(content)
+ return _child_elements(dom)[0]
+ if not self.next: raise EvaluateException("Unresolvable URI " + uri)
+ return self.next.XML(uri, tc, ps, **keywords)
+
+ def Resolve(self, uri, tc, ps, **keywords):
+ if isinstance(tc, TC.XML):
+ return self.XML(uri, tc, ps, **keywords)
+ return self.Opaque(uri, tc, ps, **keywords)
+
+ def __getitem__(self, cid):
+ head, body = self.id_dict[cid]
+ newio = StringIO.StringIO(body.getvalue())
+ return newio
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/schema.py b/ZSI/schema.py
new file mode 100644
index 0000000..fa16dd9
--- /dev/null
+++ b/ZSI/schema.py
@@ -0,0 +1,327 @@
+#! /usr/bin/env python
+# $Header$
+'''XML Schema support
+'''
+
+from ZSI import _copyright, _seqtypes, _find_type, EvaluateException
+from ZSI.wstools.Namespaces import SCHEMA, SOAP
+from ZSI.wstools.Utility import SplitQName
+
+
+def _get_type_definition(namespaceURI, name, **kw):
+ return SchemaInstanceType.getTypeDefinition(namespaceURI, name, **kw)
+
+def _get_global_element_declaration(namespaceURI, name, **kw):
+ return SchemaInstanceType.getElementDeclaration(namespaceURI, name, **kw)
+
+def _get_substitute_element(elt, what):
+ raise NotImplementedError, 'Not implemented'
+
+def _has_type_definition(namespaceURI, name):
+ return SchemaInstanceType.getTypeDefinition(namespaceURI, name) is not None
+
+
+#
+# functions for retrieving schema items from
+# the global schema instance.
+#
+GED = _get_global_element_declaration
+GTD = _get_type_definition
+
+
+def WrapImmutable(pyobj, what):
+ '''Wrap immutable instance so a typecode can be
+ set, making it self-describing ie. serializable.
+ '''
+ return _GetPyobjWrapper.WrapImmutable(pyobj, what)
+
+def RegisterBuiltin(arg):
+ '''Add a builtin to be registered, and register it
+ with the Any typecode.
+ '''
+ _GetPyobjWrapper.RegisterBuiltin(arg)
+ _GetPyobjWrapper.RegisterAnyElement()
+
+def RegisterAnyElement():
+ '''register all Wrapper classes with the Any typecode.
+ This allows instances returned by Any to be self-describing.
+ ie. serializable. AnyElement falls back on Any to parse
+ anything it doesn't understand.
+ '''
+ return _GetPyobjWrapper.RegisterAnyElement()
+
+
+class SchemaInstanceType(type):
+ '''Register all types/elements, when hit already defined
+ class dont create a new one just give back reference. Thus
+ import order determines which class is loaded.
+
+ class variables:
+ types -- dict of typecode classes definitions
+ representing global type definitions.
+ elements -- dict of typecode classes representing
+ global element declarations.
+ element_typecode_cache -- dict of typecode instances
+ representing global element declarations.
+ '''
+ types = {}
+ elements = {}
+ element_typecode_cache = {}
+
+ def __new__(cls,classname,bases,classdict):
+ '''If classdict has literal and schema register it as a
+ element declaration, else if has type and schema register
+ it as a type definition.
+ '''
+ if classname in ['ElementDeclaration', 'TypeDefinition', 'LocalElementDeclaration',]:
+ return type.__new__(cls,classname,bases,classdict)
+
+ if ElementDeclaration in bases:
+ if classdict.has_key('schema') is False or classdict.has_key('literal') is False:
+ raise AttributeError, 'ElementDeclaration must define schema and literal attributes'
+
+ key = (classdict['schema'],classdict['literal'])
+ if SchemaInstanceType.elements.has_key(key) is False:
+ SchemaInstanceType.elements[key] = type.__new__(cls,classname,bases,classdict)
+ return SchemaInstanceType.elements[key]
+
+ if TypeDefinition in bases:
+ if classdict.has_key('type') is None:
+ raise AttributeError, 'TypeDefinition must define type attribute'
+
+ key = classdict['type']
+ if SchemaInstanceType.types.has_key(key) is False:
+ SchemaInstanceType.types[key] = type.__new__(cls,classname,bases,classdict)
+ return SchemaInstanceType.types[key]
+
+ if LocalElementDeclaration in bases:
+ return type.__new__(cls,classname,bases,classdict)
+
+ raise TypeError, 'SchemaInstanceType must be an ElementDeclaration or TypeDefinition '
+
+ def getTypeDefinition(cls, namespaceURI, name, lazy=False):
+ '''Grab a type definition, returns a typecode class definition
+ because the facets (name, minOccurs, maxOccurs) must be provided.
+
+ Parameters:
+ namespaceURI --
+ name --
+ '''
+ klass = cls.types.get((namespaceURI, name), None)
+ if lazy and klass is not None:
+ return _Mirage(klass)
+ return klass
+ getTypeDefinition = classmethod(getTypeDefinition)
+
+ def getElementDeclaration(cls, namespaceURI, name, isref=False, lazy=False):
+ '''Grab an element declaration, returns a typecode instance
+ representation or a typecode class definition. An element
+ reference has its own facets, and is local so it will not be
+ cached.
+
+ Parameters:
+ namespaceURI --
+ name --
+ isref -- if element reference, return class definition.
+ '''
+ key = (namespaceURI, name)
+ if isref:
+ klass = cls.elements.get(key,None)
+ if klass is not None and lazy is True:
+ return _Mirage(klass)
+ return klass
+
+ typecode = cls.element_typecode_cache.get(key, None)
+ if typecode is None:
+ tcls = cls.elements.get(key,None)
+ if tcls is not None:
+ typecode = cls.element_typecode_cache[key] = tcls()
+ typecode.typed = False
+
+ return typecode
+ getElementDeclaration = classmethod(getElementDeclaration)
+
+
+class ElementDeclaration:
+ '''Typecodes subclass to represent a Global Element Declaration by
+ setting class variables schema and literal.
+
+ schema = namespaceURI
+ literal = NCName
+ '''
+ __metaclass__ = SchemaInstanceType
+
+
+class LocalElementDeclaration:
+ '''Typecodes subclass to represent a Local Element Declaration.
+ '''
+ __metaclass__ = SchemaInstanceType
+
+
+class TypeDefinition:
+ '''Typecodes subclass to represent a Global Type Definition by
+ setting class variable type.
+
+ type = (namespaceURI, NCName)
+ '''
+ __metaclass__ = SchemaInstanceType
+
+ def getSubstituteType(self, elt, ps):
+ '''if xsi:type does not match the instance type attr,
+ check to see if it is a derived type substitution.
+
+ DONT Return the element's type.
+
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ pyclass = SchemaInstanceType.getTypeDefinition(*self.type)
+ if pyclass is None:
+ raise EvaluateException(
+ 'No Type registed for xsi:type=(%s, %s)' %
+ (self.type[0], self.type[1]), ps.Backtrace(elt))
+
+ typeName = _find_type(elt)
+ prefix,typeName = SplitQName(typeName)
+ uri = ps.GetElementNSdict(elt).get(prefix)
+ subclass = SchemaInstanceType.getTypeDefinition(uri, typeName)
+ if subclass is None:
+ raise EvaluateException(
+ 'No registered xsi:type=(%s, %s), substitute for xsi:type=(%s, %s)' %
+ (uri, typeName, self.type[0], self.type[1]), ps.Backtrace(elt))
+
+ if not issubclass(subclass, pyclass) and subclass(None) and not issubclass(subclass, pyclass):
+ raise TypeError(
+ 'Substitute Type (%s, %s) is not derived from %s' %
+ (self.type[0], self.type[1], pyclass), ps.Backtrace(elt))
+
+ return subclass((self.nspname, self.pname))
+
+
+
+class _Mirage:
+ '''Used with SchemaInstanceType for lazy evaluation, eval during serialize or
+ parse as needed. Mirage is callable, TypeCodes are not. When called it returns the
+ typecode. Tightly coupled with generated code.
+
+ NOTE: **Must Use ClassType** for intended MRO of __call__ since setting it in
+ an instance attribute rather than a class attribute (will not work for object).
+ '''
+ def __init__(self, klass):
+ self.klass = klass
+ self.__reveal = False
+ self.__cache = None
+ if issubclass(klass, ElementDeclaration):
+ self.__call__ = self._hide_element
+
+ def __str__(self):
+ msg = "<Mirage id=%s, Local Element %s>"
+ if issubclass(self.klass, ElementDeclaration):
+ msg = "<Mirage id=%s, GED %s>"
+ return msg %(id(self), self.klass)
+
+ def _hide_type(self, pname, aname, minOccurs=0, maxOccurs=1, nillable=False,
+ **kw):
+ self.__call__ = self._reveal_type
+ self.__reveal = True
+
+ # store all attributes, make some visable for pyclass_type
+ self.__kw = kw
+ self.minOccurs,self.maxOccurs,self.nillable = minOccurs,maxOccurs,nillable
+ self.nspname,self.pname,self.aname = None,pname,aname
+ if type(self.pname) in (tuple,list):
+ self.nspname,self.pname = pname
+
+ return self
+
+ def _hide_element(self, minOccurs=0, maxOccurs=1, nillable=False, **kw):
+ self.__call__ = self._reveal_element
+ self.__reveal = True
+
+ # store all attributes, make some visable for pyclass_type
+ self.__kw = kw
+ self.nspname = self.klass.schema
+ self.pname = self.klass.literal
+ #TODO: Fix hack
+ #self.aname = '_%s' %self.pname
+ self.minOccurs,self.maxOccurs,self.nillable = minOccurs,maxOccurs,nillable
+
+ return self
+
+ def _reveal_type(self):
+ if self.__cache is None:
+ self.__cache = self.klass(pname=self.pname,
+ aname=self.aname, minOccurs=self.minOccurs,
+ maxOccurs=self.maxOccurs, nillable=self.nillable,
+ **self.__kw)
+ return self.__cache
+
+ def _reveal_element(self):
+ if self.__cache is None:
+ self.__cache = self.klass(minOccurs=self.minOccurs,
+ maxOccurs=self.maxOccurs, nillable=self.nillable,
+ **self.__kw)
+ return self.__cache
+
+ __call__ = _hide_type
+
+
+class _GetPyobjWrapper:
+ '''Get a python object that wraps data and typecode. Used by
+ <any> parse routine, so that typecode information discovered
+ during parsing is retained in the pyobj representation
+ and thus can be serialized.
+ '''
+ types_dict = {}
+
+ def RegisterBuiltin(cls, arg):
+ '''register a builtin, create a new wrapper.
+ '''
+ if arg in cls.types_dict:
+ raise RuntimeError, '%s already registered' %arg
+ class _Wrapper(arg):
+ 'Wrapper for builtin %s\n%s' %(arg, cls.__doc__)
+ _Wrapper.__name__ = '_%sWrapper' %arg.__name__
+ cls.types_dict[arg] = _Wrapper
+ RegisterBuiltin = classmethod(RegisterBuiltin)
+
+ def RegisterAnyElement(cls):
+ '''If find registered TypeCode instance, add Wrapper class
+ to TypeCode class serialmap and Re-RegisterType. Provides
+ Any serialzation of any instances of the Wrapper.
+ '''
+ for k,v in cls.types_dict.items():
+ what = Any.serialmap.get(k)
+ if what is None: continue
+ if v in what.__class__.seriallist: continue
+ what.__class__.seriallist.append(v)
+ RegisterType(what.__class__, clobber=1, **what.__dict__)
+ RegisterAnyElement = classmethod(RegisterAnyElement)
+
+ def WrapImmutable(cls, pyobj, what):
+ '''return a wrapper for pyobj, with typecode attribute set.
+ Parameters:
+ pyobj -- instance of builtin type (immutable)
+ what -- typecode describing the data
+ '''
+ d = cls.types_dict
+ if type(pyobj) is bool:
+ pyclass = d[int]
+ elif d.has_key(type(pyobj)) is True:
+ pyclass = d[type(pyobj)]
+ else:
+ raise TypeError,\
+ 'Expecting a built-in type in %s (got %s).' %(
+ d.keys(),type(pyobj))
+
+ newobj = pyclass(pyobj)
+ newobj.typecode = what
+ return newobj
+ WrapImmutable = classmethod(WrapImmutable)
+
+
+from TC import Any, RegisterType
+
+if __name__ == '__main__': print _copyright
+
diff --git a/ZSI/twisted/WSresource.py b/ZSI/twisted/WSresource.py
new file mode 100644
index 0000000..2adf67b
--- /dev/null
+++ b/ZSI/twisted/WSresource.py
@@ -0,0 +1,355 @@
+###########################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+# $Id: WSresource.py 1172 2006-04-04 05:01:27Z boverhof $
+###########################################################################
+
+import sys, warnings
+
+# twisted & related imports
+from zope.interface import classProvides, implements, Interface
+from twisted.python import log, failure
+from twisted.web.error import NoResource
+from twisted.web.server import NOT_DONE_YET
+import twisted.web.http
+import twisted.web.resource
+
+# ZSI imports
+from ZSI import _get_element_nsuri_name, EvaluateException, ParseException
+from ZSI.parse import ParsedSoap
+from ZSI.writer import SoapWriter
+from ZSI import fault
+
+# WS-Address related imports
+from ZSI.address import Address
+from ZSI.ServiceContainer import WSActionException
+
+
+#
+# Stability: Unstable
+#
+
+class HandlerChainInterface(Interface):
+
+ def processRequest(self, input, **kw):
+ """returns a representation of the request, the
+ last link in the chain must return a response
+ pyobj with a typecode attribute.
+ Parameters:
+ input --
+ Keyword Parameters:
+ request -- HTTPRequest instance
+ resource -- Resource instance
+ """
+ def processResponse(self, output, **kw):
+ """returns a string representing the soap response.
+ Parameters
+ output --
+ Keyword Parameters:
+ request -- HTTPRequest instance
+ resource -- Resource instance
+ """
+
+class CallbackChainInterface(Interface):
+
+ def processRequest(self, input, **kw):
+ """returns a response pyobj with a typecode
+ attribute.
+ Parameters:
+ input --
+ Keyword Parameters:
+ request -- HTTPRequest instance
+ resource -- Resource instance
+ """
+
+class DataHandler:
+ """
+ class variables:
+ readerClass -- factory class to create reader for ParsedSoap instances.
+ writerClass -- ElementProxy implementation to use for SoapWriter instances.
+ """
+ classProvides(HandlerChainInterface)
+ readerClass = None
+ writerClass = None
+
+ @classmethod
+ def processRequest(cls, input, **kw):
+ return ParsedSoap(input, readerclass=cls.readerClass)
+
+ @classmethod
+ def processResponse(cls, output, **kw):
+ sw = SoapWriter(outputclass=cls.writerClass)
+ sw.serialize(output)
+ return sw
+
+
+class DefaultCallbackHandler:
+ classProvides(CallbackChainInterface)
+
+ @classmethod
+ def processRequest(cls, ps, **kw):
+ """invokes callback that should return a (request,response) tuple.
+ representing the SOAP request and response respectively.
+ ps -- ParsedSoap instance representing HTTP Body.
+ request -- twisted.web.server.Request
+ """
+ resource = kw['resource']
+ request = kw['request']
+ method = getattr(resource, 'soap_%s' %
+ _get_element_nsuri_name(ps.body_root)[-1])
+
+ try:
+ req_pyobj,rsp_pyobj = method(ps, request=request)
+ except TypeError, ex:
+ log.err(
+ 'ERROR: service %s is broken, method MUST return request, response'\
+ % cls.__name__
+ )
+ raise
+ except Exception, ex:
+ log.err('failure when calling bound method')
+ raise
+
+ return rsp_pyobj
+
+
+class WSAddressHandler:
+ """General WS-Address handler. This implementation depends on a
+ 'wsAction' dictionary in the service stub which contains keys to
+ WS-Action values.
+
+ Implementation saves state on request response flow, so using this
+ handle is not reliable if execution is deferred between proceesRequest
+ and processResponse.
+
+ TODO: sink this up with wsdl2dispatch
+ TODO: reduce coupling with WSAddressCallbackHandler.
+ """
+ implements(HandlerChainInterface)
+
+ def processRequest(self, ps, **kw):
+ # TODO: Clean this up
+ resource = kw['resource']
+
+ d = getattr(resource, 'root', None)
+ key = _get_element_nsuri_name(ps.body_root)
+ if d is None or d.has_key(key) is False:
+ raise RuntimeError,\
+ 'Error looking for key(%s) in root dictionary(%s)' %(key, str(d))
+
+ self.op_name = d[key]
+ self.address = address = Address()
+ address.parse(ps)
+ action = address.getAction()
+ if not action:
+ raise WSActionException('No WS-Action specified in Request')
+
+ request = kw['request']
+ http_headers = request.getAllHeaders()
+ soap_action = http_headers.get('soapaction')
+ if soap_action and soap_action.strip('\'"') != action:
+ raise WSActionException(\
+ 'SOAP Action("%s") must match WS-Action("%s") if specified.'\
+ %(soap_action,action)
+ )
+
+ # Save WS-Address in ParsedSoap instance.
+ ps.address = address
+ return ps
+
+ def processResponse(self, sw, **kw):
+ if sw is None:
+ self.address = None
+ return
+
+ request, resource = kw['request'], kw['resource']
+ if isinstance(request, twisted.web.http.Request) is False:
+ raise TypeError, '%s instance expected' %http.Request
+
+ d = getattr(resource, 'wsAction', None)
+ key = self.op_name
+ if d is None or d.has_key(key) is False:
+ raise WSActionNotSpecified,\
+ 'Error looking for key(%s) in wsAction dictionary(%s)' %(key, str(d))
+
+ addressRsp = Address(action=d[key])
+ if request.transport.TLS == 0:
+ addressRsp.setResponseFromWSAddress(\
+ self.address, 'http://%s:%d%s' %(
+ request.host.host, request.host.port, request.path)
+ )
+ else:
+ addressRsp.setResponseFromWSAddress(\
+ self.address, 'https://%s:%d%s' %(
+ request.host.host, request.host.port, request.path)
+ )
+
+ addressRsp.serialize(sw, typed=False)
+ self.address = None
+ return sw
+
+
+class WSAddressCallbackHandler:
+ classProvides(CallbackChainInterface)
+
+ @classmethod
+ def processRequest(cls, ps, **kw):
+ """invokes callback that should return a (request,response) tuple.
+ representing the SOAP request and response respectively.
+ ps -- ParsedSoap instance representing HTTP Body.
+ request -- twisted.web.server.Request
+ """
+ resource = kw['resource']
+ request = kw['request']
+ method = getattr(resource, 'wsa_%s' %
+ _get_element_nsuri_name(ps.body_root)[-1])
+
+ # TODO: grab ps.address, clean this up.
+ try:
+ req_pyobj,rsp_pyobj = method(ps, ps.address, request=request)
+ except TypeError, ex:
+ log.err(
+ 'ERROR: service %s is broken, method MUST return request, response'\
+ %self.__class__.__name__
+ )
+ raise
+ except Exception, ex:
+ log.err('failure when calling bound method')
+ raise
+
+ return rsp_pyobj
+
+
+def CheckInputArgs(*interfaces):
+ """Must provide at least one interface, the last one may be repeated.
+ """
+ l = len(interfaces)
+ def wrapper(func):
+ def check_args(self, *args, **kw):
+ for i in range(len(args)):
+ if (l > i and interfaces[i].providedBy(args[i])) or interfaces[-1].providedBy(args[i]):
+ continue
+ if l > i: raise TypeError, 'arg %s does not implement %s' %(args[i], interfaces[i])
+ raise TypeError, 'arg %s does not implement %s' %(args[i], interfaces[-1])
+ func(self, *args, **kw)
+ return check_args
+ return wrapper
+
+
+class DefaultHandlerChain:
+
+ @CheckInputArgs(CallbackChainInterface, HandlerChainInterface)
+ def __init__(self, cb, *handlers):
+ self.handlercb = cb
+ self.handlers = handlers
+ self.debug = len(log.theLogPublisher.observers) > 0
+
+ def processRequest(self, arg, **kw):
+ if self.debug:
+ log.msg('--->PROCESS REQUEST\n%s' %arg, debug=1)
+
+ for h in self.handlers:
+ arg = h.processRequest(arg, **kw)
+
+ return self.handlercb.processRequest(arg, **kw)
+
+ def processResponse(self, arg, **kw):
+ if self.debug:
+ log.msg('===>PROCESS RESPONSE: %s' %str(arg), debug=1)
+
+ if arg is None:
+ return
+
+ for h in self.handlers:
+ arg = h.processResponse(arg, **kw)
+
+ s = str(arg)
+ if self.debug:
+ log.msg(s, debug=1)
+
+ return s
+
+
+class DefaultHandlerChainFactory:
+ protocol = DefaultHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+ return cls.protocol(DefaultCallbackHandler, DataHandler)
+
+
+class WSAddressHandlerChainFactory:
+ protocol = DefaultHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+ return cls.protocol(WSAddressCallbackHandler, DataHandler,
+ WSAddressHandler())
+
+
+class WSResource(twisted.web.resource.Resource, object):
+ """
+ class variables:
+ encoding --
+ factory -- hander chain, which has a factory method "newInstance"
+ that returns a
+ """
+ encoding = "UTF-8"
+ factory = DefaultHandlerChainFactory
+
+ def __init__(self):
+ """
+ """
+ twisted.web.resource.Resource.__init__(self)
+
+ def _writeResponse(self, request, response, status=200):
+ """
+ request -- request message
+ response --- response message
+ status -- HTTP Status
+ """
+ request.setResponseCode(status)
+ if self.encoding is not None:
+ mimeType = 'text/xml; charset="%s"' % self.encoding
+ else:
+ mimeType = "text/xml"
+
+ request.setHeader("Content-type", mimeType)
+ request.setHeader("Content-length", str(len(response)))
+ request.write(response)
+ request.finish()
+ return NOT_DONE_YET
+
+ def _writeFault(self, request, ex):
+ """
+ request -- request message
+ ex -- Exception
+ """
+ response = None
+ response = fault.FaultFromException(ex, False, sys.exc_info()[2]).AsSOAP()
+ log.err('SOAP FAULT: %s' % response)
+ return self._writeResponse(request, response, status=500)
+
+ def render_POST(self, request):
+ """Dispatch Method called by twisted render, creates a
+ request/response handler chain.
+ request -- twisted.web.server.Request
+ """
+ chain = self.factory.newInstance()
+ data = request.content.read()
+ try:
+ pyobj = chain.processRequest(data, request=request, resource=self)
+ except Exception, ex:
+ return self._writeFault(request, ex)
+
+ try:
+ soap = chain.processResponse(pyobj, request=request, resource=self)
+ except Exception, ex:
+ return self._writeFault(request, ex)
+
+ if soap is not None:
+ return self._writeResponse(request, soap)
+
+ request.finish()
+ return NOT_DONE_YET
+
diff --git a/ZSI/twisted/WSsecurity.py b/ZSI/twisted/WSsecurity.py
new file mode 100644
index 0000000..b082efe
--- /dev/null
+++ b/ZSI/twisted/WSsecurity.py
@@ -0,0 +1,389 @@
+###########################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+# $Id: WSsecurity.py 1134 2006-02-24 00:23:06Z boverhof $
+###########################################################################
+
+import sys, time, warnings
+import sha, base64
+
+# twisted & related imports
+from zope.interface import classProvides, implements, Interface
+from twisted.python import log, failure
+from twisted.web.error import NoResource
+from twisted.web.server import NOT_DONE_YET
+from twisted.internet import reactor
+import twisted.web.http
+import twisted.web.resource
+
+# ZSI imports
+from ZSI import _get_element_nsuri_name, EvaluateException, ParseException
+from ZSI.parse import ParsedSoap
+from ZSI.writer import SoapWriter
+from ZSI.TC import _get_global_element_declaration as GED
+from ZSI import fault
+from ZSI.wstools.Namespaces import OASIS, DSIG
+from WSresource import DefaultHandlerChain, HandlerChainInterface,\
+ WSAddressCallbackHandler, DataHandler, WSAddressHandler
+
+
+#
+# Global Element Declarations
+#
+UsernameTokenDec = GED(OASIS.WSSE, "UsernameToken")
+SecurityDec = GED(OASIS.WSSE, "Security")
+SignatureDec = GED(DSIG.BASE, "Signature")
+PasswordDec = GED(OASIS.WSSE, "Password")
+NonceDec = GED(OASIS.WSSE, "Nonce")
+CreatedDec = GED(OASIS.UTILITY, "Created")
+
+if None in [UsernameTokenDec,SecurityDec,SignatureDec,PasswordDec,NonceDec,CreatedDec]:
+ raise ImportError, 'required global element(s) unavailable: %s ' %({
+ (OASIS.WSSE, "UsernameToken"):UsernameTokenDec,
+ (OASIS.WSSE, "Security"):SecurityDec,
+ (DSIG.BASE, "Signature"):SignatureDec,
+ (OASIS.WSSE, "Password"):PasswordDec,
+ (OASIS.WSSE, "Nonce"):NonceDec,
+ (OASIS.UTILITY, "Created"):CreatedDec,
+ })
+
+
+#
+# Stability: Unstable, Untested, Not Finished.
+#
+
+class WSSecurityHandler:
+ """Web Services Security: SOAP Message Security 1.0
+
+ Class Variables:
+ debug -- If True provide more detailed SOAP:Fault information to clients.
+ """
+ classProvides(HandlerChainInterface)
+ debug = True
+
+ @classmethod
+ def processRequest(cls, ps, **kw):
+ if type(ps) is not ParsedSoap:
+ raise TypeError,'Expecting ParsedSoap instance'
+
+ security = ps.ParseHeaderElements([cls.securityDec])
+
+ # Assume all security headers are supposed to be processed here.
+ for pyobj in security or []:
+ for any in pyobj.Any or []:
+
+ if any.typecode is UsernameTokenDec:
+ try:
+ ps = cls.UsernameTokenProfileHandler.processRequest(ps, any)
+ except Exception, ex:
+ if cls.debug: raise
+ raise RuntimeError, 'Unauthorized Username/passphrase combination'
+ continue
+
+ if any.typecode is SignatureDec:
+ try:
+ ps = cls.SignatureHandler.processRequest(ps, any)
+ except Exception, ex:
+ if cls.debug: raise
+ raise RuntimeError, 'Invalid Security Header'
+ continue
+
+ raise RuntimeError, 'WS-Security, Unsupported token %s' %str(any)
+
+ return ps
+
+ @classmethod
+ def processResponse(cls, output, **kw):
+ return output
+
+
+ class UsernameTokenProfileHandler:
+ """Web Services Security UsernameToken Profile 1.0
+
+ Class Variables:
+ targetNamespace --
+ """
+ classProvides(HandlerChainInterface)
+
+ # Class Variables
+ targetNamespace = OASIS.WSSE
+ sweepInterval = 60*5
+ nonces = None
+
+ # Set to None to disable
+ PasswordText = targetNamespace + "#PasswordText"
+ PasswordDigest = targetNamespace + "#PasswordDigest"
+
+ # Override passwordCallback
+ passwordCallback = lambda cls,username: None
+
+ @classmethod
+ def sweep(cls, index):
+ """remove nonces every sweepInterval.
+ Parameters:
+ index -- remove all nonces up to this index.
+ """
+ if cls.nonces is None:
+ cls.nonces = []
+
+ seconds = cls.sweepInterval
+ cls.nonces = cls.nonces[index:]
+ reactor.callLater(seconds, cls.sweep, len(cls.nonces))
+
+ @classmethod
+ def processRequest(cls, ps, token, **kw):
+ """
+ Parameters:
+ ps -- ParsedSoap instance
+ token -- UsernameToken pyclass instance
+ """
+ if token.typecode is not UsernameTokenDec:
+ raise TypeError, 'expecting GED (%s,%s) representation.' %(
+ UsernameTokenDec.nspname, UsernameTokenDec.pname)
+
+ username = token.Username
+
+ # expecting only one password
+ # may have a nonce and a created
+ password = nonce = timestamp = None
+ for any in token.Any or []:
+ if any.typecode is PasswordDec:
+ password = any
+ continue
+
+ if any.typecode is NonceTypeDec:
+ nonce = any
+ continue
+
+ if any.typecode is CreatedTypeDec:
+ timestamp = any
+ continue
+
+ raise TypeError, 'UsernameTokenProfileHander unexpected %s' %str(any)
+
+ if password is None:
+ raise RuntimeError, 'Unauthorized, no password'
+
+ # TODO: not yet supporting complexType simpleContent in pyclass_type
+ attrs = getattr(password, password.typecode.attrs_aname, {})
+ pwtype = attrs.get('Type', cls.PasswordText)
+
+ # Clear Text Passwords
+ if cls.PasswordText is not None and pwtype == cls.PasswordText:
+ if password == cls.passwordCallback(username):
+ return ps
+
+ raise RuntimeError, 'Unauthorized, clear text password failed'
+
+ if cls.nonces is None: cls.sweep(0)
+ if nonce is not None:
+ if nonce in cls.nonces:
+ raise RuntimeError, 'Invalid Nonce'
+
+ # created was 10 seconds ago or sooner
+ if created is not None and created < time.gmtime(time.time()-10):
+ raise RuntimeError, 'UsernameToken created is expired'
+
+ cls.nonces.append(nonce)
+
+ # PasswordDigest, recommended that implemenations
+ # require a Nonce and Created
+ if cls.PasswordDigest is not None and pwtype == cls.PasswordDigest:
+ digest = sha.sha()
+ for i in (nonce, created, cls.passwordCallback(username)):
+ if i is None: continue
+ digest.update(i)
+
+ if password == base64.encodestring(digest.digest()).strip():
+ return ps
+
+ raise RuntimeError, 'Unauthorized, digest failed'
+
+ raise RuntimeError, 'Unauthorized, contents of UsernameToken unknown'
+
+ @classmethod
+ def processResponse(cls, output, **kw):
+ return output
+
+ @staticmethod
+ def hmac_sha1(xml):
+ return
+
+ class SignatureHandler:
+ """Web Services Security UsernameToken Profile 1.0
+ """
+ digestMethods = {
+ DSIG.BASE+"#sha1":sha.sha,
+ }
+ signingMethods = {
+ DSIG.BASE+"#hmac-sha1":hmac_sha1,
+ }
+ canonicalizationMethods = {
+ DSIG.C14N_EXCL:lambda node: Canonicalize(node, unsuppressedPrefixes=[]),
+ DSIG.C14N:lambda node: Canonicalize(node),
+ }
+
+ @classmethod
+ def processRequest(cls, ps, signature, **kw):
+ """
+ Parameters:
+ ps -- ParsedSoap instance
+ signature -- Signature pyclass instance
+ """
+ if token.typecode is not SignatureDec:
+ raise TypeError, 'expecting GED (%s,%s) representation.' %(
+ SignatureDec.nspname, SignatureDec.pname)
+
+ si = signature.SignedInfo
+ si.CanonicalizationMethod
+ calgo = si.CanonicalizationMethod.get_attribute_Algorithm()
+ for any in si.CanonicalizationMethod.Any:
+ pass
+
+ # Check Digest
+ si.Reference
+ context = XPath.Context.Context(ps.dom, processContents={'wsu':OASIS.UTILITY})
+ exp = XPath.Compile('//*[@wsu:Id="%s"]' %si.Reference.get_attribute_URI())
+ nodes = exp.evaluate(context)
+ if len(nodes) != 1:
+ raise RuntimeError, 'A SignedInfo Reference must refer to one node %s.' %(
+ si.Reference.get_attribute_URI())
+
+ try:
+ xml = cls.canonicalizeMethods[calgo](nodes[0])
+ except IndexError:
+ raise RuntimeError, 'Unsupported canonicalization algorithm'
+
+ try:
+ digest = cls.digestMethods[salgo]
+ except IndexError:
+ raise RuntimeError, 'unknown digestMethods Algorithm'
+
+ digestValue = base64.encodestring(digest(xml).digest()).strip()
+ if si.Reference.DigestValue != digestValue:
+ raise RuntimeError, 'digest does not match'
+
+ if si.Reference.Transforms:
+ pass
+
+ signature.KeyInfo
+ signature.KeyInfo.KeyName
+ signature.KeyInfo.KeyValue
+ signature.KeyInfo.RetrievalMethod
+ signature.KeyInfo.X509Data
+ signature.KeyInfo.PGPData
+ signature.KeyInfo.SPKIData
+ signature.KeyInfo.MgmtData
+ signature.KeyInfo.Any
+
+ signature.Object
+
+ # TODO: Check Signature
+ signature.SignatureValue
+ si.SignatureMethod
+ salgo = si.SignatureMethod.get_attribute_Algorithm()
+ if si.SignatureMethod.HMACOutputLength:
+ pass
+ for any in si.SignatureMethod.Any:
+ pass
+
+ # <SignedInfo><Reference URI="">
+ exp = XPath.Compile('//child::*[attribute::URI = "%s"]/..' %(
+ si.Reference.get_attribute_URI()))
+ nodes = exp.evaluate(context)
+ if len(nodes) != 1:
+ raise RuntimeError, 'A SignedInfo Reference must refer to one node %s.' %(
+ si.Reference.get_attribute_URI())
+
+ try:
+ xml = cls.canonicalizeMethods[calgo](nodes[0])
+ except IndexError:
+ raise RuntimeError, 'Unsupported canonicalization algorithm'
+
+ # TODO: Check SignatureValue
+
+ @classmethod
+ def processResponse(cls, output, **kw):
+ return output
+
+
+ class X509TokenProfileHandler:
+ """Web Services Security UsernameToken Profile 1.0
+ """
+ targetNamespace = DSIG.BASE
+
+ # Token Types
+ singleCertificate = targetNamespace + "#X509v3"
+ certificatePath = targetNamespace + "#X509PKIPathv1"
+ setCerticatesCRLs = targetNamespace + "#PKCS7"
+
+ @classmethod
+ def processRequest(cls, ps, signature, **kw):
+ return ps
+
+
+
+"""
+<element name="KeyInfo" type="ds:KeyInfoType"/>
+<complexType name="KeyInfoType" mixed="true">
+ <choice maxOccurs="unbounded">
+ <element ref="ds:KeyName"/>
+ <element ref="ds:KeyValue"/>
+ <element ref="ds:RetrievalMethod"/>
+ <element ref="ds:X509Data"/>
+ <element ref="ds:PGPData"/>
+ <element ref="ds:SPKIData"/>
+ <element ref="ds:MgmtData"/>
+ <any processContents="lax" namespace="##other"/>
+ <!-- (1,1) elements from (0,unbounded) namespaces -->
+ </choice>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+
+
+<element name="Signature" type="ds:SignatureType"/>
+<complexType name="SignatureType">
+ <sequence>
+ <element ref="ds:SignedInfo"/>
+ <element ref="ds:SignatureValue"/>
+ <element ref="ds:KeyInfo" minOccurs="0"/>
+ <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+ <element name="SignatureValue" type="ds:SignatureValueType"/>
+ <complexType name="SignatureValueType">
+ <simpleContent>
+ <extension base="base64Binary">
+ <attribute name="Id" type="ID" use="optional"/>
+ </extension>
+ </simpleContent>
+ </complexType>
+
+<!-- Start SignedInfo -->
+
+<element name="SignedInfo" type="ds:SignedInfoType"/>
+<complexType name="SignedInfoType">
+ <sequence>
+ <element ref="ds:CanonicalizationMethod"/>
+ <element ref="ds:SignatureMethod"/>
+ <element ref="ds:Reference" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+"""
+
+
+class WSSecurityHandlerChainFactory:
+ protocol = DefaultHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+
+ return cls.protocol(WSAddressCallbackHandler, DataHandler,
+ WSSecurityHandler, WSAddressHandler())
+
+
+
diff --git a/ZSI/twisted/__init__.py b/ZSI/twisted/__init__.py
new file mode 100644
index 0000000..f9b565d
--- /dev/null
+++ b/ZSI/twisted/__init__.py
@@ -0,0 +1,7 @@
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+# $Id: __init__.py 1132 2006-02-17 01:55:41Z boverhof $
+###########################################################################
+
+__all__=['WSresource', 'WSsecurity']
diff --git a/ZSI/twisted/client.py b/ZSI/twisted/client.py
new file mode 100644
index 0000000..7813e1f
--- /dev/null
+++ b/ZSI/twisted/client.py
@@ -0,0 +1,344 @@
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import time
+
+# twisted & related imports
+from zope.interface import classProvides, implements, Interface
+from twisted.web import client
+from twisted.internet import defer
+from twisted.internet import reactor
+from twisted.python import log
+from twisted.python.failure import Failure
+
+from ZSI.parse import ParsedSoap
+from ZSI.writer import SoapWriter
+from ZSI.fault import FaultFromFaultMessage
+from ZSI.wstools.Namespaces import WSA
+
+from WSresource import HandlerChainInterface, CheckInputArgs
+
+
+#
+# Stability: Unstable
+#
+
+class HTTPPageGetter(client.HTTPPageGetter):
+ def handleStatus_500(self):
+ """potentially a SOAP:Fault.
+ """
+ log.err('HTTP Error 500')
+ def handleStatus_404(self):
+ """client error, not found
+ """
+ log.err('HTTP Error 404')
+
+
+client.HTTPClientFactory.protocol = HTTPPageGetter
+
+
+def getPage(url, contextFactory=None, *args, **kwargs):
+ """Download a web page as a string.
+
+ Download a page. Return a deferred, which will callback with a
+ page (as a string) or errback with a description of the error.
+
+ See HTTPClientFactory to see what extra args can be passed.
+ """
+ scheme, host, port, path = client._parse(url)
+ factory = client.HTTPClientFactory(url, *args, **kwargs)
+ if scheme == 'https':
+ if contextFactory is None:
+ raise RuntimeError, 'must provide a contextFactory'
+ conn = reactor.connectSSL(host, port, factory, contextFactory)
+ else:
+ conn = reactor.connectTCP(host, port, factory)
+
+ return factory
+
+
+class ClientDataHandler:
+ """
+ class variables:
+ readerClass -- factory class to create reader for ParsedSoap instances.
+ writerClass -- ElementProxy implementation to use for SoapWriter
+ instances.
+ """
+ classProvides(HandlerChainInterface)
+ readerClass = None
+ writerClass = None
+
+ @classmethod
+ def processResponse(cls, soapdata, **kw):
+ """called by deferred, returns pyobj representing reply.
+ Parameters and Key Words:
+ soapdata -- SOAP Data
+ replytype -- reply type of response
+ """
+ if len(soapdata) == 0:
+ raise TypeError('Received empty response')
+
+# log.msg("_" * 33, time.ctime(time.time()),
+# "RESPONSE: \n%s" %soapdata, debug=True)
+
+ ps = ParsedSoap(soapdata, readerclass=cls.readerClass)
+ if ps.IsAFault() is True:
+ log.msg('Received SOAP:Fault', debug=True)
+ raise FaultFromFaultMessage(ps)
+
+ return ps
+
+ @classmethod
+ def processRequest(cls, obj, nsdict={}, header=True,
+ **kw):
+ tc = None
+ if kw.has_key('requesttypecode'):
+ tc = kw['requesttypecode']
+ elif kw.has_key('requestclass'):
+ tc = kw['requestclass'].typecode
+ else:
+ tc = getattr(obj.__class__, 'typecode', None)
+
+ sw = SoapWriter(nsdict=nsdict, header=header,
+ outputclass=cls.writerClass)
+ sw.serialize(obj, tc)
+ return sw
+
+
+class WSAddressHandler:
+ """Minimal WS-Address handler. Most of the logic is in
+ the ZSI.address.Address class.
+
+ class variables:
+ uri -- default WSA Addressing URI
+ """
+ implements(HandlerChainInterface)
+ uri = WSA.ADDRESS
+
+ def processResponse(self, ps, wsaction=None, soapaction=None, **kw):
+ addr = self.address
+ addr.parse(ps)
+ action = addr.getAction()
+ if not action:
+ raise WSActionException('No WS-Action specified in Request')
+
+ if not soapaction:
+ return ps
+
+ soapaction = soapaction.strip('\'"')
+ if soapaction and soapaction != wsaction:
+ raise WSActionException(\
+ 'SOAP Action("%s") must match WS-Action("%s") if specified.'%(
+ soapaction, wsaction)
+ )
+
+ return ps
+
+ def processRequest(self, sw, wsaction=None, url=None, endPointReference=None, **kw):
+ from ZSI.address import Address
+ if sw is None:
+ self.address = None
+ return
+
+ if not sw.header:
+ raise RuntimeError, 'expecting SOAP:Header'
+
+ self.address = addr = Address(url, wsAddressURI=self.uri)
+ addr.setRequest(endPointReference, wsaction)
+ addr.serialize(sw, typed=False)
+
+ return sw
+
+
+class DefaultClientHandlerChain:
+
+ @CheckInputArgs(HandlerChainInterface)
+ def __init__(self, *handlers):
+ self.handlers = handlers
+ self.debug = len(log.theLogPublisher.observers) > 0
+ self.flow = None
+
+ @staticmethod
+ def parseResponse(ps, replytype):
+ return ps.Parse(replytype)
+
+ def processResponse(self, arg, replytype, **kw):
+ """
+ Parameters:
+ arg -- deferred
+ replytype -- typecode
+ """
+ if self.debug:
+ log.msg('--->PROCESS REQUEST\n%s' %arg, debug=1)
+
+ for h in self.handlers:
+ arg.addCallback(h.processResponse, **kw)
+
+ arg.addCallback(self.parseResponse, replytype)
+
+ def processRequest(self, arg, **kw):
+ """
+ Parameters:
+ arg -- XML Soap data string
+ """
+ if self.debug:
+ log.msg('===>PROCESS RESPONSE: %s' %str(arg), debug=1)
+
+ if arg is None:
+ return
+
+ for h in self.handlers:
+ arg = h.processRequest(arg, **kw)
+
+ s = str(arg)
+ if self.debug:
+ log.msg(s, debug=1)
+
+ return s
+
+
+class DefaultClientHandlerChainFactory:
+ protocol = DefaultClientHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+ return cls.protocol(ClientDataHandler)
+
+
+class WSAddressClientHandlerChainFactory:
+ protocol = DefaultClientHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+ return cls.protocol(ClientDataHandler,
+ WSAddressHandler())
+
+
+class Binding:
+ """Object that represents a binding (connection) to a SOAP server.
+ """
+ agent='ZSI.twisted client'
+ factory = DefaultClientHandlerChainFactory
+ defer = False
+
+ def __init__(self, url=None, nsdict=None, contextFactory=None,
+ tracefile=None, **kw):
+ """Initialize.
+ Keyword arguments include:
+ url -- URL of resource, POST is path
+ nsdict -- namespace entries to add
+ contextFactory -- security contexts
+ tracefile -- file to dump packet traces
+ """
+ self.url = url
+ self.nsdict = nsdict or {}
+ self.contextFactory = contextFactory
+ self.http_headers = {'content-type': 'text/xml',}
+ self.trace = tracefile
+
+ def addHTTPHeader(self, key, value):
+ self.http_headers[key] = value
+
+ def getHTTPHeaders(self):
+ return self.http_headers
+
+ def Send(self, url, opname, pyobj, nsdict={}, soapaction=None, chain=None,
+ **kw):
+ """Returns a ProcessingChain which needs to be passed to Receive if
+ Send is being called consecutively.
+ """
+ url = url or self.url
+ cookies = None
+ if chain is not None:
+ cookies = chain.flow.cookies
+
+ d = {}
+ d.update(self.nsdict)
+ d.update(nsdict)
+
+ if soapaction is not None:
+ self.addHTTPHeader('SOAPAction', soapaction)
+
+ chain = self.factory.newInstance()
+ soapdata = chain.processRequest(pyobj, nsdict=nsdict,
+ soapaction=soapaction, **kw)
+
+ if self.trace:
+ print >>self.trace, "_" * 33, time.ctime(time.time()), "REQUEST:"
+ print >>self.trace, soapdata
+
+ f = getPage(str(url), contextFactory=self.contextFactory,
+ postdata=soapdata, agent=self.agent,
+ method='POST', headers=self.getHTTPHeaders(),
+ cookies=cookies)
+
+ if isinstance(f, Failure):
+ return f
+
+ chain.flow = f
+ self.chain = chain
+ return chain
+
+ def Receive(self, replytype, chain=None, **kw):
+ """This method allows code to act in a synchronous manner, it waits to
+ return until the deferred fires but it doesn't prevent other queued
+ calls from being executed. Send must be called first, which sets up
+ the chain/factory.
+
+ WARNING: If defer is set to True, must either call Receive
+ immediately after Send (ie. no intervening Sends) or pass
+ chain in as a paramter.
+
+ Parameters:
+ replytype -- TypeCode
+ KeyWord Parameters:
+ chain -- processing chain, optional
+
+ """
+ chain = chain or self.chain
+ d = chain.flow.deferred
+ if self.trace:
+ def trace(soapdata):
+ print >>self.trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
+ print >>self.trace, soapdata
+ return soapdata
+
+ d.addCallback(trace)
+
+ chain.processResponse(d, replytype, **kw)
+ if self.defer:
+ return d
+
+ failure = []
+ append = failure.append
+ def errback(result):
+ """Used with Response method to suppress 'Unhandled error in
+ Deferred' messages by adding an errback.
+ """
+ append(result)
+ return None
+
+ d.addErrback(errback)
+
+ # spin reactor
+ while not d.called:
+ reactor.runUntilCurrent()
+ t2 = reactor.timeout()
+ t = reactor.running and t2
+ reactor.doIteration(t)
+
+ pyobj = d.result
+ if len(failure):
+ failure[0].raiseException()
+
+ return pyobj
+
+def trace():
+ if trace:
+ print >>trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
+ for i in (self.reply_code, self.reply_msg,):
+ print >>trace, str(i)
+ print >>trace, "-------"
+ print >>trace, str(self.reply_headers)
+ print >>trace, self.data
diff --git a/ZSI/typeinterpreter.py b/ZSI/typeinterpreter.py
new file mode 100644
index 0000000..8fe47c5
--- /dev/null
+++ b/ZSI/typeinterpreter.py
@@ -0,0 +1,133 @@
+###########################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+import ZSI
+from ZSI import TC, TCtimes, TCcompound
+from ZSI.TC import TypeCode
+from ZSI import _copyright, EvaluateException
+from ZSI.wstools.Utility import SplitQName
+from ZSI.wstools.Namespaces import SOAP, SCHEMA
+
+###########################################################################
+# Module Classes: BaseTypeInterpreter
+###########################################################################
+
+class NamespaceException(Exception): pass
+class BaseTypeInterpreter:
+ """Example mapping of xsd/soapenc types to zsi python types.
+ Checks against all available classes in ZSI.TC. Used in
+ wsdl2python, wsdlInterpreter, and ServiceProxy.
+ """
+
+ def __init__(self):
+ self._type_list = [TC.Iinteger, TC.IunsignedShort, TC.gYearMonth, \
+ TC.InonNegativeInteger, TC.Iint, TC.String, \
+ TC.gDateTime, TC.IunsignedInt, TC.Duration,\
+ TC.IpositiveInteger, TC.FPfloat, TC.gDay, TC.gMonth, \
+ TC.InegativeInteger, TC.gDate, TC.URI, \
+ TC.HexBinaryString, TC.IunsignedByte, \
+ TC.gMonthDay, TC.InonPositiveInteger, \
+ TC.Ibyte, TC.FPdouble, TC.gTime, TC.gYear, \
+ TC.Ilong, TC.IunsignedLong, TC.Ishort, \
+ TC.Token, TC.QName]
+
+ self._tc_to_int = [
+ ZSI.TCnumbers.IEnumeration,
+ ZSI.TCnumbers.Iint,
+ ZSI.TCnumbers.Iinteger,
+ ZSI.TCnumbers.Ilong,
+ ZSI.TCnumbers.InegativeInteger,
+ ZSI.TCnumbers.InonNegativeInteger,
+ ZSI.TCnumbers.InonPositiveInteger,
+ ZSI.TC.Integer,
+ ZSI.TCnumbers.IpositiveInteger,
+ ZSI.TCnumbers.Ishort]
+
+ self._tc_to_float = [
+ ZSI.TC.Decimal,
+ ZSI.TCnumbers.FPEnumeration,
+ ZSI.TCnumbers.FPdouble,
+ ZSI.TCnumbers.FPfloat]
+
+ self._tc_to_string = [
+ ZSI.TC.Base64String,
+ ZSI.TC.Enumeration,
+ ZSI.TC.HexBinaryString,
+ ZSI.TCnumbers.Ibyte,
+ ZSI.TCnumbers.IunsignedByte,
+ ZSI.TCnumbers.IunsignedInt,
+ ZSI.TCnumbers.IunsignedLong,
+ ZSI.TCnumbers.IunsignedShort,
+ ZSI.TC.String,
+ ZSI.TC.URI,
+ ZSI.TC.XMLString,
+ ZSI.TC.Token]
+
+ self._tc_to_tuple = [
+ ZSI.TC.Duration,
+ ZSI.TC.QName,
+ ZSI.TCtimes.gDate,
+ ZSI.TCtimes.gDateTime,
+ ZSI.TCtimes.gDay,
+ ZSI.TCtimes.gMonthDay,
+ ZSI.TCtimes.gTime,
+ ZSI.TCtimes.gYear,
+ ZSI.TCtimes.gMonth,
+ ZSI.TCtimes.gYearMonth]
+
+ return
+
+ def _get_xsd_typecode(self, msg_type):
+ untaged_xsd_types = {'boolean':TC.Boolean,
+ 'decimal':TC.Decimal,
+ 'base64Binary':TC.Base64String}
+ if untaged_xsd_types.has_key(msg_type):
+ return untaged_xsd_types[msg_type]
+ for tc in self._type_list:
+ if tc.type == (SCHEMA.XSD3,msg_type):
+ break
+ else:
+ tc = TC.AnyType
+ return tc
+
+ def _get_soapenc_typecode(self, msg_type):
+ if msg_type == 'Array':
+ return TCcompound.Array
+ if msg_type == 'Struct':
+ return TCcompound.Struct
+
+ return self._get_xsd_typecode(msg_type)
+
+ def get_typeclass(self, msg_type, targetNamespace):
+ prefix, name = SplitQName(msg_type)
+ if targetNamespace in SCHEMA.XSD_LIST:
+ return self._get_xsd_typecode(name)
+ elif targetNamespace in [SOAP.ENC]:
+ return self._get_soapenc_typecode(name)
+ return None
+
+ def get_pythontype(self, msg_type, targetNamespace, typeclass=None):
+ if not typeclass:
+ tc = self.get_typeclass(msg_type, targetNamespace)
+ else:
+ tc = typeclass
+ if tc in self._tc_to_int:
+ return 'int'
+ elif tc in self._tc_to_float:
+ return 'float'
+ elif tc in self._tc_to_string:
+ return 'str'
+ elif tc in self._tc_to_tuple:
+ return 'tuple'
+ elif tc in [TCcompound.Array]:
+ return 'list'
+ elif tc in [TC.Boolean]:
+ return 'bool'
+ elif isinstance(tc, TypeCode):
+ raise EvaluateException,\
+ 'failed to map zsi typecode to a python type'
+ return None
+
+
diff --git a/ZSI/version.py b/ZSI/version.py
new file mode 100644
index 0000000..4936345
--- /dev/null
+++ b/ZSI/version.py
@@ -0,0 +1,2 @@
+# Auto-generated file; do not edit
+Version = (2, 0, 0)
diff --git a/ZSI/writer.py b/ZSI/writer.py
new file mode 100644
index 0000000..27fd8ab
--- /dev/null
+++ b/ZSI/writer.py
@@ -0,0 +1,190 @@
+#! /usr/bin/env python
+# $Id: writer.py 1270 2006-10-19 22:31:07Z boverhof $
+'''SOAP message serialization.
+'''
+
+from ZSI import _copyright, _get_idstr, ZSI_SCHEMA_URI
+from ZSI import _backtrace, _stringtypes, _seqtypes
+from ZSI.wstools.Utility import MessageInterface, ElementProxy
+from ZSI.wstools.Namespaces import XMLNS, SOAP, SCHEMA
+from ZSI.wstools.c14n import Canonicalize
+import types
+
+_standard_ns = [ ('xml', XMLNS.XML), ('xmlns', XMLNS.BASE) ]
+
+_reserved_ns = {
+ 'SOAP-ENV': SOAP.ENV,
+ 'SOAP-ENC': SOAP.ENC,
+ 'ZSI': ZSI_SCHEMA_URI,
+ 'xsd': SCHEMA.BASE,
+ 'xsi': SCHEMA.BASE + '-instance',
+}
+
+class SoapWriter:
+ '''SOAP output formatter.
+ Instance Data:
+ memo -- memory for id/href
+ envelope -- add Envelope?
+ encodingStyle --
+ header -- add SOAP Header?
+ outputclass -- ElementProxy class.
+ '''
+
+ def __init__(self, envelope=True, encodingStyle=None, header=True,
+ nsdict={}, outputclass=None, **kw):
+ '''Initialize.
+ '''
+ outputclass = outputclass or ElementProxy
+ if not issubclass(outputclass, MessageInterface):
+ raise TypeError, 'outputclass must subclass MessageInterface'
+
+ self.dom, self.memo, self.nsdict= \
+ outputclass(self), [], nsdict
+ self.envelope = envelope
+ self.encodingStyle = encodingStyle
+ self.header = header
+ self.body = None
+ self.callbacks = []
+ self.closed = False
+
+ def __str__(self):
+ self.close()
+ return str(self.dom)
+
+ def getSOAPHeader(self):
+ if self.header in (True, False):
+ return None
+ return self.header
+
+ def serialize_header(self, pyobj, typecode=None, **kw):
+ '''Serialize a Python object in SOAP-ENV:Header, make
+ sure everything in Header unique (no #href). Must call
+ serialize first to create a document.
+
+ Parameters:
+ pyobjs -- instances to serialize in SOAP Header
+ typecode -- default typecode
+ '''
+ kw['unique'] = True
+ soap_env = _reserved_ns['SOAP-ENV']
+ #header = self.dom.getElement(soap_env, 'Header')
+ header = self._header
+ if header is None:
+ header = self._header = self.dom.createAppendElement(soap_env,
+ 'Header')
+
+ typecode = getattr(pyobj, 'typecode', typecode)
+ if typecode is None:
+ raise RuntimeError(
+ 'typecode is required to serialize pyobj in header')
+
+ helt = typecode.serialize(header, self, pyobj, **kw)
+
+ def serialize(self, pyobj, typecode=None, root=None, header_pyobjs=(), **kw):
+ '''Serialize a Python object to the output stream.
+ pyobj -- python instance to serialize in body.
+ typecode -- typecode describing body
+ root -- SOAP-ENC:root
+ header_pyobjs -- list of pyobj for soap header inclusion, each
+ instance must specify the typecode attribute.
+ '''
+ self.body = None
+ if self.envelope:
+ soap_env = _reserved_ns['SOAP-ENV']
+ self.dom.createDocument(soap_env, 'Envelope')
+ for prefix, nsuri in _reserved_ns.items():
+ self.dom.setNamespaceAttribute(prefix, nsuri)
+ self.writeNSdict(self.nsdict)
+ if self.encodingStyle:
+ self.dom.setAttributeNS(soap_env, 'encodingStyle',
+ self.encodingStyle)
+ if self.header:
+ self._header = self.dom.createAppendElement(soap_env, 'Header')
+
+ for h in header_pyobjs:
+ self.serialize_header(h, **kw)
+
+ self.body = self.dom.createAppendElement(soap_env, 'Body')
+ else:
+ self.dom.createDocument(None,None)
+
+ if typecode is None: typecode = pyobj.__class__.typecode
+ kw = kw.copy()
+
+ if self.body is None:
+ elt = typecode.serialize(self.dom, self, pyobj, **kw)
+ else:
+ elt = typecode.serialize(self.body, self, pyobj, **kw)
+
+ if root is not None:
+ if root not in [ 0, 1 ]:
+ raise ValueError, "SOAP-ENC root attribute not in [0,1]"
+ elt.setAttributeNS(SOAP.ENC, 'root', root)
+
+ return self
+
+ def writeNSdict(self, nsdict):
+ '''Write a namespace dictionary, taking care to not clobber the
+ standard (or reserved by us) prefixes.
+ '''
+ for k,v in nsdict.items():
+ if (k,v) in _standard_ns: continue
+ rv = _reserved_ns.get(k)
+ if rv:
+ if rv != v:
+ raise KeyError("Reserved namespace " + str((k,v)) + " used")
+ continue
+ if k:
+ self.dom.setNamespaceAttribute(k, v)
+ else:
+ self.dom.setNamespaceAttribute('xmlns', v)
+
+
+ def ReservedNS(self, prefix, uri):
+ '''Is this namespace (prefix,uri) reserved by us?
+ '''
+ return _reserved_ns.get(prefix, uri) != uri
+
+ def AddCallback(self, func, *arglist):
+ '''Add a callback function and argument list to be invoked before
+ closing off the SOAP Body.
+ '''
+ self.callbacks.append((func, arglist))
+
+ def Known(self, obj):
+ '''Seen this object (known by its id()? Return 1 if so,
+ otherwise add it to our memory and return 0.
+ '''
+ obj = _get_idstr(obj)
+ if obj in self.memo: return 1
+ self.memo.append(obj)
+ return 0
+
+ def Forget(self, obj):
+ '''Forget we've seen this object.
+ '''
+ obj = _get_idstr(obj)
+ try:
+ self.memo.remove(obj)
+ except ValueError:
+ pass
+
+ def Backtrace(self, elt):
+ '''Return a human-readable "backtrace" from the document root to
+ the specified element.
+ '''
+ return _backtrace(elt._getNode(), self.dom._getNode())
+
+ def close(self):
+ '''Invoke all the callbacks, and close off the SOAP message.
+ '''
+ if self.closed: return
+ for func,arglist in self.callbacks:
+ apply(func, arglist)
+ self.closed = True
+
+ def __del__(self):
+ if not self.closed: self.close()
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/wstools/.cvsignore b/ZSI/wstools/.cvsignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/ZSI/wstools/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/ZSI/wstools/Namespaces.py b/ZSI/wstools/Namespaces.py
new file mode 100755
index 0000000..da7e0c7
--- /dev/null
+++ b/ZSI/wstools/Namespaces.py
@@ -0,0 +1,194 @@
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+"""Namespace module, so you don't need PyXML
+"""
+
+ident = "$Id: Namespaces.py 1160 2006-03-17 19:28:11Z boverhof $"
+try:
+ from xml.ns import SOAP, SCHEMA, WSDL, XMLNS, DSIG, ENCRYPTION
+ DSIG.C14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
+
+except:
+ class SOAP:
+ ENV = "http://schemas.xmlsoap.org/soap/envelope/"
+ ENC = "http://schemas.xmlsoap.org/soap/encoding/"
+ ACTOR_NEXT = "http://schemas.xmlsoap.org/soap/actor/next"
+
+ class SCHEMA:
+ XSD1 = "http://www.w3.org/1999/XMLSchema"
+ XSD2 = "http://www.w3.org/2000/10/XMLSchema"
+ XSD3 = "http://www.w3.org/2001/XMLSchema"
+ XSD_LIST = [ XSD1, XSD2, XSD3 ]
+ XSI1 = "http://www.w3.org/1999/XMLSchema-instance"
+ XSI2 = "http://www.w3.org/2000/10/XMLSchema-instance"
+ XSI3 = "http://www.w3.org/2001/XMLSchema-instance"
+ XSI_LIST = [ XSI1, XSI2, XSI3 ]
+ BASE = XSD3
+
+ class WSDL:
+ BASE = "http://schemas.xmlsoap.org/wsdl/"
+ BIND_HTTP = "http://schemas.xmlsoap.org/wsdl/http/"
+ BIND_MIME = "http://schemas.xmlsoap.org/wsdl/mime/"
+ BIND_SOAP = "http://schemas.xmlsoap.org/wsdl/soap/"
+ BIND_SOAP12 = "http://schemas.xmlsoap.org/wsdl/soap12/"
+
+ class XMLNS:
+ BASE = "http://www.w3.org/2000/xmlns/"
+ XML = "http://www.w3.org/XML/1998/namespace"
+ HTML = "http://www.w3.org/TR/REC-html40"
+
+ class DSIG:
+ BASE = "http://www.w3.org/2000/09/xmldsig#"
+ C14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
+ C14N_COMM = "http://www.w3.org/TR/2000/CR-xml-c14n-20010315#WithComments"
+ C14N_EXCL = "http://www.w3.org/2001/10/xml-exc-c14n#"
+ DIGEST_MD2 = "http://www.w3.org/2000/09/xmldsig#md2"
+ DIGEST_MD5 = "http://www.w3.org/2000/09/xmldsig#md5"
+ DIGEST_SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
+ ENC_BASE64 = "http://www.w3.org/2000/09/xmldsig#base64"
+ ENVELOPED = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
+ HMAC_SHA1 = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"
+ SIG_DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"
+ SIG_RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
+ XPATH = "http://www.w3.org/TR/1999/REC-xpath-19991116"
+ XSLT = "http://www.w3.org/TR/1999/REC-xslt-19991116"
+
+ class ENCRYPTION:
+ BASE = "http://www.w3.org/2001/04/xmlenc#"
+ BLOCK_3DES = "http://www.w3.org/2001/04/xmlenc#des-cbc"
+ BLOCK_AES128 = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"
+ BLOCK_AES192 = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"
+ BLOCK_AES256 = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
+ DIGEST_RIPEMD160 = "http://www.w3.org/2001/04/xmlenc#ripemd160"
+ DIGEST_SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256"
+ DIGEST_SHA512 = "http://www.w3.org/2001/04/xmlenc#sha512"
+ KA_DH = "http://www.w3.org/2001/04/xmlenc#dh"
+ KT_RSA_1_5 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ KT_RSA_OAEP = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
+ STREAM_ARCFOUR = "http://www.w3.org/2001/04/xmlenc#arcfour"
+ WRAP_3DES = "http://www.w3.org/2001/04/xmlenc#kw-3des"
+ WRAP_AES128 = "http://www.w3.org/2001/04/xmlenc#kw-aes128"
+ WRAP_AES192 = "http://www.w3.org/2001/04/xmlenc#kw-aes192"
+ WRAP_AES256 = "http://www.w3.org/2001/04/xmlenc#kw-aes256"
+
+
+class WSRF_V1_2:
+ '''OASIS WSRF Specifications Version 1.2
+ '''
+ class LIFETIME:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd"
+ XSD_DRAFT4 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceLifetime-1.2-draft-04.xsd"
+
+ WSDL_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.wsdl"
+ WSDL_DRAFT4 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceLifetime-1.2-draft-04.wsdl"
+ LATEST = WSDL_DRAFT4
+ WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT4)
+ XSD_LIST = (XSD_DRAFT1, XSD_DRAFT4)
+
+ class PROPERTIES:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd"
+ XSD_DRAFT5 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceProperties-1.2-draft-05.xsd"
+
+ WSDL_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
+ WSDL_DRAFT5 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceProperties-1.2-draft-05.wsdl"
+ LATEST = WSDL_DRAFT5
+ WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT5)
+ XSD_LIST = (XSD_DRAFT1, XSD_DRAFT5)
+
+ class BASENOTIFICATION:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd"
+
+ WSDL_DRAFT1 = "http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl"
+ LATEST = WSDL_DRAFT1
+ WSDL_LIST = (WSDL_DRAFT1,)
+ XSD_LIST = (XSD_DRAFT1,)
+
+ class BASEFAULTS:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-BaseFaults-1.2-draft-01.xsd"
+ XSD_DRAFT3 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-BaseFaults-1.2-draft-03.xsd"
+ #LATEST = DRAFT3
+ #WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT3)
+ XSD_LIST = (XSD_DRAFT1, XSD_DRAFT3)
+
+WSRF = WSRF_V1_2
+WSRFLIST = (WSRF_V1_2,)
+
+
+class OASIS:
+ '''URLs for Oasis specifications
+ '''
+ WSSE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
+ UTILITY = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
+
+ class X509TOKEN:
+ Base64Binary = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
+ STRTransform = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0"
+ PKCS7 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#PKCS7"
+ X509 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509"
+ X509PKIPathv1 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"
+ X509v3SubjectKeyIdentifier = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3SubjectKeyIdentifier"
+
+ LIFETIME = WSRF_V1_2.LIFETIME.XSD_DRAFT1
+ PROPERTIES = WSRF_V1_2.PROPERTIES.XSD_DRAFT1
+ BASENOTIFICATION = WSRF_V1_2.BASENOTIFICATION.XSD_DRAFT1
+ BASEFAULTS = WSRF_V1_2.BASEFAULTS.XSD_DRAFT1
+
+
+class WSTRUST:
+ BASE = "http://schemas.xmlsoap.org/ws/2004/04/trust"
+ ISSUE = "http://schemas.xmlsoap.org/ws/2004/04/trust/Issue"
+
+class WSSE:
+ BASE = "http://schemas.xmlsoap.org/ws/2002/04/secext"
+ TRUST = WSTRUST.BASE
+
+
+class WSU:
+ BASE = "http://schemas.xmlsoap.org/ws/2002/04/utility"
+ UTILITY = "http://schemas.xmlsoap.org/ws/2002/07/utility"
+
+
+class WSR:
+ PROPERTIES = "http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceProperties"
+ LIFETIME = "http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceLifetime"
+
+
+class WSA200408:
+ ADDRESS = "http://schemas.xmlsoap.org/ws/2004/08/addressing"
+ ANONYMOUS = "%s/role/anonymous" %ADDRESS
+ FAULT = "%s/fault" %ADDRESS
+
+class WSA200403:
+ ADDRESS = "http://schemas.xmlsoap.org/ws/2004/03/addressing"
+ ANONYMOUS = "%s/role/anonymous" %ADDRESS
+ FAULT = "%s/fault" %ADDRESS
+
+class WSA200303:
+ ADDRESS = "http://schemas.xmlsoap.org/ws/2003/03/addressing"
+ ANONYMOUS = "%s/role/anonymous" %ADDRESS
+ FAULT = None
+
+
+WSA = WSA200408
+WSA_LIST = (WSA200408, WSA200403, WSA200303)
+
+class WSP:
+ POLICY = "http://schemas.xmlsoap.org/ws/2002/12/policy"
+
+class BEA:
+ SECCONV = "http://schemas.xmlsoap.org/ws/2004/04/sc"
+ SCTOKEN = "http://schemas.xmlsoap.org/ws/2004/04/security/sc/sct"
+
+class GLOBUS:
+ SECCONV = "http://wsrf.globus.org/core/2004/07/security/secconv"
+ CORE = "http://www.globus.org/namespaces/2004/06/core"
+ SIG = "http://www.globus.org/2002/04/xmlenc#gssapi-sign"
+ TOKEN = "http://www.globus.org/ws/2004/09/security/sc#GSSAPI_GSI_TOKEN"
+
+ZSI_SCHEMA_URI = 'http://www.zolera.com/schemas/ZSI/'
diff --git a/ZSI/wstools/TimeoutSocket.py b/ZSI/wstools/TimeoutSocket.py
new file mode 100755
index 0000000..9e2f2db
--- /dev/null
+++ b/ZSI/wstools/TimeoutSocket.py
@@ -0,0 +1,179 @@
+"""Based on code from timeout_socket.py, with some tweaks for compatibility.
+ These tweaks should really be rolled back into timeout_socket, but it's
+ not totally clear who is maintaining it at this point. In the meantime,
+ we'll use a different module name for our tweaked version to avoid any
+ confusion.
+
+ The original timeout_socket is by:
+
+ Scott Cotton <scott@chronis.pobox.com>
+ Lloyd Zusman <ljz@asfast.com>
+ Phil Mayes <pmayes@olivebr.com>
+ Piers Lauder <piers@cs.su.oz.au>
+ Radovan Garabik <garabik@melkor.dnp.fmph.uniba.sk>
+"""
+
+ident = "$Id: TimeoutSocket.py 237 2003-05-20 21:10:14Z warnes $"
+
+import string, socket, select, errno
+
+WSAEINVAL = getattr(errno, 'WSAEINVAL', 10022)
+
+
+class TimeoutSocket:
+ """A socket imposter that supports timeout limits."""
+
+ def __init__(self, timeout=20, sock=None):
+ self.timeout = float(timeout)
+ self.inbuf = ''
+ if sock is None:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock = sock
+ self.sock.setblocking(0)
+ self._rbuf = ''
+ self._wbuf = ''
+
+ def __getattr__(self, name):
+ # Delegate to real socket attributes.
+ return getattr(self.sock, name)
+
+ def connect(self, *addr):
+ timeout = self.timeout
+ sock = self.sock
+ try:
+ # Non-blocking mode
+ sock.setblocking(0)
+ apply(sock.connect, addr)
+ sock.setblocking(timeout != 0)
+ return 1
+ except socket.error,why:
+ if not timeout:
+ raise
+ sock.setblocking(1)
+ if len(why.args) == 1:
+ code = 0
+ else:
+ code, why = why
+ if code not in (
+ errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK
+ ):
+ raise
+ r,w,e = select.select([],[sock],[],timeout)
+ if w:
+ try:
+ apply(sock.connect, addr)
+ return 1
+ except socket.error,why:
+ if len(why.args) == 1:
+ code = 0
+ else:
+ code, why = why
+ if code in (errno.EISCONN, WSAEINVAL):
+ return 1
+ raise
+ raise TimeoutError('socket connect() timeout.')
+
+ def send(self, data, flags=0):
+ total = len(data)
+ next = 0
+ while 1:
+ r, w, e = select.select([],[self.sock], [], self.timeout)
+ if w:
+ buff = data[next:next + 8192]
+ sent = self.sock.send(buff, flags)
+ next = next + sent
+ if next == total:
+ return total
+ continue
+ raise TimeoutError('socket send() timeout.')
+
+ def recv(self, amt, flags=0):
+ if select.select([self.sock], [], [], self.timeout)[0]:
+ return self.sock.recv(amt, flags)
+ raise TimeoutError('socket recv() timeout.')
+
+ buffsize = 4096
+ handles = 1
+
+ def makefile(self, mode="r", buffsize=-1):
+ self.handles = self.handles + 1
+ self.mode = mode
+ return self
+
+ def close(self):
+ self.handles = self.handles - 1
+ if self.handles == 0 and self.sock.fileno() >= 0:
+ self.sock.close()
+
+ def read(self, n=-1):
+ if not isinstance(n, type(1)):
+ n = -1
+ if n >= 0:
+ k = len(self._rbuf)
+ if n <= k:
+ data = self._rbuf[:n]
+ self._rbuf = self._rbuf[n:]
+ return data
+ n = n - k
+ L = [self._rbuf]
+ self._rbuf = ""
+ while n > 0:
+ new = self.recv(max(n, self.buffsize))
+ if not new: break
+ k = len(new)
+ if k > n:
+ L.append(new[:n])
+ self._rbuf = new[n:]
+ break
+ L.append(new)
+ n = n - k
+ return "".join(L)
+ k = max(4096, self.buffsize)
+ L = [self._rbuf]
+ self._rbuf = ""
+ while 1:
+ new = self.recv(k)
+ if not new: break
+ L.append(new)
+ k = min(k*2, 1024**2)
+ return "".join(L)
+
+ def readline(self, limit=-1):
+ data = ""
+ i = self._rbuf.find('\n')
+ while i < 0 and not (0 < limit <= len(self._rbuf)):
+ new = self.recv(self.buffsize)
+ if not new: break
+ i = new.find('\n')
+ if i >= 0: i = i + len(self._rbuf)
+ self._rbuf = self._rbuf + new
+ if i < 0: i = len(self._rbuf)
+ else: i = i+1
+ if 0 <= limit < len(self._rbuf): i = limit
+ data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
+ return data
+
+ def readlines(self, sizehint = 0):
+ total = 0
+ list = []
+ while 1:
+ line = self.readline()
+ if not line: break
+ list.append(line)
+ total += len(line)
+ if sizehint and total >= sizehint:
+ break
+ return list
+
+ def writelines(self, list):
+ self.send(''.join(list))
+
+ def write(self, data):
+ self.send(data)
+
+ def flush(self):
+ pass
+
+
+class TimeoutError(Exception):
+ pass
diff --git a/ZSI/wstools/UserTuple.py b/ZSI/wstools/UserTuple.py
new file mode 100644
index 0000000..86ff8e5
--- /dev/null
+++ b/ZSI/wstools/UserTuple.py
@@ -0,0 +1,99 @@
+"""
+A more or less complete user-defined wrapper around tuple objects.
+Adapted version of the standard library's UserList.
+
+Taken from Stefan Schwarzer's ftputil library, available at
+<http://www.ndh.net/home/sschwarzer/python/python_software.html>, and used under this license:
+
+
+
+
+Copyright (C) 1999, Stefan Schwarzer
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+- Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+- Neither the name of the above author nor the names of the
+ contributors to the software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+
+
+
+
+# $Id: UserTuple.py 277 2003-07-21 14:18:54Z warnes $
+
+#XXX tuple instances (in Python 2.2) contain also:
+# __class__, __delattr__, __getattribute__, __hash__, __new__,
+# __reduce__, __setattr__, __str__
+# What about these?
+
+class UserTuple:
+ def __init__(self, inittuple=None):
+ self.data = ()
+ if inittuple is not None:
+ # XXX should this accept an arbitrary sequence?
+ if type(inittuple) == type(self.data):
+ self.data = inittuple
+ elif isinstance(inittuple, UserTuple):
+ # this results in
+ # self.data is inittuple.data
+ # but that's ok for tuples because they are
+ # immutable. (Builtin tuples behave the same.)
+ self.data = inittuple.data[:]
+ else:
+ # the same applies here; (t is tuple(t)) == 1
+ self.data = tuple(inittuple)
+ def __repr__(self): return repr(self.data)
+ def __lt__(self, other): return self.data < self.__cast(other)
+ def __le__(self, other): return self.data <= self.__cast(other)
+ def __eq__(self, other): return self.data == self.__cast(other)
+ def __ne__(self, other): return self.data != self.__cast(other)
+ def __gt__(self, other): return self.data > self.__cast(other)
+ def __ge__(self, other): return self.data >= self.__cast(other)
+ def __cast(self, other):
+ if isinstance(other, UserTuple): return other.data
+ else: return other
+ def __cmp__(self, other):
+ return cmp(self.data, self.__cast(other))
+ def __contains__(self, item): return item in self.data
+ def __len__(self): return len(self.data)
+ def __getitem__(self, i): return self.data[i]
+ def __getslice__(self, i, j):
+ i = max(i, 0); j = max(j, 0)
+ return self.__class__(self.data[i:j])
+ def __add__(self, other):
+ if isinstance(other, UserTuple):
+ return self.__class__(self.data + other.data)
+ elif isinstance(other, type(self.data)):
+ return self.__class__(self.data + other)
+ else:
+ return self.__class__(self.data + tuple(other))
+ # dir( () ) contains no __radd__ (at least in Python 2.2)
+ def __mul__(self, n):
+ return self.__class__(self.data*n)
+ __rmul__ = __mul__
+
diff --git a/ZSI/wstools/Utility.py b/ZSI/wstools/Utility.py
new file mode 100755
index 0000000..ebe62fe
--- /dev/null
+++ b/ZSI/wstools/Utility.py
@@ -0,0 +1,1376 @@
+# Copyright (c) 2003, The Regents of the University of California,
+# through Lawrence Berkeley National Laboratory (subject to receipt of
+# any required approvals from the U.S. Dept. of Energy). All rights
+# reserved.
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+
+ident = "$Id: Utility.py 1116 2006-01-24 20:51:57Z boverhof $"
+
+import sys, types, httplib, smtplib, urllib, socket, weakref
+from os.path import isfile
+from string import join, strip, split
+from UserDict import UserDict
+from cStringIO import StringIO
+from TimeoutSocket import TimeoutSocket, TimeoutError
+from urlparse import urlparse
+from httplib import HTTPConnection, HTTPSConnection
+from exceptions import Exception
+try:
+ from ZSI import _get_idstr
+except:
+ def _get_idstr(pyobj):
+ '''Python 2.3.x generates a FutureWarning for negative IDs, so
+ we use a different prefix character to ensure uniqueness, and
+ call abs() to avoid the warning.'''
+ x = id(pyobj)
+ if x < 0:
+ return 'x%x' % abs(x)
+ return 'o%x' % x
+
+import xml.dom.minidom
+from xml.dom import Node
+
+import logging
+from c14n import Canonicalize
+from Namespaces import SCHEMA, SOAP, XMLNS, ZSI_SCHEMA_URI
+
+
+try:
+ from xml.dom.ext import SplitQName
+except:
+ def SplitQName(qname):
+ '''SplitQName(qname) -> (string, string)
+
+ Split Qualified Name into a tuple of len 2, consisting
+ of the prefix and the local name.
+
+ (prefix, localName)
+
+ Special Cases:
+ xmlns -- (localName, 'xmlns')
+ None -- (None, localName)
+ '''
+
+ l = qname.split(':')
+ if len(l) == 1:
+ l.insert(0, None)
+ elif len(l) == 2:
+ if l[0] == 'xmlns':
+ l.reverse()
+ else:
+ return
+ return tuple(l)
+
+#
+# python2.3 urllib.basejoin does not remove current directory ./
+# from path and this causes problems on subsequent basejoins.
+#
+basejoin = urllib.basejoin
+if sys.version_info[0:2] < (2, 4, 0, 'final', 0)[0:2]:
+ #basejoin = lambda base,url: urllib.basejoin(base,url.lstrip('./'))
+ token = './'
+ def basejoin(base, url):
+ if url.startswith(token) is True:
+ return urllib.basejoin(base,url[2:])
+ return urllib.basejoin(base,url)
+
+class NamespaceError(Exception):
+ """Used to indicate a Namespace Error."""
+
+
+class RecursionError(Exception):
+ """Used to indicate a HTTP redirect recursion."""
+
+
+class ParseError(Exception):
+ """Used to indicate a XML parsing error."""
+
+
+class DOMException(Exception):
+ """Used to indicate a problem processing DOM."""
+
+
+class Base:
+ """Base class for instance level Logging"""
+ def __init__(self, module=__name__):
+ self.logger = logging.getLogger('%s-%s(%s)' %(module, self.__class__, _get_idstr(self)))
+
+
+class HTTPResponse:
+ """Captures the information in an HTTP response message."""
+
+ def __init__(self, response):
+ self.status = response.status
+ self.reason = response.reason
+ self.headers = response.msg
+ self.body = response.read() or None
+ response.close()
+
+class TimeoutHTTP(HTTPConnection):
+ """A custom http connection object that supports socket timeout."""
+ def __init__(self, host, port=None, timeout=20):
+ HTTPConnection.__init__(self, host, port)
+ self.timeout = timeout
+
+ def connect(self):
+ self.sock = TimeoutSocket(self.timeout)
+ self.sock.connect((self.host, self.port))
+
+
+class TimeoutHTTPS(HTTPSConnection):
+ """A custom https object that supports socket timeout. Note that this
+ is not really complete. The builtin SSL support in the Python socket
+ module requires a real socket (type) to be passed in to be hooked to
+ SSL. That means our fake socket won't work and our timeout hacks are
+ bypassed for send and recv calls. Since our hack _is_ in place at
+ connect() time, it should at least provide some timeout protection."""
+ def __init__(self, host, port=None, timeout=20, **kwargs):
+ HTTPSConnection.__init__(self, str(host), port, **kwargs)
+ self.timeout = timeout
+
+ def connect(self):
+ sock = TimeoutSocket(self.timeout)
+ sock.connect((self.host, self.port))
+ realsock = getattr(sock.sock, '_sock', sock.sock)
+ ssl = socket.ssl(realsock, self.key_file, self.cert_file)
+ self.sock = httplib.FakeSocket(sock, ssl)
+
+
+def urlopen(url, timeout=20, redirects=None):
+ """A minimal urlopen replacement hack that supports timeouts for http.
+ Note that this supports GET only."""
+ scheme, host, path, params, query, frag = urlparse(url)
+
+ if not scheme in ('http', 'https'):
+ return urllib.urlopen(url)
+ if params: path = '%s;%s' % (path, params)
+ if query: path = '%s?%s' % (path, query)
+ if frag: path = '%s#%s' % (path, frag)
+
+ if scheme == 'https':
+ # If ssl is not compiled into Python, you will not get an exception
+ # until a conn.endheaders() call. We need to know sooner, so use
+ # getattr.
+ if hasattr(socket, 'ssl'):
+ conn = TimeoutHTTPS(host, None, timeout)
+ else:
+ import M2Crypto
+ ctx = M2Crypto.SSL.Context()
+ ctx.set_session_timeout(timeout)
+ conn = M2Crypto.httpslib.HTTPSConnection(host, ssl_context=ctx)
+ #conn.set_debuglevel(1)
+ else:
+ conn = TimeoutHTTP(host, None, timeout)
+
+ conn.putrequest('GET', path)
+ conn.putheader('Connection', 'close')
+ conn.endheaders()
+ response = None
+ while 1:
+ response = conn.getresponse()
+ if response.status != 100:
+ break
+ conn._HTTPConnection__state = httplib._CS_REQ_SENT
+ conn._HTTPConnection__response = None
+
+ status = response.status
+
+ # If we get an HTTP redirect, we will follow it automatically.
+ if status >= 300 and status < 400:
+ location = response.msg.getheader('location')
+ if location is not None:
+ response.close()
+ if redirects is not None and redirects.has_key(location):
+ raise RecursionError(
+ 'Circular HTTP redirection detected.'
+ )
+ if redirects is None:
+ redirects = {}
+ redirects[location] = 1
+ return urlopen(location, timeout, redirects)
+ raise HTTPResponse(response)
+
+ if not (status >= 200 and status < 300):
+ raise HTTPResponse(response)
+
+ body = StringIO(response.read())
+ response.close()
+ return body
+
+class DOM:
+ """The DOM singleton defines a number of XML related constants and
+ provides a number of utility methods for DOM related tasks. It
+ also provides some basic abstractions so that the rest of the
+ package need not care about actual DOM implementation in use."""
+
+ # Namespace stuff related to the SOAP specification.
+
+ NS_SOAP_ENV_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/'
+ NS_SOAP_ENC_1_1 = 'http://schemas.xmlsoap.org/soap/encoding/'
+
+ NS_SOAP_ENV_1_2 = 'http://www.w3.org/2001/06/soap-envelope'
+ NS_SOAP_ENC_1_2 = 'http://www.w3.org/2001/06/soap-encoding'
+
+ NS_SOAP_ENV_ALL = (NS_SOAP_ENV_1_1, NS_SOAP_ENV_1_2)
+ NS_SOAP_ENC_ALL = (NS_SOAP_ENC_1_1, NS_SOAP_ENC_1_2)
+
+ NS_SOAP_ENV = NS_SOAP_ENV_1_1
+ NS_SOAP_ENC = NS_SOAP_ENC_1_1
+
+ _soap_uri_mapping = {
+ NS_SOAP_ENV_1_1 : '1.1',
+ NS_SOAP_ENV_1_2 : '1.2',
+ }
+
+ SOAP_ACTOR_NEXT_1_1 = 'http://schemas.xmlsoap.org/soap/actor/next'
+ SOAP_ACTOR_NEXT_1_2 = 'http://www.w3.org/2001/06/soap-envelope/actor/next'
+ SOAP_ACTOR_NEXT_ALL = (SOAP_ACTOR_NEXT_1_1, SOAP_ACTOR_NEXT_1_2)
+
+ def SOAPUriToVersion(self, uri):
+ """Return the SOAP version related to an envelope uri."""
+ value = self._soap_uri_mapping.get(uri)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP envelope uri: %s' % uri
+ )
+
+ def GetSOAPEnvUri(self, version):
+ """Return the appropriate SOAP envelope uri for a given
+ human-friendly SOAP version string (e.g. '1.1')."""
+ attrname = 'NS_SOAP_ENV_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attrname, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP version: %s' % version
+ )
+
+ def GetSOAPEncUri(self, version):
+ """Return the appropriate SOAP encoding uri for a given
+ human-friendly SOAP version string (e.g. '1.1')."""
+ attrname = 'NS_SOAP_ENC_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attrname, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP version: %s' % version
+ )
+
+ def GetSOAPActorNextUri(self, version):
+ """Return the right special next-actor uri for a given
+ human-friendly SOAP version string (e.g. '1.1')."""
+ attrname = 'SOAP_ACTOR_NEXT_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attrname, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP version: %s' % version
+ )
+
+
+ # Namespace stuff related to XML Schema.
+
+ NS_XSD_99 = 'http://www.w3.org/1999/XMLSchema'
+ NS_XSI_99 = 'http://www.w3.org/1999/XMLSchema-instance'
+
+ NS_XSD_00 = 'http://www.w3.org/2000/10/XMLSchema'
+ NS_XSI_00 = 'http://www.w3.org/2000/10/XMLSchema-instance'
+
+ NS_XSD_01 = 'http://www.w3.org/2001/XMLSchema'
+ NS_XSI_01 = 'http://www.w3.org/2001/XMLSchema-instance'
+
+ NS_XSD_ALL = (NS_XSD_99, NS_XSD_00, NS_XSD_01)
+ NS_XSI_ALL = (NS_XSI_99, NS_XSI_00, NS_XSI_01)
+
+ NS_XSD = NS_XSD_01
+ NS_XSI = NS_XSI_01
+
+ _xsd_uri_mapping = {
+ NS_XSD_99 : NS_XSI_99,
+ NS_XSD_00 : NS_XSI_00,
+ NS_XSD_01 : NS_XSI_01,
+ }
+
+ for key, value in _xsd_uri_mapping.items():
+ _xsd_uri_mapping[value] = key
+
+
+ def InstanceUriForSchemaUri(self, uri):
+ """Return the appropriate matching XML Schema instance uri for
+ the given XML Schema namespace uri."""
+ return self._xsd_uri_mapping.get(uri)
+
+ def SchemaUriForInstanceUri(self, uri):
+ """Return the appropriate matching XML Schema namespace uri for
+ the given XML Schema instance namespace uri."""
+ return self._xsd_uri_mapping.get(uri)
+
+
+ # Namespace stuff related to WSDL.
+
+ NS_WSDL_1_1 = 'http://schemas.xmlsoap.org/wsdl/'
+ NS_WSDL_ALL = (NS_WSDL_1_1,)
+ NS_WSDL = NS_WSDL_1_1
+
+ NS_SOAP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/'
+ NS_HTTP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/http/'
+ NS_MIME_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/mime/'
+
+ NS_SOAP_BINDING_ALL = (NS_SOAP_BINDING_1_1,)
+ NS_HTTP_BINDING_ALL = (NS_HTTP_BINDING_1_1,)
+ NS_MIME_BINDING_ALL = (NS_MIME_BINDING_1_1,)
+
+ NS_SOAP_BINDING = NS_SOAP_BINDING_1_1
+ NS_HTTP_BINDING = NS_HTTP_BINDING_1_1
+ NS_MIME_BINDING = NS_MIME_BINDING_1_1
+
+ NS_SOAP_HTTP_1_1 = 'http://schemas.xmlsoap.org/soap/http'
+ NS_SOAP_HTTP_ALL = (NS_SOAP_HTTP_1_1,)
+ NS_SOAP_HTTP = NS_SOAP_HTTP_1_1
+
+
+ _wsdl_uri_mapping = {
+ NS_WSDL_1_1 : '1.1',
+ }
+
+ def WSDLUriToVersion(self, uri):
+ """Return the WSDL version related to a WSDL namespace uri."""
+ value = self._wsdl_uri_mapping.get(uri)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP envelope uri: %s' % uri
+ )
+
+ def GetWSDLUri(self, version):
+ attr = 'NS_WSDL_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLSoapBindingUri(self, version):
+ attr = 'NS_SOAP_BINDING_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLHttpBindingUri(self, version):
+ attr = 'NS_HTTP_BINDING_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLMimeBindingUri(self, version):
+ attr = 'NS_MIME_BINDING_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLHttpTransportUri(self, version):
+ attr = 'NS_SOAP_HTTP_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+
+ # Other xml namespace constants.
+ NS_XMLNS = 'http://www.w3.org/2000/xmlns/'
+
+
+
+ def isElement(self, node, name, nsuri=None):
+ """Return true if the given node is an element with the given
+ name and optional namespace uri."""
+ if node.nodeType != node.ELEMENT_NODE:
+ return 0
+ return node.localName == name and \
+ (nsuri is None or self.nsUriMatch(node.namespaceURI, nsuri))
+
+ def getElement(self, node, name, nsuri=None, default=join):
+ """Return the first child of node with a matching name and
+ namespace uri, or the default if one is provided."""
+ nsmatch = self.nsUriMatch
+ ELEMENT_NODE = node.ELEMENT_NODE
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if ((child.localName == name or name is None) and
+ (nsuri is None or nsmatch(child.namespaceURI, nsuri))
+ ):
+ return child
+ if default is not join:
+ return default
+ raise KeyError, name
+
+ def getElementById(self, node, id, default=join):
+ """Return the first child of node matching an id reference."""
+ attrget = self.getAttr
+ ELEMENT_NODE = node.ELEMENT_NODE
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if attrget(child, 'id') == id:
+ return child
+ if default is not join:
+ return default
+ raise KeyError, name
+
+ def getMappingById(self, document, depth=None, element=None,
+ mapping=None, level=1):
+ """Create an id -> element mapping of those elements within a
+ document that define an id attribute. The depth of the search
+ may be controlled by using the (1-based) depth argument."""
+ if document is not None:
+ element = document.documentElement
+ mapping = {}
+ attr = element._attrs.get('id', None)
+ if attr is not None:
+ mapping[attr.value] = element
+ if depth is None or depth > level:
+ level = level + 1
+ ELEMENT_NODE = element.ELEMENT_NODE
+ for child in element.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ self.getMappingById(None, depth, child, mapping, level)
+ return mapping
+
+ def getElements(self, node, name, nsuri=None):
+ """Return a sequence of the child elements of the given node that
+ match the given name and optional namespace uri."""
+ nsmatch = self.nsUriMatch
+ result = []
+ ELEMENT_NODE = node.ELEMENT_NODE
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if ((child.localName == name or name is None) and (
+ (nsuri is None) or nsmatch(child.namespaceURI, nsuri))):
+ result.append(child)
+ return result
+
+ def hasAttr(self, node, name, nsuri=None):
+ """Return true if element has attribute with the given name and
+ optional nsuri. If nsuri is not specified, returns true if an
+ attribute exists with the given name with any namespace."""
+ if nsuri is None:
+ if node.hasAttribute(name):
+ return True
+ return False
+ return node.hasAttributeNS(nsuri, name)
+
+ def getAttr(self, node, name, nsuri=None, default=join):
+ """Return the value of the attribute named 'name' with the
+ optional nsuri, or the default if one is specified. If
+ nsuri is not specified, an attribute that matches the
+ given name will be returned regardless of namespace."""
+ if nsuri is None:
+ result = node._attrs.get(name, None)
+ if result is None:
+ for item in node._attrsNS.keys():
+ if item[1] == name:
+ result = node._attrsNS[item]
+ break
+ else:
+ result = node._attrsNS.get((nsuri, name), None)
+ if result is not None:
+ return result.value
+ if default is not join:
+ return default
+ return ''
+
+ def getAttrs(self, node):
+ """Return a Collection of all attributes
+ """
+ attrs = {}
+ for k,v in node._attrs.items():
+ attrs[k] = v.value
+ return attrs
+
+ def getElementText(self, node, preserve_ws=None):
+ """Return the text value of an xml element node. Leading and trailing
+ whitespace is stripped from the value unless the preserve_ws flag
+ is passed with a true value."""
+ result = []
+ for child in node.childNodes:
+ nodetype = child.nodeType
+ if nodetype == child.TEXT_NODE or \
+ nodetype == child.CDATA_SECTION_NODE:
+ result.append(child.nodeValue)
+ value = join(result, '')
+ if preserve_ws is None:
+ value = strip(value)
+ return value
+
+ def findNamespaceURI(self, prefix, node):
+ """Find a namespace uri given a prefix and a context node."""
+ attrkey = (self.NS_XMLNS, prefix)
+ DOCUMENT_NODE = node.DOCUMENT_NODE
+ ELEMENT_NODE = node.ELEMENT_NODE
+ while 1:
+ if node is None:
+ raise DOMException('Value for prefix %s not found.' % prefix)
+ if node.nodeType != ELEMENT_NODE:
+ node = node.parentNode
+ continue
+ result = node._attrsNS.get(attrkey, None)
+ if result is not None:
+ return result.value
+ if hasattr(node, '__imported__'):
+ raise DOMException('Value for prefix %s not found.' % prefix)
+ node = node.parentNode
+ if node.nodeType == DOCUMENT_NODE:
+ raise DOMException('Value for prefix %s not found.' % prefix)
+
+ def findDefaultNS(self, node):
+ """Return the current default namespace uri for the given node."""
+ attrkey = (self.NS_XMLNS, 'xmlns')
+ DOCUMENT_NODE = node.DOCUMENT_NODE
+ ELEMENT_NODE = node.ELEMENT_NODE
+ while 1:
+ if node.nodeType != ELEMENT_NODE:
+ node = node.parentNode
+ continue
+ result = node._attrsNS.get(attrkey, None)
+ if result is not None:
+ return result.value
+ if hasattr(node, '__imported__'):
+ raise DOMException('Cannot determine default namespace.')
+ node = node.parentNode
+ if node.nodeType == DOCUMENT_NODE:
+ raise DOMException('Cannot determine default namespace.')
+
+ def findTargetNS(self, node):
+ """Return the defined target namespace uri for the given node."""
+ attrget = self.getAttr
+ attrkey = (self.NS_XMLNS, 'xmlns')
+ DOCUMENT_NODE = node.DOCUMENT_NODE
+ ELEMENT_NODE = node.ELEMENT_NODE
+ while 1:
+ if node.nodeType != ELEMENT_NODE:
+ node = node.parentNode
+ continue
+ result = attrget(node, 'targetNamespace', default=None)
+ if result is not None:
+ return result
+ node = node.parentNode
+ if node.nodeType == DOCUMENT_NODE:
+ raise DOMException('Cannot determine target namespace.')
+
+ def getTypeRef(self, element):
+ """Return (namespaceURI, name) for a type attribue of the given
+ element, or None if the element does not have a type attribute."""
+ typeattr = self.getAttr(element, 'type', default=None)
+ if typeattr is None:
+ return None
+ parts = typeattr.split(':', 1)
+ if len(parts) == 2:
+ nsuri = self.findNamespaceURI(parts[0], element)
+ else:
+ nsuri = self.findDefaultNS(element)
+ return (nsuri, parts[1])
+
+ def importNode(self, document, node, deep=0):
+ """Implements (well enough for our purposes) DOM node import."""
+ nodetype = node.nodeType
+ if nodetype in (node.DOCUMENT_NODE, node.DOCUMENT_TYPE_NODE):
+ raise DOMException('Illegal node type for importNode')
+ if nodetype == node.ENTITY_REFERENCE_NODE:
+ deep = 0
+ clone = node.cloneNode(deep)
+ self._setOwnerDoc(document, clone)
+ clone.__imported__ = 1
+ return clone
+
+ def _setOwnerDoc(self, document, node):
+ node.ownerDocument = document
+ for child in node.childNodes:
+ self._setOwnerDoc(document, child)
+
+ def nsUriMatch(self, value, wanted, strict=0, tt=type(())):
+ """Return a true value if two namespace uri values match."""
+ if value == wanted or (type(wanted) is tt) and value in wanted:
+ return 1
+ if not strict and value is not None:
+ wanted = type(wanted) is tt and wanted or (wanted,)
+ value = value[-1:] != '/' and value or value[:-1]
+ for item in wanted:
+ if item == value or item[:-1] == value:
+ return 1
+ return 0
+
+ def createDocument(self, nsuri, qname, doctype=None):
+ """Create a new writable DOM document object."""
+ impl = xml.dom.minidom.getDOMImplementation()
+ return impl.createDocument(nsuri, qname, doctype)
+
+ def loadDocument(self, data):
+ """Load an xml file from a file-like object and return a DOM
+ document instance."""
+ return xml.dom.minidom.parse(data)
+
+ def loadFromURL(self, url):
+ """Load an xml file from a URL and return a DOM document."""
+ if isfile(url) is True:
+ file = open(url, 'r')
+ else:
+ file = urlopen(url)
+
+ try:
+ result = self.loadDocument(file)
+ except Exception, ex:
+ file.close()
+ raise ParseError(('Failed to load document %s' %url,) + ex.args)
+ else:
+ file.close()
+ return result
+
+DOM = DOM()
+
+
+class MessageInterface:
+ '''Higher Level Interface, delegates to DOM singleton, must
+ be subclassed and implement all methods that throw NotImplementedError.
+ '''
+ def __init__(self, sw):
+ '''Constructor, May be extended, do not override.
+ sw -- soapWriter instance
+ '''
+ self.sw = None
+ if type(sw) != weakref.ReferenceType and sw is not None:
+ self.sw = weakref.ref(sw)
+ else:
+ self.sw = sw
+
+ def AddCallback(self, func, *arglist):
+ self.sw().AddCallback(func, *arglist)
+
+ def Known(self, obj):
+ return self.sw().Known(obj)
+
+ def Forget(self, obj):
+ return self.sw().Forget(obj)
+
+ def canonicalize(self):
+ '''canonicalize the underlying DOM, and return as string.
+ '''
+ raise NotImplementedError, ''
+
+ def createDocument(self, namespaceURI=SOAP.ENV, localName='Envelope'):
+ '''create Document
+ '''
+ raise NotImplementedError, ''
+
+ def createAppendElement(self, namespaceURI, localName):
+ '''create and append element(namespaceURI,localName), and return
+ the node.
+ '''
+ raise NotImplementedError, ''
+
+ def findNamespaceURI(self, qualifiedName):
+ raise NotImplementedError, ''
+
+ def resolvePrefix(self, prefix):
+ raise NotImplementedError, ''
+
+ def setAttributeNS(self, namespaceURI, localName, value):
+ '''set attribute (namespaceURI, localName)=value
+ '''
+ raise NotImplementedError, ''
+
+ def setAttributeType(self, namespaceURI, localName):
+ '''set attribute xsi:type=(namespaceURI, localName)
+ '''
+ raise NotImplementedError, ''
+
+ def setNamespaceAttribute(self, namespaceURI, prefix):
+ '''set namespace attribute xmlns:prefix=namespaceURI
+ '''
+ raise NotImplementedError, ''
+
+
+class ElementProxy(Base, MessageInterface):
+ '''
+ '''
+ _soap_env_prefix = 'SOAP-ENV'
+ _soap_enc_prefix = 'SOAP-ENC'
+ _zsi_prefix = 'ZSI'
+ _xsd_prefix = 'xsd'
+ _xsi_prefix = 'xsi'
+ _xml_prefix = 'xml'
+ _xmlns_prefix = 'xmlns'
+
+ _soap_env_nsuri = SOAP.ENV
+ _soap_enc_nsuri = SOAP.ENC
+ _zsi_nsuri = ZSI_SCHEMA_URI
+ _xsd_nsuri = SCHEMA.XSD3
+ _xsi_nsuri = SCHEMA.XSI3
+ _xml_nsuri = XMLNS.XML
+ _xmlns_nsuri = XMLNS.BASE
+
+ standard_ns = {\
+ _xml_prefix:_xml_nsuri,
+ _xmlns_prefix:_xmlns_nsuri
+ }
+ reserved_ns = {\
+ _soap_env_prefix:_soap_env_nsuri,
+ _soap_enc_prefix:_soap_enc_nsuri,
+ _zsi_prefix:_zsi_nsuri,
+ _xsd_prefix:_xsd_nsuri,
+ _xsi_prefix:_xsi_nsuri,
+ }
+ name = None
+ namespaceURI = None
+
+ def __init__(self, sw, message=None):
+ '''Initialize.
+ sw -- SoapWriter
+ '''
+ self._indx = 0
+ MessageInterface.__init__(self, sw)
+ Base.__init__(self)
+ self._dom = DOM
+ self.node = None
+ if type(message) in (types.StringType,types.UnicodeType):
+ self.loadFromString(message)
+ elif isinstance(message, ElementProxy):
+ self.node = message._getNode()
+ else:
+ self.node = message
+ self.processorNss = self.standard_ns.copy()
+ self.processorNss.update(self.reserved_ns)
+
+ def __str__(self):
+ return self.toString()
+
+ def evaluate(self, expression, processorNss=None):
+ '''expression -- XPath compiled expression
+ '''
+ from Ft.Xml import XPath
+ if not processorNss:
+ context = XPath.Context.Context(self.node, processorNss=self.processorNss)
+ else:
+ context = XPath.Context.Context(self.node, processorNss=processorNss)
+ nodes = expression.evaluate(context)
+ return map(lambda node: ElementProxy(self.sw,node), nodes)
+
+ #############################################
+ # Methods for checking/setting the
+ # classes (namespaceURI,name) node.
+ #############################################
+ def checkNode(self, namespaceURI=None, localName=None):
+ '''
+ namespaceURI -- namespace of element
+ localName -- local name of element
+ '''
+ namespaceURI = namespaceURI or self.namespaceURI
+ localName = localName or self.name
+ check = False
+ if localName and self.node:
+ check = self._dom.isElement(self.node, localName, namespaceURI)
+ if not check:
+ raise NamespaceError, 'unexpected node type %s, expecting %s' %(self.node, localName)
+
+ def setNode(self, node=None):
+ if node:
+ if isinstance(node, ElementProxy):
+ self.node = node._getNode()
+ else:
+ self.node = node
+ elif self.node:
+ node = self._dom.getElement(self.node, self.name, self.namespaceURI, default=None)
+ if not node:
+ raise NamespaceError, 'cant find element (%s,%s)' %(self.namespaceURI,self.name)
+ self.node = node
+ else:
+ #self.node = self._dom.create(self.node, self.name, self.namespaceURI, default=None)
+ self.createDocument(self.namespaceURI, localName=self.name, doctype=None)
+
+ self.checkNode()
+
+ #############################################
+ # Wrapper Methods for direct DOM Element Node access
+ #############################################
+ def _getNode(self):
+ return self.node
+
+ def _getElements(self):
+ return self._dom.getElements(self.node, name=None)
+
+ def _getOwnerDocument(self):
+ return self.node.ownerDocument or self.node
+
+ def _getUniquePrefix(self):
+ '''I guess we need to resolve all potential prefixes
+ because when the current node is attached it copies the
+ namespaces into the parent node.
+ '''
+ while 1:
+ self._indx += 1
+ prefix = 'ns%d' %self._indx
+ try:
+ self._dom.findNamespaceURI(prefix, self._getNode())
+ except DOMException, ex:
+ break
+ return prefix
+
+ def _getPrefix(self, node, nsuri):
+ '''
+ Keyword arguments:
+ node -- DOM Element Node
+ nsuri -- namespace of attribute value
+ '''
+ try:
+ if node and (node.nodeType == node.ELEMENT_NODE) and \
+ (nsuri == self._dom.findDefaultNS(node)):
+ return None
+ except DOMException, ex:
+ pass
+ if nsuri == XMLNS.XML:
+ return self._xml_prefix
+ if node.nodeType == Node.ELEMENT_NODE:
+ for attr in node.attributes.values():
+ if attr.namespaceURI == XMLNS.BASE \
+ and nsuri == attr.value:
+ return attr.localName
+ else:
+ if node.parentNode:
+ return self._getPrefix(node.parentNode, nsuri)
+ raise NamespaceError, 'namespaceURI "%s" is not defined' %nsuri
+
+ def _appendChild(self, node):
+ '''
+ Keyword arguments:
+ node -- DOM Element Node
+ '''
+ if node is None:
+ raise TypeError, 'node is None'
+ self.node.appendChild(node)
+
+ def _insertBefore(self, newChild, refChild):
+ '''
+ Keyword arguments:
+ child -- DOM Element Node to insert
+ refChild -- DOM Element Node
+ '''
+ self.node.insertBefore(newChild, refChild)
+
+ def _setAttributeNS(self, namespaceURI, qualifiedName, value):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of attribute
+ qualifiedName -- qualified name of new attribute value
+ value -- value of attribute
+ '''
+ self.node.setAttributeNS(namespaceURI, qualifiedName, value)
+
+ #############################################
+ #General Methods
+ #############################################
+ def isFault(self):
+ '''check to see if this is a soap:fault message.
+ '''
+ return False
+
+ def getPrefix(self, namespaceURI):
+ try:
+ prefix = self._getPrefix(node=self.node, nsuri=namespaceURI)
+ except NamespaceError, ex:
+ prefix = self._getUniquePrefix()
+ self.setNamespaceAttribute(prefix, namespaceURI)
+ return prefix
+
+ def getDocument(self):
+ return self._getOwnerDocument()
+
+ def setDocument(self, document):
+ self.node = document
+
+ def importFromString(self, xmlString):
+ doc = self._dom.loadDocument(StringIO(xmlString))
+ node = self._dom.getElement(doc, name=None)
+ clone = self.importNode(node)
+ self._appendChild(clone)
+
+ def importNode(self, node):
+ if isinstance(node, ElementProxy):
+ node = node._getNode()
+ return self._dom.importNode(self._getOwnerDocument(), node, deep=1)
+
+ def loadFromString(self, data):
+ self.node = self._dom.loadDocument(StringIO(data))
+
+ def canonicalize(self):
+ return Canonicalize(self.node)
+
+ def toString(self):
+ return self.canonicalize()
+
+ def createDocument(self, namespaceURI, localName, doctype=None):
+ '''If specified must be a SOAP envelope, else may contruct an empty document.
+ '''
+ prefix = self._soap_env_prefix
+
+ if namespaceURI == self.reserved_ns[prefix]:
+ qualifiedName = '%s:%s' %(prefix,localName)
+ elif namespaceURI is localName is None:
+ self.node = self._dom.createDocument(None,None,None)
+ return
+ else:
+ raise KeyError, 'only support creation of document in %s' %self.reserved_ns[prefix]
+
+ document = self._dom.createDocument(nsuri=namespaceURI, qname=qualifiedName, doctype=doctype)
+ self.node = document.childNodes[0]
+
+ #set up reserved namespace attributes
+ for prefix,nsuri in self.reserved_ns.items():
+ self._setAttributeNS(namespaceURI=self._xmlns_nsuri,
+ qualifiedName='%s:%s' %(self._xmlns_prefix,prefix),
+ value=nsuri)
+
+ #############################################
+ #Methods for attributes
+ #############################################
+ def hasAttribute(self, namespaceURI, localName):
+ return self._dom.hasAttr(self._getNode(), name=localName, nsuri=namespaceURI)
+
+ def setAttributeType(self, namespaceURI, localName):
+ '''set xsi:type
+ Keyword arguments:
+ namespaceURI -- namespace of attribute value
+ localName -- name of new attribute value
+
+ '''
+ self.logger.debug('setAttributeType: (%s,%s)', namespaceURI, localName)
+ value = localName
+ if namespaceURI:
+ value = '%s:%s' %(self.getPrefix(namespaceURI),localName)
+
+ xsi_prefix = self.getPrefix(self._xsi_nsuri)
+ self._setAttributeNS(self._xsi_nsuri, '%s:type' %xsi_prefix, value)
+
+ def createAttributeNS(self, namespace, name, value):
+ document = self._getOwnerDocument()
+ attrNode = document.createAttributeNS(namespace, name, value)
+
+ def setAttributeNS(self, namespaceURI, localName, value):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of attribute to create, None is for
+ attributes in no namespace.
+ localName -- local name of new attribute
+ value -- value of new attribute
+ '''
+ prefix = None
+ if namespaceURI:
+ try:
+ prefix = self.getPrefix(namespaceURI)
+ except KeyError, ex:
+ prefix = 'ns2'
+ self.setNamespaceAttribute(prefix, namespaceURI)
+ qualifiedName = localName
+ if prefix:
+ qualifiedName = '%s:%s' %(prefix, localName)
+ self._setAttributeNS(namespaceURI, qualifiedName, value)
+
+ def setNamespaceAttribute(self, prefix, namespaceURI):
+ '''
+ Keyword arguments:
+ prefix -- xmlns prefix
+ namespaceURI -- value of prefix
+ '''
+ self._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI)
+
+ #############################################
+ #Methods for elements
+ #############################################
+ def createElementNS(self, namespace, qname):
+ '''
+ Keyword arguments:
+ namespace -- namespace of element to create
+ qname -- qualified name of new element
+ '''
+ document = self._getOwnerDocument()
+ node = document.createElementNS(namespace, qname)
+ return ElementProxy(self.sw, node)
+
+ def createAppendSetElement(self, namespaceURI, localName, prefix=None):
+ '''Create a new element (namespaceURI,name), append it
+ to current node, then set it to be the current node.
+ Keyword arguments:
+ namespaceURI -- namespace of element to create
+ localName -- local name of new element
+ prefix -- if namespaceURI is not defined, declare prefix. defaults
+ to 'ns1' if left unspecified.
+ '''
+ node = self.createAppendElement(namespaceURI, localName, prefix=None)
+ node=node._getNode()
+ self._setNode(node._getNode())
+
+ def createAppendElement(self, namespaceURI, localName, prefix=None):
+ '''Create a new element (namespaceURI,name), append it
+ to current node, and return the newly created node.
+ Keyword arguments:
+ namespaceURI -- namespace of element to create
+ localName -- local name of new element
+ prefix -- if namespaceURI is not defined, declare prefix. defaults
+ to 'ns1' if left unspecified.
+ '''
+ declare = False
+ qualifiedName = localName
+ if namespaceURI:
+ try:
+ prefix = self.getPrefix(namespaceURI)
+ except:
+ declare = True
+ prefix = prefix or self._getUniquePrefix()
+ if prefix:
+ qualifiedName = '%s:%s' %(prefix, localName)
+ node = self.createElementNS(namespaceURI, qualifiedName)
+ if declare:
+ node._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI)
+ self._appendChild(node=node._getNode())
+ return node
+
+ def createInsertBefore(self, namespaceURI, localName, refChild):
+ qualifiedName = localName
+ prefix = self.getPrefix(namespaceURI)
+ if prefix:
+ qualifiedName = '%s:%s' %(prefix, localName)
+ node = self.createElementNS(namespaceURI, qualifiedName)
+ self._insertBefore(newChild=node._getNode(), refChild=refChild._getNode())
+ return node
+
+ def getElement(self, namespaceURI, localName):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of element
+ localName -- local name of element
+ '''
+ node = self._dom.getElement(self.node, localName, namespaceURI, default=None)
+ if node:
+ return ElementProxy(self.sw, node)
+ return None
+
+ def getAttributeValue(self, namespaceURI, localName):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of attribute
+ localName -- local name of attribute
+ '''
+ if self.hasAttribute(namespaceURI, localName):
+ attr = self.node.getAttributeNodeNS(namespaceURI,localName)
+ return attr.value
+ return None
+
+ def getValue(self):
+ return self._dom.getElementText(self.node, preserve_ws=True)
+
+ #############################################
+ #Methods for text nodes
+ #############################################
+ def createAppendTextNode(self, pyobj):
+ node = self.createTextNode(pyobj)
+ self._appendChild(node=node._getNode())
+ return node
+
+ def createTextNode(self, pyobj):
+ document = self._getOwnerDocument()
+ node = document.createTextNode(pyobj)
+ return ElementProxy(self.sw, node)
+
+ #############################################
+ #Methods for retrieving namespaceURI's
+ #############################################
+ def findNamespaceURI(self, qualifiedName):
+ parts = SplitQName(qualifiedName)
+ element = self._getNode()
+ if len(parts) == 1:
+ return (self._dom.findTargetNS(element), value)
+ return self._dom.findNamespaceURI(parts[0], element)
+
+ def resolvePrefix(self, prefix):
+ element = self._getNode()
+ return self._dom.findNamespaceURI(prefix, element)
+
+ def getSOAPEnvURI(self):
+ return self._soap_env_nsuri
+
+ def isEmpty(self):
+ return not self.node
+
+
+
+class Collection(UserDict):
+ """Helper class for maintaining ordered named collections."""
+ default = lambda self,k: k.name
+ def __init__(self, parent, key=None):
+ UserDict.__init__(self)
+ self.parent = weakref.ref(parent)
+ self.list = []
+ self._func = key or self.default
+
+ def __getitem__(self, key):
+ if type(key) is type(1):
+ return self.list[key]
+ return self.data[key]
+
+ def __setitem__(self, key, item):
+ item.parent = weakref.ref(self)
+ self.list.append(item)
+ self.data[key] = item
+
+ def keys(self):
+ return map(lambda i: self._func(i), self.list)
+
+ def items(self):
+ return map(lambda i: (self._func(i), i), self.list)
+
+ def values(self):
+ return self.list
+
+
+class CollectionNS(UserDict):
+ """Helper class for maintaining ordered named collections."""
+ default = lambda self,k: k.name
+ def __init__(self, parent, key=None):
+ UserDict.__init__(self)
+ self.parent = weakref.ref(parent)
+ self.targetNamespace = None
+ self.list = []
+ self._func = key or self.default
+
+ def __getitem__(self, key):
+ self.targetNamespace = self.parent().targetNamespace
+ if type(key) is types.IntType:
+ return self.list[key]
+ elif self.__isSequence(key):
+ nsuri,name = key
+ return self.data[nsuri][name]
+ return self.data[self.parent().targetNamespace][key]
+
+ def __setitem__(self, key, item):
+ item.parent = weakref.ref(self)
+ self.list.append(item)
+ targetNamespace = getattr(item, 'targetNamespace', self.parent().targetNamespace)
+ if not self.data.has_key(targetNamespace):
+ self.data[targetNamespace] = {}
+ self.data[targetNamespace][key] = item
+
+ def __isSequence(self, key):
+ return (type(key) in (types.TupleType,types.ListType) and len(key) == 2)
+
+ def keys(self):
+ keys = []
+ for tns in self.data.keys():
+ keys.append(map(lambda i: (tns,self._func(i)), self.data[tns].values()))
+ return keys
+
+ def items(self):
+ return map(lambda i: (self._func(i), i), self.list)
+
+ def values(self):
+ return self.list
+
+
+
+# This is a runtime guerilla patch for pulldom (used by minidom) so
+# that xml namespace declaration attributes are not lost in parsing.
+# We need them to do correct QName linking for XML Schema and WSDL.
+# The patch has been submitted to SF for the next Python version.
+
+from xml.dom.pulldom import PullDOM, START_ELEMENT
+if 1:
+ def startPrefixMapping(self, prefix, uri):
+ if not hasattr(self, '_xmlns_attrs'):
+ self._xmlns_attrs = []
+ self._xmlns_attrs.append((prefix or 'xmlns', uri))
+ self._ns_contexts.append(self._current_context.copy())
+ self._current_context[uri] = prefix or ''
+
+ PullDOM.startPrefixMapping = startPrefixMapping
+
+ def startElementNS(self, name, tagName , attrs):
+ # Retrieve xml namespace declaration attributes.
+ xmlns_uri = 'http://www.w3.org/2000/xmlns/'
+ xmlns_attrs = getattr(self, '_xmlns_attrs', None)
+ if xmlns_attrs is not None:
+ for aname, value in xmlns_attrs:
+ attrs._attrs[(xmlns_uri, aname)] = value
+ self._xmlns_attrs = []
+ uri, localname = name
+ if uri:
+ # When using namespaces, the reader may or may not
+ # provide us with the original name. If not, create
+ # *a* valid tagName from the current context.
+ if tagName is None:
+ prefix = self._current_context[uri]
+ if prefix:
+ tagName = prefix + ":" + localname
+ else:
+ tagName = localname
+ if self.document:
+ node = self.document.createElementNS(uri, tagName)
+ else:
+ node = self.buildDocument(uri, tagName)
+ else:
+ # When the tagname is not prefixed, it just appears as
+ # localname
+ if self.document:
+ node = self.document.createElement(localname)
+ else:
+ node = self.buildDocument(None, localname)
+
+ for aname,value in attrs.items():
+ a_uri, a_localname = aname
+ if a_uri == xmlns_uri:
+ if a_localname == 'xmlns':
+ qname = a_localname
+ else:
+ qname = 'xmlns:' + a_localname
+ attr = self.document.createAttributeNS(a_uri, qname)
+ node.setAttributeNodeNS(attr)
+ elif a_uri:
+ prefix = self._current_context[a_uri]
+ if prefix:
+ qname = prefix + ":" + a_localname
+ else:
+ qname = a_localname
+ attr = self.document.createAttributeNS(a_uri, qname)
+ node.setAttributeNodeNS(attr)
+ else:
+ attr = self.document.createAttribute(a_localname)
+ node.setAttributeNode(attr)
+ attr.value = value
+
+ self.lastEvent[1] = [(START_ELEMENT, node), None]
+ self.lastEvent = self.lastEvent[1]
+ self.push(node)
+
+ PullDOM.startElementNS = startElementNS
+
+#
+# This is a runtime guerilla patch for minidom so
+# that xmlns prefixed attributes dont raise AttributeErrors
+# during cloning.
+#
+# Namespace declarations can appear in any start-tag, must look for xmlns
+# prefixed attribute names during cloning.
+#
+# key (attr.namespaceURI, tag)
+# ('http://www.w3.org/2000/xmlns/', u'xsd') <xml.dom.minidom.Attr instance at 0x82227c4>
+# ('http://www.w3.org/2000/xmlns/', 'xmlns') <xml.dom.minidom.Attr instance at 0x8414b3c>
+#
+# xml.dom.minidom.Attr.nodeName = xmlns:xsd
+# xml.dom.minidom.Attr.value = = http://www.w3.org/2001/XMLSchema
+
+if 1:
+ def _clone_node(node, deep, newOwnerDocument):
+ """
+ Clone a node and give it the new owner document.
+ Called by Node.cloneNode and Document.importNode
+ """
+ if node.ownerDocument.isSameNode(newOwnerDocument):
+ operation = xml.dom.UserDataHandler.NODE_CLONED
+ else:
+ operation = xml.dom.UserDataHandler.NODE_IMPORTED
+ if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE:
+ clone = newOwnerDocument.createElementNS(node.namespaceURI,
+ node.nodeName)
+ for attr in node.attributes.values():
+ clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value)
+
+ prefix, tag = xml.dom.minidom._nssplit(attr.nodeName)
+ if prefix == 'xmlns':
+ a = clone.getAttributeNodeNS(attr.namespaceURI, tag)
+ elif prefix:
+ a = clone.getAttributeNodeNS(attr.namespaceURI, tag)
+ else:
+ a = clone.getAttributeNodeNS(attr.namespaceURI, attr.nodeName)
+ a.specified = attr.specified
+
+ if deep:
+ for child in node.childNodes:
+ c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument)
+ clone.appendChild(c)
+ elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_FRAGMENT_NODE:
+ clone = newOwnerDocument.createDocumentFragment()
+ if deep:
+ for child in node.childNodes:
+ c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument)
+ clone.appendChild(c)
+
+ elif node.nodeType == xml.dom.minidom.Node.TEXT_NODE:
+ clone = newOwnerDocument.createTextNode(node.data)
+ elif node.nodeType == xml.dom.minidom.Node.CDATA_SECTION_NODE:
+ clone = newOwnerDocument.createCDATASection(node.data)
+ elif node.nodeType == xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE:
+ clone = newOwnerDocument.createProcessingInstruction(node.target,
+ node.data)
+ elif node.nodeType == xml.dom.minidom.Node.COMMENT_NODE:
+ clone = newOwnerDocument.createComment(node.data)
+ elif node.nodeType == xml.dom.minidom.Node.ATTRIBUTE_NODE:
+ clone = newOwnerDocument.createAttributeNS(node.namespaceURI,
+ node.nodeName)
+ clone.specified = True
+ clone.value = node.value
+ elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_TYPE_NODE:
+ assert node.ownerDocument is not newOwnerDocument
+ operation = xml.dom.UserDataHandler.NODE_IMPORTED
+ clone = newOwnerDocument.implementation.createDocumentType(
+ node.name, node.publicId, node.systemId)
+ clone.ownerDocument = newOwnerDocument
+ if deep:
+ clone.entities._seq = []
+ clone.notations._seq = []
+ for n in node.notations._seq:
+ notation = xml.dom.minidom.Notation(n.nodeName, n.publicId, n.systemId)
+ notation.ownerDocument = newOwnerDocument
+ clone.notations._seq.append(notation)
+ if hasattr(n, '_call_user_data_handler'):
+ n._call_user_data_handler(operation, n, notation)
+ for e in node.entities._seq:
+ entity = xml.dom.minidom.Entity(e.nodeName, e.publicId, e.systemId,
+ e.notationName)
+ entity.actualEncoding = e.actualEncoding
+ entity.encoding = e.encoding
+ entity.version = e.version
+ entity.ownerDocument = newOwnerDocument
+ clone.entities._seq.append(entity)
+ if hasattr(e, '_call_user_data_handler'):
+ e._call_user_data_handler(operation, n, entity)
+ else:
+ # Note the cloning of Document and DocumentType nodes is
+ # implemenetation specific. minidom handles those cases
+ # directly in the cloneNode() methods.
+ raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node))
+
+ # Check for _call_user_data_handler() since this could conceivably
+ # used with other DOM implementations (one of the FourThought
+ # DOMs, perhaps?).
+ if hasattr(node, '_call_user_data_handler'):
+ node._call_user_data_handler(operation, node, clone)
+ return clone
+
+ xml.dom.minidom._clone_node = _clone_node
+
diff --git a/ZSI/wstools/WSDLTools.py b/ZSI/wstools/WSDLTools.py
new file mode 100755
index 0000000..c37d0d8
--- /dev/null
+++ b/ZSI/wstools/WSDLTools.py
@@ -0,0 +1,1668 @@
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+
+ident = "$Id: WSDLTools.py 1122 2006-02-04 01:24:50Z boverhof $"
+
+import weakref
+from cStringIO import StringIO
+from Namespaces import OASIS, XMLNS, WSA, WSA_LIST, WSRF_V1_2, WSRF
+from Utility import Collection, CollectionNS, DOM, ElementProxy, basejoin
+from XMLSchema import XMLSchema, SchemaReader, WSDLToolsAdapter
+
+
+class WSDLReader:
+ """A WSDLReader creates WSDL instances from urls and xml data."""
+
+ # Custom subclasses of WSDLReader may wish to implement a caching
+ # strategy or other optimizations. Because application needs vary
+ # so widely, we don't try to provide any caching by default.
+
+ def loadFromStream(self, stream, name=None):
+ """Return a WSDL instance loaded from a stream object."""
+ document = DOM.loadDocument(stream)
+ wsdl = WSDL()
+ if name:
+ wsdl.location = name
+ elif hasattr(stream, 'name'):
+ wsdl.location = stream.name
+ wsdl.load(document)
+ return wsdl
+
+ def loadFromURL(self, url):
+ """Return a WSDL instance loaded from the given url."""
+ document = DOM.loadFromURL(url)
+ wsdl = WSDL()
+ wsdl.location = url
+ wsdl.load(document)
+ return wsdl
+
+ def loadFromString(self, data):
+ """Return a WSDL instance loaded from an xml string."""
+ return self.loadFromStream(StringIO(data))
+
+ def loadFromFile(self, filename):
+ """Return a WSDL instance loaded from the given file."""
+ file = open(filename, 'rb')
+ try:
+ wsdl = self.loadFromStream(file)
+ finally:
+ file.close()
+ return wsdl
+
+class WSDL:
+ """A WSDL object models a WSDL service description. WSDL objects
+ may be created manually or loaded from an xml representation
+ using a WSDLReader instance."""
+
+ def __init__(self, targetNamespace=None, strict=1):
+ self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
+ self.documentation = ''
+ self.location = None
+ self.document = None
+ self.name = None
+ self.services = CollectionNS(self)
+ self.messages = CollectionNS(self)
+ self.portTypes = CollectionNS(self)
+ self.bindings = CollectionNS(self)
+ self.imports = Collection(self)
+ self.types = Types(self)
+ self.extensions = []
+ self.strict = strict
+
+ def __del__(self):
+ if self.document is not None:
+ self.document.unlink()
+
+ version = '1.1'
+
+ def addService(self, name, documentation='', targetNamespace=None):
+ if self.services.has_key(name):
+ raise WSDLError(
+ 'Duplicate service element: %s' % name
+ )
+ item = Service(name, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.services[name] = item
+ return item
+
+ def addMessage(self, name, documentation='', targetNamespace=None):
+ if self.messages.has_key(name):
+ raise WSDLError(
+ 'Duplicate message element: %s.' % name
+ )
+ item = Message(name, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.messages[name] = item
+ return item
+
+ def addPortType(self, name, documentation='', targetNamespace=None):
+ if self.portTypes.has_key(name):
+ raise WSDLError(
+ 'Duplicate portType element: name'
+ )
+ item = PortType(name, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.portTypes[name] = item
+ return item
+
+ def addBinding(self, name, type, documentation='', targetNamespace=None):
+ if self.bindings.has_key(name):
+ raise WSDLError(
+ 'Duplicate binding element: %s' % name
+ )
+ item = Binding(name, type, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.bindings[name] = item
+ return item
+
+ def addImport(self, namespace, location):
+ item = ImportElement(namespace, location)
+ self.imports[namespace] = item
+ return item
+
+ def toDom(self):
+ """ Generate a DOM representation of the WSDL instance.
+ Not dealing with generating XML Schema, thus the targetNamespace
+ of all XML Schema elements or types used by WSDL message parts
+ needs to be specified via import information items.
+ """
+ namespaceURI = DOM.GetWSDLUri(self.version)
+ self.document = DOM.createDocument(namespaceURI ,'wsdl:definitions')
+
+ # Set up a couple prefixes for easy reading.
+ child = DOM.getElement(self.document, None)
+ child.setAttributeNS(None, 'targetNamespace', self.targetNamespace)
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:wsdl', namespaceURI)
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:xsd', 'http://www.w3.org/1999/XMLSchema')
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:soap', 'http://schemas.xmlsoap.org/wsdl/soap/')
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:tns', self.targetNamespace)
+
+ if self.name:
+ child.setAttributeNS(None, 'name', self.name)
+
+ # wsdl:import
+ for item in self.imports:
+ item.toDom()
+ # wsdl:message
+ for item in self.messages:
+ item.toDom()
+ # wsdl:portType
+ for item in self.portTypes:
+ item.toDom()
+ # wsdl:binding
+ for item in self.bindings:
+ item.toDom()
+ # wsdl:service
+ for item in self.services:
+ item.toDom()
+
+ def load(self, document):
+ # We save a reference to the DOM document to ensure that elements
+ # saved as "extensions" will continue to have a meaningful context
+ # for things like namespace references. The lifetime of the DOM
+ # document is bound to the lifetime of the WSDL instance.
+ self.document = document
+
+ definitions = DOM.getElement(document, 'definitions', None, None)
+ if definitions is None:
+ raise WSDLError(
+ 'Missing <definitions> element.'
+ )
+ self.version = DOM.WSDLUriToVersion(definitions.namespaceURI)
+ NS_WSDL = DOM.GetWSDLUri(self.version)
+
+ self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace',
+ None, None)
+ self.name = DOM.getAttr(definitions, 'name', None, None)
+ self.documentation = GetDocumentation(definitions)
+
+ #
+ # Retrieve all <wsdl:import>'s, append all children of imported
+ # document to main document. First iteration grab all original
+ # <wsdl:import>'s from document, second iteration grab all
+ # "imported" <wsdl:imports> from document, etc break out when
+ # no more <wsdl:import>'s.
+ #
+ imported = []
+ base_location = self.location
+ do_it = True
+ while do_it:
+ do_it = False
+ for element in DOM.getElements(definitions, 'import', NS_WSDL):
+ location = DOM.getAttr(element, 'location')
+
+ if base_location is not None:
+ location = basejoin(base_location, location)
+
+ if location not in imported:
+ do_it = True
+ self._import(document, element, base_location)
+ imported.append(location)
+ else:
+ definitions.removeChild(element)
+
+ base_location = None
+
+ #
+ # No more <wsdl:import>'s, now load up all other
+ # WSDL information items.
+ #
+ for element in DOM.getElements(definitions, None, None):
+ targetNamespace = DOM.getAttr(element, 'targetNamespace')
+ localName = element.localName
+
+ if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
+ if localName == 'schema':
+ tns = DOM.getAttr(element, 'targetNamespace')
+ reader = SchemaReader(base_url=self.imports[tns].location)
+ schema = reader.loadFromNode(WSDLToolsAdapter(self),
+ element)
+# schema.setBaseUrl(self.location)
+ self.types.addSchema(schema)
+ else:
+ self.extensions.append(element)
+ continue
+
+ elif localName == 'message':
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ message = self.addMessage(name, docs, targetNamespace)
+ parts = DOM.getElements(element, 'part', NS_WSDL)
+ message.load(parts)
+ continue
+
+ elif localName == 'portType':
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ ptype = self.addPortType(name, docs, targetNamespace)
+ #operations = DOM.getElements(element, 'operation', NS_WSDL)
+ #ptype.load(operations)
+ ptype.load(element)
+ continue
+
+ elif localName == 'binding':
+ name = DOM.getAttr(element, 'name')
+ type = DOM.getAttr(element, 'type', default=None)
+ if type is None:
+ raise WSDLError(
+ 'Missing type attribute for binding %s.' % name
+ )
+ type = ParseQName(type, element)
+ docs = GetDocumentation(element)
+ binding = self.addBinding(name, type, docs, targetNamespace)
+ operations = DOM.getElements(element, 'operation', NS_WSDL)
+ binding.load(operations)
+ binding.load_ex(GetExtensions(element))
+ continue
+
+ elif localName == 'service':
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ service = self.addService(name, docs, targetNamespace)
+ ports = DOM.getElements(element, 'port', NS_WSDL)
+ service.load(ports)
+ service.load_ex(GetExtensions(element))
+ continue
+
+ elif localName == 'types':
+ self.types.documentation = GetDocumentation(element)
+ base_location = DOM.getAttr(element, 'base-location')
+ if base_location:
+ element.removeAttribute('base-location')
+ base_location = base_location or self.location
+ reader = SchemaReader(base_url=base_location)
+ for item in DOM.getElements(element, None, None):
+ if item.localName == 'schema':
+ schema = reader.loadFromNode(WSDLToolsAdapter(self), item)
+ # XXX <types> could have been imported
+ #schema.setBaseUrl(self.location)
+ schema.setBaseUrl(base_location)
+ self.types.addSchema(schema)
+ else:
+ self.types.addExtension(item)
+ # XXX remove the attribute
+ # element.removeAttribute('base-location')
+ continue
+
+ def _import(self, document, element, base_location=None):
+ '''Algo take <import> element's children, clone them,
+ and add them to the main document. Support for relative
+ locations is a bit complicated. The orig document context
+ is lost, so we need to store base location in DOM elements
+ representing <types>, by creating a special temporary
+ "base-location" attribute, and <import>, by resolving
+ the relative "location" and storing it as "location".
+
+ document -- document we are loading
+ element -- DOM Element representing <import>
+ base_location -- location of document from which this
+ <import> was gleaned.
+ '''
+ namespace = DOM.getAttr(element, 'namespace', default=None)
+ location = DOM.getAttr(element, 'location', default=None)
+ if namespace is None or location is None:
+ raise WSDLError(
+ 'Invalid import element (missing namespace or location).'
+ )
+ if base_location:
+ location = basejoin(base_location, location)
+ element.setAttributeNS(None, 'location', location)
+
+ obimport = self.addImport(namespace, location)
+ obimport._loaded = 1
+
+ importdoc = DOM.loadFromURL(location)
+ try:
+ if location.find('#') > -1:
+ idref = location.split('#')[-1]
+ imported = DOM.getElementById(importdoc, idref)
+ else:
+ imported = importdoc.documentElement
+ if imported is None:
+ raise WSDLError(
+ 'Import target element not found for: %s' % location
+ )
+
+ imported_tns = DOM.findTargetNS(imported)
+ if imported_tns != namespace:
+ return
+
+ if imported.localName == 'definitions':
+ imported_nodes = imported.childNodes
+ else:
+ imported_nodes = [imported]
+ parent = element.parentNode
+
+ parent.removeChild(element)
+
+ for node in imported_nodes:
+ if node.nodeType != node.ELEMENT_NODE:
+ continue
+ child = DOM.importNode(document, node, 1)
+ parent.appendChild(child)
+ child.setAttribute('targetNamespace', namespace)
+ attrsNS = imported._attrsNS
+ for attrkey in attrsNS.keys():
+ if attrkey[0] == DOM.NS_XMLNS:
+ attr = attrsNS[attrkey].cloneNode(1)
+ child.setAttributeNode(attr)
+
+ #XXX Quick Hack, should be in WSDL Namespace.
+ if child.localName == 'import':
+ rlocation = child.getAttributeNS(None, 'location')
+ alocation = basejoin(location, rlocation)
+ child.setAttribute('location', alocation)
+ elif child.localName == 'types':
+ child.setAttribute('base-location', location)
+
+ finally:
+ importdoc.unlink()
+ return location
+
+class Element:
+ """A class that provides common functions for WSDL element classes."""
+ def __init__(self, name=None, documentation=''):
+ self.name = name
+ self.documentation = documentation
+ self.extensions = []
+
+ def addExtension(self, item):
+ item.parent = weakref.ref(self)
+ self.extensions.append(item)
+
+ def getWSDL(self):
+ """Return the WSDL object that contains this information item."""
+ parent = self
+ while 1:
+ # skip any collections
+ if isinstance(parent, WSDL):
+ return parent
+ try: parent = parent.parent()
+ except: break
+
+ return None
+
+
+class ImportElement(Element):
+ def __init__(self, namespace, location):
+ self.namespace = namespace
+ self.location = location
+
+# def getWSDL(self):
+# """Return the WSDL object that contains this Message Part."""
+# return self.parent().parent()
+
+ def toDom(self):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'import')
+ epc.setAttributeNS(None, 'namespace', self.namespace)
+ epc.setAttributeNS(None, 'location', self.location)
+
+ _loaded = None
+
+
+class Types(Collection):
+ default = lambda self,k: k.targetNamespace
+ def __init__(self, parent):
+ Collection.__init__(self, parent)
+ self.documentation = ''
+ self.extensions = []
+
+ def addSchema(self, schema):
+ name = schema.targetNamespace
+ self[name] = schema
+ return schema
+
+ def addExtension(self, item):
+ self.extensions.append(item)
+
+
+class Message(Element):
+ def __init__(self, name, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.parts = Collection(self)
+
+ def addPart(self, name, type=None, element=None):
+ if self.parts.has_key(name):
+ raise WSDLError(
+ 'Duplicate message part element: %s' % name
+ )
+ if type is None and element is None:
+ raise WSDLError(
+ 'Missing type or element attribute for part: %s' % name
+ )
+ item = MessagePart(name)
+ item.element = element
+ item.type = type
+ self.parts[name] = item
+ return item
+
+ def load(self, elements):
+ for element in elements:
+ name = DOM.getAttr(element, 'name')
+ part = MessagePart(name)
+ self.parts[name] = part
+ elemref = DOM.getAttr(element, 'element', default=None)
+ typeref = DOM.getAttr(element, 'type', default=None)
+ if typeref is None and elemref is None:
+ raise WSDLError(
+ 'No type or element attribute for part: %s' % name
+ )
+ if typeref is not None:
+ part.type = ParseTypeRef(typeref, element)
+ if elemref is not None:
+ part.element = ParseTypeRef(elemref, element)
+
+# def getElementDeclaration(self):
+# """Return the XMLSchema.ElementDeclaration instance or None"""
+# element = None
+# if self.element:
+# nsuri,name = self.element
+# wsdl = self.getWSDL()
+# if wsdl.types.has_key(nsuri) and wsdl.types[nsuri].elements.has_key(name):
+# element = wsdl.types[nsuri].elements[name]
+# return element
+#
+# def getTypeDefinition(self):
+# """Return the XMLSchema.TypeDefinition instance or None"""
+# type = None
+# if self.type:
+# nsuri,name = self.type
+# wsdl = self.getWSDL()
+# if wsdl.types.has_key(nsuri) and wsdl.types[nsuri].types.has_key(name):
+# type = wsdl.types[nsuri].types[name]
+# return type
+
+# def getWSDL(self):
+# """Return the WSDL object that contains this Message Part."""
+# return self.parent().parent()
+
+ def toDom(self):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'message')
+ epc.setAttributeNS(None, 'name', self.name)
+
+ for part in self.parts:
+ part.toDom(epc._getNode())
+
+
+class MessagePart(Element):
+ def __init__(self, name):
+ Element.__init__(self, name, '')
+ self.element = None
+ self.type = None
+
+# def getWSDL(self):
+# """Return the WSDL object that contains this Message Part."""
+# return self.parent().parent().parent().parent()
+
+ def getTypeDefinition(self):
+ wsdl = self.getWSDL()
+ nsuri,name = self.type
+ schema = wsdl.types.get(nsuri, {})
+ return schema.get(name)
+
+ def getElementDeclaration(self):
+ wsdl = self.getWSDL()
+ nsuri,name = self.element
+ schema = wsdl.types.get(nsuri, {})
+ return schema.get(name)
+
+ def toDom(self, node):
+ """node -- node representing message"""
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'part')
+ epc.setAttributeNS(None, 'name', self.name)
+
+ if self.element is not None:
+ ns,name = self.element
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(None, 'element', '%s:%s'%(prefix,name))
+ elif self.type is not None:
+ ns,name = self.type
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(None, 'type', '%s:%s'%(prefix,name))
+
+
+class PortType(Element):
+ '''PortType has a anyAttribute, thus must provide for an extensible
+ mechanism for supporting such attributes. ResourceProperties is
+ specified in WS-ResourceProperties. wsa:Action is specified in
+ WS-Address.
+
+ Instance Data:
+ name -- name attribute
+ resourceProperties -- optional. wsr:ResourceProperties attribute,
+ value is a QName this is Parsed into a (namespaceURI, name)
+ that represents a Global Element Declaration.
+ operations
+ '''
+
+ def __init__(self, name, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.operations = Collection(self)
+ self.resourceProperties = None
+
+# def getWSDL(self):
+# return self.parent().parent()
+
+ def getTargetNamespace(self):
+ return self.targetNamespace or self.getWSDL().targetNamespace
+
+ def getResourceProperties(self):
+ return self.resourceProperties
+
+ def addOperation(self, name, documentation='', parameterOrder=None):
+ item = Operation(name, documentation, parameterOrder)
+ self.operations[name] = item
+ return item
+
+ def load(self, element):
+ self.name = DOM.getAttr(element, 'name')
+ self.documentation = GetDocumentation(element)
+ self.targetNamespace = DOM.getAttr(element, 'targetNamespace')
+
+ for nsuri in WSRF_V1_2.PROPERTIES.XSD_LIST:
+ if DOM.hasAttr(element, 'ResourceProperties', nsuri):
+ rpref = DOM.getAttr(element, 'ResourceProperties', nsuri)
+ self.resourceProperties = ParseQName(rpref, element)
+
+ NS_WSDL = DOM.GetWSDLUri(self.getWSDL().version)
+ elements = DOM.getElements(element, 'operation', NS_WSDL)
+ for element in elements:
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ param_order = DOM.getAttr(element, 'parameterOrder', default=None)
+ if param_order is not None:
+ param_order = param_order.split(' ')
+ operation = self.addOperation(name, docs, param_order)
+
+ item = DOM.getElement(element, 'input', None, None)
+ if item is not None:
+ name = DOM.getAttr(item, 'name')
+ docs = GetDocumentation(item)
+ msgref = DOM.getAttr(item, 'message')
+ message = ParseQName(msgref, item)
+ for WSA in WSA_LIST:
+ action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
+ if action: break
+ operation.setInput(message, name, docs, action)
+
+ item = DOM.getElement(element, 'output', None, None)
+ if item is not None:
+ name = DOM.getAttr(item, 'name')
+ docs = GetDocumentation(item)
+ msgref = DOM.getAttr(item, 'message')
+ message = ParseQName(msgref, item)
+ for WSA in WSA_LIST:
+ action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
+ if action: break
+ operation.setOutput(message, name, docs, action)
+
+ for item in DOM.getElements(element, 'fault', None):
+ name = DOM.getAttr(item, 'name')
+ docs = GetDocumentation(item)
+ msgref = DOM.getAttr(item, 'message')
+ message = ParseQName(msgref, item)
+ for WSA in WSA_LIST:
+ action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
+ if action: break
+ operation.addFault(message, name, docs, action)
+
+ def toDom(self):
+ wsdl = self.getWSDL()
+
+ ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'portType')
+ epc.setAttributeNS(None, 'name', self.name)
+ if self.resourceProperties:
+ ns,name = self.resourceProperties
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(WSRF.PROPERTIES.LATEST, 'ResourceProperties',
+ '%s:%s'%(prefix,name))
+
+ for op in self.operations:
+ op.toDom(epc._getNode())
+
+
+
+class Operation(Element):
+ def __init__(self, name, documentation='', parameterOrder=None):
+ Element.__init__(self, name, documentation)
+ self.parameterOrder = parameterOrder
+ self.faults = Collection(self)
+ self.input = None
+ self.output = None
+
+ def getWSDL(self):
+ """Return the WSDL object that contains this Operation."""
+ return self.parent().parent().parent().parent()
+
+ def getPortType(self):
+ return self.parent().parent()
+
+ def getInputAction(self):
+ """wsa:Action attribute"""
+ return GetWSAActionInput(self)
+
+ def getInputMessage(self):
+ if self.input is None:
+ return None
+ wsdl = self.getPortType().getWSDL()
+ return wsdl.messages[self.input.message]
+
+ def getOutputAction(self):
+ """wsa:Action attribute"""
+ return GetWSAActionOutput(self)
+
+ def getOutputMessage(self):
+ if self.output is None:
+ return None
+ wsdl = self.getPortType().getWSDL()
+ return wsdl.messages[self.output.message]
+
+ def getFaultAction(self, name):
+ """wsa:Action attribute"""
+ return GetWSAActionFault(self, name)
+
+ def getFaultMessage(self, name):
+ wsdl = self.getPortType().getWSDL()
+ return wsdl.messages[self.faults[name].message]
+
+ def addFault(self, message, name, documentation='', action=None):
+ if self.faults.has_key(name):
+ raise WSDLError(
+ 'Duplicate fault element: %s' % name
+ )
+ item = MessageRole('fault', message, name, documentation, action)
+ self.faults[name] = item
+ return item
+
+ def setInput(self, message, name='', documentation='', action=None):
+ self.input = MessageRole('input', message, name, documentation, action)
+ self.input.parent = weakref.ref(self)
+ return self.input
+
+ def setOutput(self, message, name='', documentation='', action=None):
+ self.output = MessageRole('output', message, name, documentation, action)
+ self.output.parent = weakref.ref(self)
+ return self.output
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'operation')
+ epc.setAttributeNS(None, 'name', self.name)
+ node = epc._getNode()
+ if self.input:
+ self.input.toDom(node)
+ if self.output:
+ self.output.toDom(node)
+ for fault in self.faults:
+ fault.toDom(node)
+
+
+class MessageRole(Element):
+ def __init__(self, type, message, name='', documentation='', action=None):
+ Element.__init__(self, name, documentation)
+ self.message = message
+ self.type = type
+ self.action = action
+
+ def getWSDL(self):
+ """Return the WSDL object that contains this information item."""
+ parent = self
+ while 1:
+ # skip any collections
+ if isinstance(parent, WSDL):
+ return parent
+ try: parent = parent.parent()
+ except: break
+
+ return None
+
+ def getMessage(self):
+ """Return the WSDL object that represents the attribute message
+ (namespaceURI, name) tuple
+ """
+ wsdl = self.getWSDL()
+ return wsdl.messages[self.message]
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), self.type)
+ if not isinstance(self.message, basestring) and len(self.message) == 2:
+ ns,name = self.message
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(None, 'message', '%s:%s' %(prefix,name))
+ else:
+ epc.setAttributeNS(None, 'message', self.message)
+
+ if self.action:
+ epc.setAttributeNS(WSA.ADDRESS, 'Action', self.action)
+
+ if self.name:
+ epc.setAttributeNS(None, 'name', self.name)
+
+
+class Binding(Element):
+ def __init__(self, name, type, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.operations = Collection(self)
+ self.type = type
+
+# def getWSDL(self):
+# """Return the WSDL object that contains this binding."""
+# return self.parent().parent()
+
+ def getPortType(self):
+ """Return the PortType object associated with this binding."""
+ return self.getWSDL().portTypes[self.type]
+
+ def findBinding(self, kind):
+ for item in self.extensions:
+ if isinstance(item, kind):
+ return item
+ return None
+
+ def findBindings(self, kind):
+ return [ item for item in self.extensions if isinstance(item, kind) ]
+
+ def addOperationBinding(self, name, documentation=''):
+ item = OperationBinding(name, documentation)
+ self.operations[name] = item
+ return item
+
+ def load(self, elements):
+ for element in elements:
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ opbinding = self.addOperationBinding(name, docs)
+ opbinding.load_ex(GetExtensions(element))
+
+ item = DOM.getElement(element, 'input', None, None)
+ if item is not None:
+ #TODO: addInputBinding?
+ mbinding = MessageRoleBinding('input')
+ mbinding.documentation = GetDocumentation(item)
+ opbinding.input = mbinding
+ mbinding.load_ex(GetExtensions(item))
+ mbinding.parent = weakref.ref(opbinding)
+
+ item = DOM.getElement(element, 'output', None, None)
+ if item is not None:
+ mbinding = MessageRoleBinding('output')
+ mbinding.documentation = GetDocumentation(item)
+ opbinding.output = mbinding
+ mbinding.load_ex(GetExtensions(item))
+ mbinding.parent = weakref.ref(opbinding)
+
+ for item in DOM.getElements(element, 'fault', None):
+ name = DOM.getAttr(item, 'name')
+ mbinding = MessageRoleBinding('fault', name)
+ mbinding.documentation = GetDocumentation(item)
+ opbinding.faults[name] = mbinding
+ mbinding.load_ex(GetExtensions(item))
+ mbinding.parent = weakref.ref(opbinding)
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
+ transport = DOM.getAttr(e, 'transport', default=None)
+ style = DOM.getAttr(e, 'style', default='document')
+ ob = SoapBinding(transport, style)
+ self.addExtension(ob)
+ continue
+ elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
+ verb = DOM.getAttr(e, 'verb')
+ ob = HttpBinding(verb)
+ self.addExtension(ob)
+ continue
+ else:
+ self.addExtension(e)
+
+ def toDom(self):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'binding')
+ epc.setAttributeNS(None, 'name', self.name)
+
+ ns,name = self.type
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(None, 'type', '%s:%s' %(prefix,name))
+
+ node = epc._getNode()
+ for ext in self.extensions:
+ ext.toDom(node)
+ for op_binding in self.operations:
+ op_binding.toDom(node)
+
+
+class OperationBinding(Element):
+ def __init__(self, name, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.input = None
+ self.output = None
+ self.faults = Collection(self)
+
+# def getWSDL(self):
+# """Return the WSDL object that contains this binding."""
+# return self.parent().parent().parent().parent()
+
+
+ def getBinding(self):
+ """Return the parent Binding object of the operation binding."""
+ return self.parent().parent()
+
+ def getOperation(self):
+ """Return the abstract Operation associated with this binding."""
+ return self.getBinding().getPortType().operations[self.name]
+
+ def findBinding(self, kind):
+ for item in self.extensions:
+ if isinstance(item, kind):
+ return item
+ return None
+
+ def findBindings(self, kind):
+ return [ item for item in self.extensions if isinstance(item, kind) ]
+
+ def addInputBinding(self, binding):
+ if self.input is None:
+ self.input = MessageRoleBinding('input')
+ self.input.parent = weakref.ref(self)
+ self.input.addExtension(binding)
+ return binding
+
+ def addOutputBinding(self, binding):
+ if self.output is None:
+ self.output = MessageRoleBinding('output')
+ self.output.parent = weakref.ref(self)
+ self.output.addExtension(binding)
+ return binding
+
+ def addFaultBinding(self, name, binding):
+ fault = self.get(name, None)
+ if fault is None:
+ fault = MessageRoleBinding('fault', name)
+ fault.addExtension(binding)
+ return binding
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
+ soapaction = DOM.getAttr(e, 'soapAction', default=None)
+ style = DOM.getAttr(e, 'style', default=None)
+ ob = SoapOperationBinding(soapaction, style)
+ self.addExtension(ob)
+ continue
+ elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
+ location = DOM.getAttr(e, 'location')
+ ob = HttpOperationBinding(location)
+ self.addExtension(ob)
+ continue
+ else:
+ self.addExtension(e)
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'operation')
+ epc.setAttributeNS(None, 'name', self.name)
+
+ node = epc._getNode()
+ for ext in self.extensions:
+ ext.toDom(node)
+ if self.input:
+ self.input.toDom(node)
+ if self.output:
+ self.output.toDom(node)
+ for fault in self.faults:
+ fault.toDom(node)
+
+
+class MessageRoleBinding(Element):
+ def __init__(self, type, name='', documentation=''):
+ Element.__init__(self, name, documentation)
+ self.type = type
+
+ def findBinding(self, kind):
+ for item in self.extensions:
+ if isinstance(item, kind):
+ return item
+ return None
+
+ def findBindings(self, kind):
+ return [ item for item in self.extensions if isinstance(item, kind) ]
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
+ encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+ namespace = DOM.getAttr(e, 'namespace', default=None)
+ parts = DOM.getAttr(e, 'parts', default=None)
+ use = DOM.getAttr(e, 'use', default=None)
+ if use is None:
+ raise WSDLError(
+ 'Invalid soap:body binding element.'
+ )
+ ob = SoapBodyBinding(use, namespace, encstyle, parts)
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
+ encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+ namespace = DOM.getAttr(e, 'namespace', default=None)
+ name = DOM.getAttr(e, 'name', default=None)
+ use = DOM.getAttr(e, 'use', default=None)
+ if use is None or name is None:
+ raise WSDLError(
+ 'Invalid soap:fault binding element.'
+ )
+ ob = SoapFaultBinding(name, use, namespace, encstyle)
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
+ 'header', 'headerfault'
+ ):
+ encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+ namespace = DOM.getAttr(e, 'namespace', default=None)
+ message = DOM.getAttr(e, 'message')
+ part = DOM.getAttr(e, 'part')
+ use = DOM.getAttr(e, 'use')
+ if name == 'header':
+ _class = SoapHeaderBinding
+ else:
+ _class = SoapHeaderFaultBinding
+ message = ParseQName(message, e)
+ ob = _class(message, part, use, namespace, encstyle)
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
+ ob = HttpUrlReplacementBinding()
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
+ ob = HttpUrlEncodedBinding()
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
+ ob = MimeMultipartRelatedBinding()
+ self.addExtension(ob)
+ ob.load_ex(GetExtensions(e))
+ continue
+
+ elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
+ part = DOM.getAttr(e, 'part', default=None)
+ type = DOM.getAttr(e, 'type', default=None)
+ ob = MimeContentBinding(part, type)
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
+ part = DOM.getAttr(e, 'part', default=None)
+ ob = MimeXmlBinding(part)
+ self.addExtension(ob)
+ continue
+
+ else:
+ self.addExtension(e)
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), self.type)
+
+ node = epc._getNode()
+ for item in self.extensions:
+ if item: item.toDom(node)
+
+
+class Service(Element):
+ def __init__(self, name, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.ports = Collection(self)
+
+ def getWSDL(self):
+ return self.parent().parent()
+
+ def addPort(self, name, binding, documentation=''):
+ item = Port(name, binding, documentation)
+ self.ports[name] = item
+ return item
+
+ def load(self, elements):
+ for element in elements:
+ name = DOM.getAttr(element, 'name', default=None)
+ docs = GetDocumentation(element)
+ binding = DOM.getAttr(element, 'binding', default=None)
+ if name is None or binding is None:
+ raise WSDLError(
+ 'Invalid port element.'
+ )
+ binding = ParseQName(binding, element)
+ port = self.addPort(name, binding, docs)
+ port.load_ex(GetExtensions(element))
+
+ def load_ex(self, elements):
+ for e in elements:
+ self.addExtension(e)
+
+ def toDom(self):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), "service")
+ epc.setAttributeNS(None, "name", self.name)
+
+ node = epc._getNode()
+ for port in self.ports:
+ port.toDom(node)
+
+
+class Port(Element):
+ def __init__(self, name, binding, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.binding = binding
+
+# def getWSDL(self):
+# return self.parent().parent().getWSDL()
+
+ def getService(self):
+ """Return the Service object associated with this port."""
+ return self.parent().parent()
+
+ def getBinding(self):
+ """Return the Binding object that is referenced by this port."""
+ wsdl = self.getService().getWSDL()
+ return wsdl.bindings[self.binding]
+
+ def getPortType(self):
+ """Return the PortType object that is referenced by this port."""
+ wsdl = self.getService().getWSDL()
+ binding = wsdl.bindings[self.binding]
+ return wsdl.portTypes[binding.type]
+
+ def getAddressBinding(self):
+ """A convenience method to obtain the extension element used
+ as the address binding for the port."""
+ for item in self.extensions:
+ if isinstance(item, SoapAddressBinding) or \
+ isinstance(item, HttpAddressBinding):
+ return item
+ raise WSDLError(
+ 'No address binding found in port.'
+ )
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
+ location = DOM.getAttr(e, 'location', default=None)
+ ob = SoapAddressBinding(location)
+ self.addExtension(ob)
+ continue
+ elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
+ location = DOM.getAttr(e, 'location', default=None)
+ ob = HttpAddressBinding(location)
+ self.addExtension(ob)
+ continue
+ else:
+ self.addExtension(e)
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), "port")
+ epc.setAttributeNS(None, "name", self.name)
+
+ ns,name = self.binding
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(None, "binding", "%s:%s" %(prefix,name))
+
+ node = epc._getNode()
+ for ext in self.extensions:
+ ext.toDom(node)
+
+
+class SoapBinding:
+ def __init__(self, transport, style='rpc'):
+ self.transport = transport
+ self.style = style
+
+ def getWSDL(self):
+ return self.parent().getWSDL()
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'binding')
+ if self.transport:
+ epc.setAttributeNS(None, "transport", self.transport)
+ if self.style:
+ epc.setAttributeNS(None, "style", self.style)
+
+class SoapAddressBinding:
+ def __init__(self, location):
+ self.location = location
+
+ def getWSDL(self):
+ return self.parent().getWSDL()
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'address')
+ epc.setAttributeNS(None, "location", self.location)
+
+
+class SoapOperationBinding:
+ def __init__(self, soapAction=None, style=None):
+ self.soapAction = soapAction
+ self.style = style
+
+ def getWSDL(self):
+ return self.parent().getWSDL()
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'operation')
+ if self.soapAction:
+ epc.setAttributeNS(None, 'soapAction', self.soapAction)
+ if self.style:
+ epc.setAttributeNS(None, 'style', self.style)
+
+
+class SoapBodyBinding:
+ def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
+ if not use in ('literal', 'encoded'):
+ raise WSDLError(
+ 'Invalid use attribute value: %s' % use
+ )
+ self.encodingStyle = encodingStyle
+ self.namespace = namespace
+ if type(parts) in (type(''), type(u'')):
+ parts = parts.split()
+ self.parts = parts
+ self.use = use
+
+ def getWSDL(self):
+ return self.parent().getWSDL()
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'body')
+ epc.setAttributeNS(None, "use", self.use)
+ epc.setAttributeNS(None, "namespace", self.namespace)
+
+
+class SoapFaultBinding:
+ def __init__(self, name, use, namespace=None, encodingStyle=None):
+ if not use in ('literal', 'encoded'):
+ raise WSDLError(
+ 'Invalid use attribute value: %s' % use
+ )
+ self.encodingStyle = encodingStyle
+ self.namespace = namespace
+ self.name = name
+ self.use = use
+
+ def getWSDL(self):
+ return self.parent().getWSDL()
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'body')
+ epc.setAttributeNS(None, "use", self.use)
+ epc.setAttributeNS(None, "name", self.name)
+ if self.namespace is not None:
+ epc.setAttributeNS(None, "namespace", self.namespace)
+ if self.encodingStyle is not None:
+ epc.setAttributeNS(None, "encodingStyle", self.encodingStyle)
+
+
+class SoapHeaderBinding:
+ def __init__(self, message, part, use, namespace=None, encodingStyle=None):
+ if not use in ('literal', 'encoded'):
+ raise WSDLError(
+ 'Invalid use attribute value: %s' % use
+ )
+ self.encodingStyle = encodingStyle
+ self.namespace = namespace
+ self.message = message
+ self.part = part
+ self.use = use
+
+ tagname = 'header'
+
+class SoapHeaderFaultBinding(SoapHeaderBinding):
+ tagname = 'headerfault'
+
+
+class HttpBinding:
+ def __init__(self, verb):
+ self.verb = verb
+
+class HttpAddressBinding:
+ def __init__(self, location):
+ self.location = location
+
+
+class HttpOperationBinding:
+ def __init__(self, location):
+ self.location = location
+
+class HttpUrlReplacementBinding:
+ pass
+
+
+class HttpUrlEncodedBinding:
+ pass
+
+
+class MimeContentBinding:
+ def __init__(self, part=None, type=None):
+ self.part = part
+ self.type = type
+
+
+class MimeXmlBinding:
+ def __init__(self, part=None):
+ self.part = part
+
+
+class MimeMultipartRelatedBinding:
+ def __init__(self):
+ self.parts = []
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_MIME_BINDING_ALL and name == 'part':
+ self.parts.append(MimePartBinding())
+ continue
+
+
+class MimePartBinding:
+ def __init__(self):
+ self.items = []
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
+ part = DOM.getAttr(e, 'part', default=None)
+ type = DOM.getAttr(e, 'type', default=None)
+ ob = MimeContentBinding(part, type)
+ self.items.append(ob)
+ continue
+
+ elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
+ part = DOM.getAttr(e, 'part', default=None)
+ ob = MimeXmlBinding(part)
+ self.items.append(ob)
+ continue
+
+ elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
+ encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+ namespace = DOM.getAttr(e, 'namespace', default=None)
+ parts = DOM.getAttr(e, 'parts', default=None)
+ use = DOM.getAttr(e, 'use', default=None)
+ if use is None:
+ raise WSDLError(
+ 'Invalid soap:body binding element.'
+ )
+ ob = SoapBodyBinding(use, namespace, encstyle, parts)
+ self.items.append(ob)
+ continue
+
+
+class WSDLError(Exception):
+ pass
+
+
+
+def DeclareNSPrefix(writer, prefix, nsuri):
+ if writer.hasNSPrefix(nsuri):
+ return
+ writer.declareNSPrefix(prefix, nsuri)
+
+def ParseTypeRef(value, element):
+ parts = value.split(':', 1)
+ if len(parts) == 1:
+ return (DOM.findTargetNS(element), value)
+ nsuri = DOM.findNamespaceURI(parts[0], element)
+ return (nsuri, parts[1])
+
+def ParseQName(value, element):
+ nameref = value.split(':', 1)
+ if len(nameref) == 2:
+ nsuri = DOM.findNamespaceURI(nameref[0], element)
+ name = nameref[-1]
+ else:
+ nsuri = DOM.findTargetNS(element)
+ name = nameref[-1]
+ return nsuri, name
+
+def GetDocumentation(element):
+ docnode = DOM.getElement(element, 'documentation', None, None)
+ if docnode is not None:
+ return DOM.getElementText(docnode)
+ return ''
+
+def GetExtensions(element):
+ return [ item for item in DOM.getElements(element, None, None)
+ if item.namespaceURI != DOM.NS_WSDL ]
+
+def GetWSAActionFault(operation, name):
+ """Find wsa:Action attribute, and return value or WSA.FAULT
+ for the default.
+ """
+ attr = operation.faults[name].action
+ if attr is not None:
+ return attr
+ return WSA.FAULT
+
+def GetWSAActionInput(operation):
+ """Find wsa:Action attribute, and return value or the default."""
+ attr = operation.input.action
+ if attr is not None:
+ return attr
+ portType = operation.getPortType()
+ targetNamespace = portType.getTargetNamespace()
+ ptName = portType.name
+ msgName = operation.input.name
+ if not msgName:
+ msgName = operation.name + 'Request'
+ if targetNamespace.endswith('/'):
+ return '%s%s/%s' %(targetNamespace, ptName, msgName)
+ return '%s/%s/%s' %(targetNamespace, ptName, msgName)
+
+def GetWSAActionOutput(operation):
+ """Find wsa:Action attribute, and return value or the default."""
+ attr = operation.output.action
+ if attr is not None:
+ return attr
+ targetNamespace = operation.getPortType().getTargetNamespace()
+ ptName = operation.getPortType().name
+ msgName = operation.output.name
+ if not msgName:
+ msgName = operation.name + 'Response'
+ if targetNamespace.endswith('/'):
+ return '%s%s/%s' %(targetNamespace, ptName, msgName)
+ return '%s/%s/%s' %(targetNamespace, ptName, msgName)
+
+def FindExtensions(object, kind, t_type=type(())):
+ if isinstance(kind, t_type):
+ result = []
+ namespaceURI, name = kind
+ return [ item for item in object.extensions
+ if hasattr(item, 'nodeType') \
+ and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
+ and item.name == name ]
+ return [ item for item in object.extensions if isinstance(item, kind) ]
+
+def FindExtension(object, kind, t_type=type(())):
+ if isinstance(kind, t_type):
+ namespaceURI, name = kind
+ for item in object.extensions:
+ if hasattr(item, 'nodeType') \
+ and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
+ and item.name == name:
+ return item
+ else:
+ for item in object.extensions:
+ if isinstance(item, kind):
+ return item
+ return None
+
+
+class SOAPCallInfo:
+ """SOAPCallInfo captures the important binding information about a
+ SOAP operation, in a structure that is easier to work with than
+ raw WSDL structures."""
+
+ def __init__(self, methodName):
+ self.methodName = methodName
+ self.inheaders = []
+ self.outheaders = []
+ self.inparams = []
+ self.outparams = []
+ self.retval = None
+
+ encodingStyle = DOM.NS_SOAP_ENC
+ documentation = ''
+ soapAction = None
+ transport = None
+ namespace = None
+ location = None
+ use = 'encoded'
+ style = 'rpc'
+
+ def addInParameter(self, name, type, namespace=None, element_type=0):
+ """Add an input parameter description to the call info."""
+ parameter = ParameterInfo(name, type, namespace, element_type)
+ self.inparams.append(parameter)
+ return parameter
+
+ def addOutParameter(self, name, type, namespace=None, element_type=0):
+ """Add an output parameter description to the call info."""
+ parameter = ParameterInfo(name, type, namespace, element_type)
+ self.outparams.append(parameter)
+ return parameter
+
+ def setReturnParameter(self, name, type, namespace=None, element_type=0):
+ """Set the return parameter description for the call info."""
+ parameter = ParameterInfo(name, type, namespace, element_type)
+ self.retval = parameter
+ return parameter
+
+ def addInHeaderInfo(self, name, type, namespace, element_type=0,
+ mustUnderstand=0):
+ """Add an input SOAP header description to the call info."""
+ headerinfo = HeaderInfo(name, type, namespace, element_type)
+ if mustUnderstand:
+ headerinfo.mustUnderstand = 1
+ self.inheaders.append(headerinfo)
+ return headerinfo
+
+ def addOutHeaderInfo(self, name, type, namespace, element_type=0,
+ mustUnderstand=0):
+ """Add an output SOAP header description to the call info."""
+ headerinfo = HeaderInfo(name, type, namespace, element_type)
+ if mustUnderstand:
+ headerinfo.mustUnderstand = 1
+ self.outheaders.append(headerinfo)
+ return headerinfo
+
+ def getInParameters(self):
+ """Return a sequence of the in parameters of the method."""
+ return self.inparams
+
+ def getOutParameters(self):
+ """Return a sequence of the out parameters of the method."""
+ return self.outparams
+
+ def getReturnParameter(self):
+ """Return param info about the return value of the method."""
+ return self.retval
+
+ def getInHeaders(self):
+ """Return a sequence of the in headers of the method."""
+ return self.inheaders
+
+ def getOutHeaders(self):
+ """Return a sequence of the out headers of the method."""
+ return self.outheaders
+
+
+class ParameterInfo:
+ """A ParameterInfo object captures parameter binding information."""
+ def __init__(self, name, type, namespace=None, element_type=0):
+ if element_type:
+ self.element_type = 1
+ if namespace is not None:
+ self.namespace = namespace
+ self.name = name
+ self.type = type
+
+ element_type = 0
+ namespace = None
+ default = None
+
+
+class HeaderInfo(ParameterInfo):
+ """A HeaderInfo object captures SOAP header binding information."""
+ def __init__(self, name, type, namespace, element_type=None):
+ ParameterInfo.__init__(self, name, type, namespace, element_type)
+
+ mustUnderstand = 0
+ actor = None
+
+
+def callInfoFromWSDL(port, name):
+ """Return a SOAPCallInfo given a WSDL port and operation name."""
+ wsdl = port.getService().getWSDL()
+ binding = port.getBinding()
+ portType = binding.getPortType()
+ operation = portType.operations[name]
+ opbinding = binding.operations[name]
+ messages = wsdl.messages
+ callinfo = SOAPCallInfo(name)
+
+ addrbinding = port.getAddressBinding()
+ if not isinstance(addrbinding, SoapAddressBinding):
+ raise ValueError, 'Unsupported binding type.'
+ callinfo.location = addrbinding.location
+
+ soapbinding = binding.findBinding(SoapBinding)
+ if soapbinding is None:
+ raise ValueError, 'Missing soap:binding element.'
+ callinfo.transport = soapbinding.transport
+ callinfo.style = soapbinding.style or 'document'
+
+ soap_op_binding = opbinding.findBinding(SoapOperationBinding)
+ if soap_op_binding is not None:
+ callinfo.soapAction = soap_op_binding.soapAction
+ callinfo.style = soap_op_binding.style or callinfo.style
+
+ parameterOrder = operation.parameterOrder
+
+ if operation.input is not None:
+ message = messages[operation.input.message]
+ msgrole = opbinding.input
+
+ mime = msgrole.findBinding(MimeMultipartRelatedBinding)
+ if mime is not None:
+ raise ValueError, 'Mime bindings are not supported.'
+ else:
+ for item in msgrole.findBindings(SoapHeaderBinding):
+ part = messages[item.message].parts[item.part]
+ header = callinfo.addInHeaderInfo(
+ part.name,
+ part.element or part.type,
+ item.namespace,
+ element_type = part.element and 1 or 0
+ )
+ header.encodingStyle = item.encodingStyle
+
+ body = msgrole.findBinding(SoapBodyBinding)
+ if body is None:
+ raise ValueError, 'Missing soap:body binding.'
+ callinfo.encodingStyle = body.encodingStyle
+ callinfo.namespace = body.namespace
+ callinfo.use = body.use
+
+ if body.parts is not None:
+ parts = []
+ for name in body.parts:
+ parts.append(message.parts[name])
+ else:
+ parts = message.parts.values()
+
+ for part in parts:
+ callinfo.addInParameter(
+ part.name,
+ part.element or part.type,
+ element_type = part.element and 1 or 0
+ )
+
+ if operation.output is not None:
+ try:
+ message = messages[operation.output.message]
+ except KeyError:
+ if self.strict:
+ raise RuntimeError(
+ "Recieved message not defined in the WSDL schema: %s" %
+ operation.output.message)
+ else:
+ message = wsdl.addMessage(operation.output.message)
+ print "Warning:", \
+ "Recieved message not defined in the WSDL schema.", \
+ "Adding it."
+ print "Message:", operation.output.message
+
+ msgrole = opbinding.output
+
+ mime = msgrole.findBinding(MimeMultipartRelatedBinding)
+ if mime is not None:
+ raise ValueError, 'Mime bindings are not supported.'
+ else:
+ for item in msgrole.findBindings(SoapHeaderBinding):
+ part = messages[item.message].parts[item.part]
+ header = callinfo.addOutHeaderInfo(
+ part.name,
+ part.element or part.type,
+ item.namespace,
+ element_type = part.element and 1 or 0
+ )
+ header.encodingStyle = item.encodingStyle
+
+ body = msgrole.findBinding(SoapBodyBinding)
+ if body is None:
+ raise ValueError, 'Missing soap:body binding.'
+ callinfo.encodingStyle = body.encodingStyle
+ callinfo.namespace = body.namespace
+ callinfo.use = body.use
+
+ if body.parts is not None:
+ parts = []
+ for name in body.parts:
+ parts.append(message.parts[name])
+ else:
+ parts = message.parts.values()
+
+ if parts:
+ for part in parts:
+ callinfo.addOutParameter(
+ part.name,
+ part.element or part.type,
+ element_type = part.element and 1 or 0
+ )
+
+ return callinfo
diff --git a/ZSI/wstools/XMLSchema.py b/ZSI/wstools/XMLSchema.py
new file mode 100755
index 0000000..1342835
--- /dev/null
+++ b/ZSI/wstools/XMLSchema.py
@@ -0,0 +1,3081 @@
+# Copyright (c) 2003, The Regents of the University of California,
+# through Lawrence Berkeley National Laboratory (subject to receipt of
+# any required approvals from the U.S. Dept. of Energy). All rights
+# reserved.
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+
+ident = "$Id: XMLSchema.py 1207 2006-05-04 18:34:01Z boverhof $"
+
+import types, weakref, sys, warnings
+from Namespaces import SCHEMA, XMLNS
+from Utility import DOM, DOMException, Collection, SplitQName, basejoin
+from StringIO import StringIO
+
+# If we have no threading, this should be a no-op
+try:
+ from threading import RLock
+except ImportError:
+ class RLock:
+ def acquire():
+ pass
+ def release():
+ pass
+
+#
+# Collections in XMLSchema class
+#
+TYPES = 'types'
+ATTRIBUTE_GROUPS = 'attr_groups'
+ATTRIBUTES = 'attr_decl'
+ELEMENTS = 'elements'
+MODEL_GROUPS = 'model_groups'
+
+
+def GetSchema(component):
+ """convience function for finding the parent XMLSchema instance.
+ """
+ parent = component
+ while not isinstance(parent, XMLSchema):
+ parent = parent._parent()
+ return parent
+
+class SchemaReader:
+ """A SchemaReader creates XMLSchema objects from urls and xml data.
+ """
+
+ namespaceToSchema = {}
+
+ def __init__(self, domReader=None, base_url=None):
+ """domReader -- class must implement DOMAdapterInterface
+ base_url -- base url string
+ """
+ self.__base_url = base_url
+ self.__readerClass = domReader
+ if not self.__readerClass:
+ self.__readerClass = DOMAdapter
+ self._includes = {}
+ self._imports = {}
+
+ def __setImports(self, schema):
+ """Add dictionary of imports to schema instance.
+ schema -- XMLSchema instance
+ """
+ for ns,val in schema.imports.items():
+ if self._imports.has_key(ns):
+ schema.addImportSchema(self._imports[ns])
+
+ def __setIncludes(self, schema):
+ """Add dictionary of includes to schema instance.
+ schema -- XMLSchema instance
+ """
+ for schemaLocation, val in schema.includes.items():
+ if self._includes.has_key(schemaLocation):
+ schema.addIncludeSchema(self._imports[schemaLocation])
+
+ def addSchemaByLocation(self, location, schema):
+ """provide reader with schema document for a location.
+ """
+ self._includes[location] = schema
+
+ def addSchemaByNamespace(self, schema):
+ """provide reader with schema document for a targetNamespace.
+ """
+ self._imports[schema.targetNamespace] = schema
+
+ def loadFromNode(self, parent, element):
+ """element -- DOM node or document
+ parent -- WSDLAdapter instance
+ """
+ reader = self.__readerClass(element)
+ schema = XMLSchema(parent)
+ #HACK to keep a reference
+ schema.wsdl = parent
+ schema.setBaseUrl(self.__base_url)
+ schema.load(reader)
+ return schema
+
+ def loadFromStream(self, file, url=None):
+ """Return an XMLSchema instance loaded from a file object.
+ file -- file object
+ url -- base location for resolving imports/includes.
+ """
+ reader = self.__readerClass()
+ reader.loadDocument(file)
+ schema = XMLSchema()
+ if url is not None:
+ schema.setBaseUrl(url)
+ schema.load(reader)
+ self.__setIncludes(schema)
+ self.__setImports(schema)
+ return schema
+
+ def loadFromString(self, data):
+ """Return an XMLSchema instance loaded from an XML string.
+ data -- XML string
+ """
+ return self.loadFromStream(StringIO(data))
+
+ def loadFromURL(self, url, schema=None):
+ """Return an XMLSchema instance loaded from the given url.
+ url -- URL to dereference
+ schema -- Optional XMLSchema instance.
+ """
+ reader = self.__readerClass()
+ if self.__base_url:
+ url = basejoin(self.__base_url,url)
+
+ reader.loadFromURL(url)
+ schema = schema or XMLSchema()
+ schema.setBaseUrl(url)
+ schema.load(reader)
+ self.__setIncludes(schema)
+ self.__setImports(schema)
+ return schema
+
+ def loadFromFile(self, filename):
+ """Return an XMLSchema instance loaded from the given file.
+ filename -- name of file to open
+ """
+ if self.__base_url:
+ filename = basejoin(self.__base_url,filename)
+ file = open(filename, 'rb')
+ try:
+ schema = self.loadFromStream(file, filename)
+ finally:
+ file.close()
+
+ return schema
+
+
+class SchemaError(Exception):
+ pass
+
+
+###########################
+# DOM Utility Adapters
+##########################
+class DOMAdapterInterface:
+ def hasattr(self, attr, ns=None):
+ """return true if node has attribute
+ attr -- attribute to check for
+ ns -- namespace of attribute, by default None
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def getContentList(self, *contents):
+ """returns an ordered list of child nodes
+ *contents -- list of node names to return
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def setAttributeDictionary(self, attributes):
+ """set attribute dictionary
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def getAttributeDictionary(self):
+ """returns a dict of node's attributes
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def getNamespace(self, prefix):
+ """returns namespace referenced by prefix.
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def getTagName(self):
+ """returns tagName of node
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+
+ def getParentNode(self):
+ """returns parent element in DOMAdapter or None
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def loadDocument(self, file):
+ """load a Document from a file object
+ file --
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def loadFromURL(self, url):
+ """load a Document from an url
+ url -- URL to dereference
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+
+class DOMAdapter(DOMAdapterInterface):
+ """Adapter for ZSI.Utility.DOM
+ """
+ def __init__(self, node=None):
+ """Reset all instance variables.
+ element -- DOM document, node, or None
+ """
+ if hasattr(node, 'documentElement'):
+ self.__node = node.documentElement
+ else:
+ self.__node = node
+ self.__attributes = None
+
+ def getNode(self):
+ return self.__node
+
+ def hasattr(self, attr, ns=None):
+ """attr -- attribute
+ ns -- optional namespace, None means unprefixed attribute.
+ """
+ if not self.__attributes:
+ self.setAttributeDictionary()
+ if ns:
+ return self.__attributes.get(ns,{}).has_key(attr)
+ return self.__attributes.has_key(attr)
+
+ def getContentList(self, *contents):
+ nodes = []
+ ELEMENT_NODE = self.__node.ELEMENT_NODE
+ for child in DOM.getElements(self.__node, None):
+ if child.nodeType == ELEMENT_NODE and\
+ SplitQName(child.tagName)[1] in contents:
+ nodes.append(child)
+ return map(self.__class__, nodes)
+
+ def setAttributeDictionary(self):
+ self.__attributes = {}
+ for v in self.__node._attrs.values():
+ self.__attributes[v.nodeName] = v.nodeValue
+
+ def getAttributeDictionary(self):
+ if not self.__attributes:
+ self.setAttributeDictionary()
+ return self.__attributes
+
+ def getTagName(self):
+ return self.__node.tagName
+
+ def getParentNode(self):
+ if self.__node.parentNode.nodeType == self.__node.ELEMENT_NODE:
+ return DOMAdapter(self.__node.parentNode)
+ return None
+
+ def getNamespace(self, prefix):
+ """prefix -- deference namespace prefix in node's context.
+ Ascends parent nodes until found.
+ """
+ namespace = None
+ if prefix == 'xmlns':
+ namespace = DOM.findDefaultNS(prefix, self.__node)
+ else:
+ try:
+ namespace = DOM.findNamespaceURI(prefix, self.__node)
+ except DOMException, ex:
+ if prefix != 'xml':
+ raise SchemaError, '%s namespace not declared for %s'\
+ %(prefix, self.__node._get_tagName())
+ namespace = XMLNS.XML
+ return namespace
+
+ def loadDocument(self, file):
+ self.__node = DOM.loadDocument(file)
+ if hasattr(self.__node, 'documentElement'):
+ self.__node = self.__node.documentElement
+
+ def loadFromURL(self, url):
+ self.__node = DOM.loadFromURL(url)
+ if hasattr(self.__node, 'documentElement'):
+ self.__node = self.__node.documentElement
+
+
+class XMLBase:
+ """ These class variables are for string indentation.
+ """
+ tag = None
+ __indent = 0
+ __rlock = RLock()
+
+ def __str__(self):
+ XMLBase.__rlock.acquire()
+ XMLBase.__indent += 1
+ tmp = "<" + str(self.__class__) + '>\n'
+ for k,v in self.__dict__.items():
+ tmp += "%s* %s = %s\n" %(XMLBase.__indent*' ', k, v)
+ XMLBase.__indent -= 1
+ XMLBase.__rlock.release()
+ return tmp
+
+
+"""Marker Interface: can determine something about an instances properties by using
+ the provided convenience functions.
+
+"""
+class DefinitionMarker:
+ """marker for definitions
+ """
+ pass
+
+class DeclarationMarker:
+ """marker for declarations
+ """
+ pass
+
+class AttributeMarker:
+ """marker for attributes
+ """
+ pass
+
+class AttributeGroupMarker:
+ """marker for attribute groups
+ """
+ pass
+
+class WildCardMarker:
+ """marker for wildcards
+ """
+ pass
+
+class ElementMarker:
+ """marker for wildcards
+ """
+ pass
+
+class ReferenceMarker:
+ """marker for references
+ """
+ pass
+
+class ModelGroupMarker:
+ """marker for model groups
+ """
+ pass
+
+class AllMarker(ModelGroupMarker):
+ """marker for all model group
+ """
+ pass
+
+class ChoiceMarker(ModelGroupMarker):
+ """marker for choice model group
+ """
+ pass
+
+class SequenceMarker(ModelGroupMarker):
+ """marker for sequence model group
+ """
+ pass
+
+class ExtensionMarker:
+ """marker for extensions
+ """
+ pass
+
+class RestrictionMarker:
+ """marker for restrictions
+ """
+ facets = ['enumeration', 'length', 'maxExclusive', 'maxInclusive',\
+ 'maxLength', 'minExclusive', 'minInclusive', 'minLength',\
+ 'pattern', 'fractionDigits', 'totalDigits', 'whiteSpace']
+
+class SimpleMarker:
+ """marker for simple type information
+ """
+ pass
+
+class ListMarker:
+ """marker for simple type list
+ """
+ pass
+
+class UnionMarker:
+ """marker for simple type Union
+ """
+ pass
+
+
+class ComplexMarker:
+ """marker for complex type information
+ """
+ pass
+
+class LocalMarker:
+ """marker for complex type information
+ """
+ pass
+
+
+class MarkerInterface:
+ def isDefinition(self):
+ return isinstance(self, DefinitionMarker)
+
+ def isDeclaration(self):
+ return isinstance(self, DeclarationMarker)
+
+ def isAttribute(self):
+ return isinstance(self, AttributeMarker)
+
+ def isAttributeGroup(self):
+ return isinstance(self, AttributeGroupMarker)
+
+ def isElement(self):
+ return isinstance(self, ElementMarker)
+
+ def isReference(self):
+ return isinstance(self, ReferenceMarker)
+
+ def isWildCard(self):
+ return isinstance(self, WildCardMarker)
+
+ def isModelGroup(self):
+ return isinstance(self, ModelGroupMarker)
+
+ def isAll(self):
+ return isinstance(self, AllMarker)
+
+ def isChoice(self):
+ return isinstance(self, ChoiceMarker)
+
+ def isSequence(self):
+ return isinstance(self, SequenceMarker)
+
+ def isExtension(self):
+ return isinstance(self, ExtensionMarker)
+
+ def isRestriction(self):
+ return isinstance(self, RestrictionMarker)
+
+ def isSimple(self):
+ return isinstance(self, SimpleMarker)
+
+ def isComplex(self):
+ return isinstance(self, ComplexMarker)
+
+ def isLocal(self):
+ return isinstance(self, LocalMarker)
+
+ def isList(self):
+ return isinstance(self, ListMarker)
+
+ def isUnion(self):
+ return isinstance(self, UnionMarker)
+
+
+##########################################################
+# Schema Components
+#########################################################
+class XMLSchemaComponent(XMLBase, MarkerInterface):
+ """
+ class variables:
+ required -- list of required attributes
+ attributes -- dict of default attribute values, including None.
+ Value can be a function for runtime dependencies.
+ contents -- dict of namespace keyed content lists.
+ 'xsd' content of xsd namespace.
+ xmlns_key -- key for declared xmlns namespace.
+ xmlns -- xmlns is special prefix for namespace dictionary
+ xml -- special xml prefix for xml namespace.
+ """
+ required = []
+ attributes = {}
+ contents = {}
+ xmlns_key = ''
+ xmlns = 'xmlns'
+ xml = 'xml'
+
+ def __init__(self, parent=None):
+ """parent -- parent instance
+ instance variables:
+ attributes -- dictionary of node's attributes
+ """
+ self.attributes = None
+ self._parent = parent
+ if self._parent:
+ self._parent = weakref.ref(parent)
+
+ if not self.__class__ == XMLSchemaComponent\
+ and not (type(self.__class__.required) == type(XMLSchemaComponent.required)\
+ and type(self.__class__.attributes) == type(XMLSchemaComponent.attributes)\
+ and type(self.__class__.contents) == type(XMLSchemaComponent.contents)):
+ raise RuntimeError, 'Bad type for a class variable in %s' %self.__class__
+
+ def getItemTrace(self):
+ """Returns a node trace up to the <schema> item.
+ """
+ item, path, name, ref = self, [], 'name', 'ref'
+ while not isinstance(item,XMLSchema) and not isinstance(item,WSDLToolsAdapter):
+ attr = item.getAttribute(name)
+ if attr is None:
+ attr = item.getAttribute(ref)
+ if attr is None: path.append('<%s>' %(item.tag))
+ else: path.append('<%s ref="%s">' %(item.tag, attr))
+ else:
+ path.append('<%s name="%s">' %(item.tag,attr))
+ item = item._parent()
+ try:
+ tns = item.getTargetNamespace()
+ except:
+ tns = ''
+ path.append('<%s targetNamespace="%s">' %(item.tag, tns))
+ path.reverse()
+ return ''.join(path)
+
+ def getTargetNamespace(self):
+ """return targetNamespace
+ """
+ parent = self
+ targetNamespace = 'targetNamespace'
+ tns = self.attributes.get(targetNamespace)
+ while not tns:
+ parent = parent._parent()
+ tns = parent.attributes.get(targetNamespace)
+ return tns
+
+ def getAttributeDeclaration(self, attribute):
+ """attribute -- attribute with a QName value (eg. type).
+ collection -- check types collection in parent Schema instance
+ """
+ return self.getQNameAttribute(ATTRIBUTES, attribute)
+
+ def getAttributeGroup(self, attribute):
+ """attribute -- attribute with a QName value (eg. type).
+ collection -- check types collection in parent Schema instance
+ """
+ return self.getQNameAttribute(ATTRIBUTE_GROUPS, attribute)
+
+ def getTypeDefinition(self, attribute):
+ """attribute -- attribute with a QName value (eg. type).
+ collection -- check types collection in parent Schema instance
+ """
+ return self.getQNameAttribute(TYPES, attribute)
+
+ def getElementDeclaration(self, attribute):
+ """attribute -- attribute with a QName value (eg. element).
+ collection -- check elements collection in parent Schema instance.
+ """
+ return self.getQNameAttribute(ELEMENTS, attribute)
+
+ def getModelGroup(self, attribute):
+ """attribute -- attribute with a QName value (eg. ref).
+ collection -- check model_group collection in parent Schema instance.
+ """
+ return self.getQNameAttribute(MODEL_GROUPS, attribute)
+
+ def getQNameAttribute(self, collection, attribute):
+ """returns object instance representing QName --> (namespace,name),
+ or if does not exist return None.
+ attribute -- an information item attribute, with a QName value.
+ collection -- collection in parent Schema instance to search.
+ """
+ obj = None
+ tdc = self.getAttributeQName(attribute)
+ if tdc:
+ obj = self.getSchemaItem(collection, tdc.getTargetNamespace(), tdc.getName())
+
+ return obj
+
+ def getSchemaItem(self, collection, namespace, name):
+ """returns object instance representing namespace, name,
+ or if does not exist return None.
+ namespace -- namespace item defined in.
+ name -- name of item.
+ collection -- collection in parent Schema instance to search.
+ """
+ parent = GetSchema(self)
+ if parent.targetNamespace == namespace:
+ try:
+ obj = getattr(parent, collection)[name]
+ except KeyError, ex:
+ raise KeyError, 'targetNamespace(%s) collection(%s) has no item(%s)'\
+ %(namespace, collection, name)
+
+ return obj
+
+ if not parent.imports.has_key(namespace):
+ return None
+
+ # Lazy Eval
+ schema = parent.imports[namespace]
+ if not isinstance(schema, XMLSchema):
+ schema = schema.getSchema()
+ if schema is not None:
+ parent.imports[namespace] = schema
+
+ if schema is None:
+ raise SchemaError, 'no schema instance for imported namespace (%s).'\
+ %(namespace)
+
+ if not isinstance(schema, XMLSchema):
+ raise TypeError, 'expecting XMLSchema instance not "%r"' %schema
+
+ try:
+ obj = getattr(schema, collection)[name]
+ except KeyError, ex:
+ raise KeyError, 'targetNamespace(%s) collection(%s) has no item(%s)'\
+ %(namespace, collection, name)
+
+ return obj
+
+ def getXMLNS(self, prefix=None):
+ """deference prefix or by default xmlns, returns namespace.
+ """
+ if prefix == XMLSchemaComponent.xml:
+ return XMLNS.XML
+ parent = self
+ ns = self.attributes[XMLSchemaComponent.xmlns].get(prefix or\
+ XMLSchemaComponent.xmlns_key)
+ while not ns:
+ parent = parent._parent()
+ ns = parent.attributes[XMLSchemaComponent.xmlns].get(prefix or\
+ XMLSchemaComponent.xmlns_key)
+ if not ns and isinstance(parent, WSDLToolsAdapter):
+ if prefix is None:
+ return ''
+ raise SchemaError, 'unknown prefix %s' %prefix
+ return ns
+
+ def getAttribute(self, attribute):
+ """return requested attribute value or None
+ """
+ if type(attribute) in (list, tuple):
+ if len(attribute) != 2:
+ raise LookupError, 'To access attributes must use name or (namespace,name)'
+
+ return self.attributes.get(attribute[0]).get(attribute[1])
+
+ return self.attributes.get(attribute)
+
+ def getAttributeQName(self, attribute):
+ """return requested attribute value as (namespace,name) or None
+ """
+ qname = self.getAttribute(attribute)
+ if isinstance(qname, TypeDescriptionComponent) is True:
+ return qname
+ if qname is None:
+ return None
+
+ prefix,ncname = SplitQName(qname)
+ namespace = self.getXMLNS(prefix)
+ return TypeDescriptionComponent((namespace,ncname))
+
+ def getAttributeName(self):
+ """return attribute name or None
+ """
+ return self.getAttribute('name')
+
+ def setAttributes(self, node):
+ """Sets up attribute dictionary, checks for required attributes and
+ sets default attribute values. attr is for default attribute values
+ determined at runtime.
+
+ structure of attributes dictionary
+ ['xmlns'][xmlns_key] -- xmlns namespace
+ ['xmlns'][prefix] -- declared namespace prefix
+ [namespace][prefix] -- attributes declared in a namespace
+ [attribute] -- attributes w/o prefix, default namespaces do
+ not directly apply to attributes, ie Name can't collide
+ with QName.
+ """
+ self.attributes = {XMLSchemaComponent.xmlns:{}}
+ for k,v in node.getAttributeDictionary().items():
+ prefix,value = SplitQName(k)
+ if value == XMLSchemaComponent.xmlns:
+ self.attributes[value][prefix or XMLSchemaComponent.xmlns_key] = v
+ elif prefix:
+ ns = node.getNamespace(prefix)
+ if not ns:
+ raise SchemaError, 'no namespace for attribute prefix %s'\
+ %prefix
+ if not self.attributes.has_key(ns):
+ self.attributes[ns] = {}
+ elif self.attributes[ns].has_key(value):
+ raise SchemaError, 'attribute %s declared multiple times in %s'\
+ %(value, ns)
+ self.attributes[ns][value] = v
+ elif not self.attributes.has_key(value):
+ self.attributes[value] = v
+ else:
+ raise SchemaError, 'attribute %s declared multiple times' %value
+
+ if not isinstance(self, WSDLToolsAdapter):
+ self.__checkAttributes()
+ self.__setAttributeDefaults()
+
+ #set QNames
+ for k in ['type', 'element', 'base', 'ref', 'substitutionGroup', 'itemType']:
+ if self.attributes.has_key(k):
+ prefix, value = SplitQName(self.attributes.get(k))
+ self.attributes[k] = \
+ TypeDescriptionComponent((self.getXMLNS(prefix), value))
+
+ #Union, memberTypes is a whitespace separated list of QNames
+ for k in ['memberTypes']:
+ if self.attributes.has_key(k):
+ qnames = self.attributes[k]
+ self.attributes[k] = []
+ for qname in qnames.split():
+ prefix, value = SplitQName(qname)
+ self.attributes['memberTypes'].append(\
+ TypeDescriptionComponent(\
+ (self.getXMLNS(prefix), value)))
+
+ def getContents(self, node):
+ """retrieve xsd contents
+ """
+ return node.getContentList(*self.__class__.contents['xsd'])
+
+ def __setAttributeDefaults(self):
+ """Looks for default values for unset attributes. If
+ class variable representing attribute is None, then
+ it must be defined as an instance variable.
+ """
+ for k,v in self.__class__.attributes.items():
+ if v is not None and self.attributes.has_key(k) is False:
+ if isinstance(v, types.FunctionType):
+ self.attributes[k] = v(self)
+ else:
+ self.attributes[k] = v
+
+ def __checkAttributes(self):
+ """Checks that required attributes have been defined,
+ attributes w/default cannot be required. Checks
+ all defined attributes are legal, attribute
+ references are not subject to this test.
+ """
+ for a in self.__class__.required:
+ if not self.attributes.has_key(a):
+ raise SchemaError,\
+ 'class instance %s, missing required attribute %s'\
+ %(self.__class__, a)
+ for a,v in self.attributes.items():
+ # attribute #other, ie. not in empty namespace
+ if type(v) is dict:
+ continue
+
+ # predefined prefixes xmlns, xml
+ if a in (XMLSchemaComponent.xmlns, XMLNS.XML):
+ continue
+
+ if (a not in self.__class__.attributes.keys()) and not\
+ (self.isAttribute() and self.isReference()):
+ raise SchemaError, '%s, unknown attribute(%s,%s)' \
+ %(self.getItemTrace(), a, self.attributes[a])
+
+
+class WSDLToolsAdapter(XMLSchemaComponent):
+ """WSDL Adapter to grab the attributes from the wsdl document node.
+ """
+ attributes = {'name':None, 'targetNamespace':None}
+ tag = 'definitions'
+
+ def __init__(self, wsdl):
+ XMLSchemaComponent.__init__(self, parent=wsdl)
+ self.setAttributes(DOMAdapter(wsdl.document))
+
+ def getImportSchemas(self):
+ """returns WSDLTools.WSDL types Collection
+ """
+ return self._parent().types
+
+
+class Notation(XMLSchemaComponent):
+ """<notation>
+ parent:
+ schema
+ attributes:
+ id -- ID
+ name -- NCName, Required
+ public -- token, Required
+ system -- anyURI
+ contents:
+ annotation?
+ """
+ required = ['name', 'public']
+ attributes = {'id':None, 'name':None, 'public':None, 'system':None}
+ contents = {'xsd':('annotation')}
+ tag = 'notation'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class Annotation(XMLSchemaComponent):
+ """<annotation>
+ parent:
+ all,any,anyAttribute,attribute,attributeGroup,choice,complexContent,
+ complexType,element,extension,field,group,import,include,key,keyref,
+ list,notation,redefine,restriction,schema,selector,simpleContent,
+ simpleType,union,unique
+ attributes:
+ id -- ID
+ contents:
+ (documentation | appinfo)*
+ """
+ attributes = {'id':None}
+ contents = {'xsd':('documentation', 'appinfo')}
+ tag = 'annotation'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'documentation':
+ #print_debug('class %s, documentation skipped' %self.__class__, 5)
+ continue
+ elif component == 'appinfo':
+ #print_debug('class %s, appinfo skipped' %self.__class__, 5)
+ continue
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+ class Documentation(XMLSchemaComponent):
+ """<documentation>
+ parent:
+ annotation
+ attributes:
+ source, anyURI
+ xml:lang, language
+ contents:
+ mixed, any
+ """
+ attributes = {'source':None, 'xml:lang':None}
+ contents = {'xsd':('mixed', 'any')}
+ tag = 'documentation'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'mixed':
+ #print_debug('class %s, mixed skipped' %self.__class__, 5)
+ continue
+ elif component == 'any':
+ #print_debug('class %s, any skipped' %self.__class__, 5)
+ continue
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+ class Appinfo(XMLSchemaComponent):
+ """<appinfo>
+ parent:
+ annotation
+ attributes:
+ source, anyURI
+ contents:
+ mixed, any
+ """
+ attributes = {'source':None, 'anyURI':None}
+ contents = {'xsd':('mixed', 'any')}
+ tag = 'appinfo'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'mixed':
+ #print_debug('class %s, mixed skipped' %self.__class__, 5)
+ continue
+ elif component == 'any':
+ #print_debug('class %s, any skipped' %self.__class__, 5)
+ continue
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+class XMLSchemaFake:
+ # This is temporary, for the benefit of WSDL until the real thing works.
+ def __init__(self, element):
+ self.targetNamespace = DOM.getAttr(element, 'targetNamespace')
+ self.element = element
+
+class XMLSchema(XMLSchemaComponent):
+ """A schema is a collection of schema components derived from one
+ or more schema documents, that is, one or more <schema> element
+ information items. It represents the abstract notion of a schema
+ rather than a single schema document (or other representation).
+
+ <schema>
+ parent:
+ ROOT
+ attributes:
+ id -- ID
+ version -- token
+ xml:lang -- language
+ targetNamespace -- anyURI
+ attributeFormDefault -- 'qualified' | 'unqualified', 'unqualified'
+ elementFormDefault -- 'qualified' | 'unqualified', 'unqualified'
+ blockDefault -- '#all' | list of
+ ('substitution | 'extension' | 'restriction')
+ finalDefault -- '#all' | list of
+ ('extension' | 'restriction' | 'list' | 'union')
+
+ contents:
+ ((include | import | redefine | annotation)*,
+ (attribute, attributeGroup, complexType, element, group,
+ notation, simpleType)*, annotation*)*
+
+
+ attributes -- schema attributes
+ imports -- import statements
+ includes -- include statements
+ redefines --
+ types -- global simpleType, complexType definitions
+ elements -- global element declarations
+ attr_decl -- global attribute declarations
+ attr_groups -- attribute Groups
+ model_groups -- model Groups
+ notations -- global notations
+ """
+ attributes = {'id':None,
+ 'version':None,
+ 'xml:lang':None,
+ 'targetNamespace':None,
+ 'attributeFormDefault':'unqualified',
+ 'elementFormDefault':'unqualified',
+ 'blockDefault':None,
+ 'finalDefault':None}
+ contents = {'xsd':('include', 'import', 'redefine', 'annotation',
+ 'attribute', 'attributeGroup', 'complexType',
+ 'element', 'group', 'notation', 'simpleType',
+ 'annotation')}
+ empty_namespace = ''
+ tag = 'schema'
+
+ def __init__(self, parent=None):
+ """parent --
+ instance variables:
+ targetNamespace -- schema's declared targetNamespace, or empty string.
+ _imported_schemas -- namespace keyed dict of schema dependencies, if
+ a schema is provided instance will not resolve import statement.
+ _included_schemas -- schemaLocation keyed dict of component schemas,
+ if schema is provided instance will not resolve include statement.
+ _base_url -- needed for relative URLs support, only works with URLs
+ relative to initial document.
+ includes -- collection of include statements
+ imports -- collection of import statements
+ elements -- collection of global element declarations
+ types -- collection of global type definitions
+ attr_decl -- collection of global attribute declarations
+ attr_groups -- collection of global attribute group definitions
+ model_groups -- collection of model group definitions
+ notations -- collection of notations
+
+ """
+ self.__node = None
+ self.targetNamespace = None
+ XMLSchemaComponent.__init__(self, parent)
+ f = lambda k: k.attributes['name']
+ ns = lambda k: k.attributes['namespace']
+ sl = lambda k: k.attributes['schemaLocation']
+ self.includes = Collection(self, key=sl)
+ self.imports = Collection(self, key=ns)
+ self.elements = Collection(self, key=f)
+ self.types = Collection(self, key=f)
+ self.attr_decl = Collection(self, key=f)
+ self.attr_groups = Collection(self, key=f)
+ self.model_groups = Collection(self, key=f)
+ self.notations = Collection(self, key=f)
+
+ self._imported_schemas = {}
+ self._included_schemas = {}
+ self._base_url = None
+
+ def getNode(self):
+ """
+ Interacting with the underlying DOM tree.
+ """
+ return self.__node
+
+ def addImportSchema(self, schema):
+ """for resolving import statements in Schema instance
+ schema -- schema instance
+ _imported_schemas
+ """
+ if not isinstance(schema, XMLSchema):
+ raise TypeError, 'expecting a Schema instance'
+ if schema.targetNamespace != self.targetNamespace:
+ self._imported_schemas[schema.targetNamespace] = schema
+ else:
+ raise SchemaError, 'import schema bad targetNamespace'
+
+ def addIncludeSchema(self, schemaLocation, schema):
+ """for resolving include statements in Schema instance
+ schemaLocation -- schema location
+ schema -- schema instance
+ _included_schemas
+ """
+ if not isinstance(schema, XMLSchema):
+ raise TypeError, 'expecting a Schema instance'
+ if not schema.targetNamespace or\
+ schema.targetNamespace == self.targetNamespace:
+ self._included_schemas[schemaLocation] = schema
+ else:
+ raise SchemaError, 'include schema bad targetNamespace'
+
+ def setImportSchemas(self, schema_dict):
+ """set the import schema dictionary, which is used to
+ reference depedent schemas.
+ """
+ self._imported_schemas = schema_dict
+
+ def getImportSchemas(self):
+ """get the import schema dictionary, which is used to
+ reference depedent schemas.
+ """
+ return self._imported_schemas
+
+ def getSchemaNamespacesToImport(self):
+ """returns tuple of namespaces the schema instance has declared
+ itself to be depedent upon.
+ """
+ return tuple(self.includes.keys())
+
+ def setIncludeSchemas(self, schema_dict):
+ """set the include schema dictionary, which is keyed with
+ schemaLocation (uri).
+ This is a means of providing
+ schemas to the current schema for content inclusion.
+ """
+ self._included_schemas = schema_dict
+
+ def getIncludeSchemas(self):
+ """get the include schema dictionary, which is keyed with
+ schemaLocation (uri).
+ """
+ return self._included_schemas
+
+ def getBaseUrl(self):
+ """get base url, used for normalizing all relative uri's
+ """
+ return self._base_url
+
+ def setBaseUrl(self, url):
+ """set base url, used for normalizing all relative uri's
+ """
+ self._base_url = url
+
+ def getElementFormDefault(self):
+ """return elementFormDefault attribute
+ """
+ return self.attributes.get('elementFormDefault')
+
+ def isElementFormDefaultQualified(self):
+ return self.attributes.get('elementFormDefault') == 'qualified'
+
+ def getAttributeFormDefault(self):
+ """return attributeFormDefault attribute
+ """
+ return self.attributes.get('attributeFormDefault')
+
+ def getBlockDefault(self):
+ """return blockDefault attribute
+ """
+ return self.attributes.get('blockDefault')
+
+ def getFinalDefault(self):
+ """return finalDefault attribute
+ """
+ return self.attributes.get('finalDefault')
+
+ def load(self, node, location=None):
+ self.__node = node
+
+ pnode = node.getParentNode()
+ if pnode:
+ pname = SplitQName(pnode.getTagName())[1]
+ if pname == 'types':
+ attributes = {}
+ self.setAttributes(pnode)
+ attributes.update(self.attributes)
+ self.setAttributes(node)
+ for k,v in attributes['xmlns'].items():
+ if not self.attributes['xmlns'].has_key(k):
+ self.attributes['xmlns'][k] = v
+ else:
+ self.setAttributes(node)
+ else:
+ self.setAttributes(node)
+
+ self.targetNamespace = self.getTargetNamespace()
+ for childNode in self.getContents(node):
+ component = SplitQName(childNode.getTagName())[1]
+
+ if component == 'include':
+ tp = self.__class__.Include(self)
+ tp.fromDom(childNode)
+
+ sl = tp.attributes['schemaLocation']
+ schema = tp.getSchema()
+
+ if not self.getIncludeSchemas().has_key(sl):
+ self.addIncludeSchema(sl, schema)
+
+ self.includes[sl] = tp
+
+ pn = childNode.getParentNode().getNode()
+ pn.removeChild(childNode.getNode())
+ for child in schema.getNode().getNode().childNodes:
+ pn.appendChild(child.cloneNode(1))
+
+ for collection in ['imports','elements','types',
+ 'attr_decl','attr_groups','model_groups',
+ 'notations']:
+ for k,v in getattr(schema,collection).items():
+ if not getattr(self,collection).has_key(k):
+ v._parent = weakref.ref(self)
+ getattr(self,collection)[k] = v
+ else:
+ warnings.warn("Not keeping schema component.")
+
+ elif component == 'import':
+ slocd = SchemaReader.namespaceToSchema
+ tp = self.__class__.Import(self)
+ tp.fromDom(childNode)
+ import_ns = tp.getAttribute('namespace') or\
+ self.__class__.empty_namespace
+ schema = slocd.get(import_ns)
+ if schema is None:
+ schema = XMLSchema()
+ slocd[import_ns] = schema
+ try:
+ tp.loadSchema(schema)
+ except SchemaError:
+ # Dependency declaration, hopefully implementation
+ # is aware of this namespace (eg. SOAP,WSDL,?)
+ #warnings.warn(\
+ # '<import namespace="%s" schemaLocation=?>, %s'\
+ # %(import_ns, 'failed to load schema instance')
+ #)
+ del slocd[import_ns]
+ class LazyEval(str):
+ '''Lazy evaluation of import, replace entry in self.imports.'''
+ def getSchema(namespace):
+ schema = slocd.get(namespace)
+ if schema is None:
+ parent = self._parent()
+ wstypes = parent
+ if isinstance(parent, WSDLToolsAdapter):
+ wstypes = parent.getImportSchemas()
+ schema = wstypes.get(namespace)
+ if isinstance(schema, XMLSchema):
+ self.imports[namespace] = schema
+ return schema
+
+ return None
+
+ self.imports[import_ns] = LazyEval(import_ns)
+ continue
+ else:
+ tp._schema = schema
+
+ if self.getImportSchemas().has_key(import_ns):
+ warnings.warn(\
+ 'Detected multiple imports of the namespace "%s" '\
+ %import_ns)
+
+ self.addImportSchema(schema)
+ # spec says can have multiple imports of same namespace
+ # but purpose of import is just dependency declaration.
+ self.imports[import_ns] = tp
+
+ elif component == 'redefine':
+ warnings.warn('redefine is ignored')
+ elif component == 'annotation':
+ warnings.warn('annotation is ignored')
+ elif component == 'attribute':
+ tp = AttributeDeclaration(self)
+ tp.fromDom(childNode)
+ self.attr_decl[tp.getAttribute('name')] = tp
+ elif component == 'attributeGroup':
+ tp = AttributeGroupDefinition(self)
+ tp.fromDom(childNode)
+ self.attr_groups[tp.getAttribute('name')] = tp
+ elif component == 'element':
+ tp = ElementDeclaration(self)
+ tp.fromDom(childNode)
+ self.elements[tp.getAttribute('name')] = tp
+ elif component == 'group':
+ tp = ModelGroupDefinition(self)
+ tp.fromDom(childNode)
+ self.model_groups[tp.getAttribute('name')] = tp
+ elif component == 'notation':
+ tp = Notation(self)
+ tp.fromDom(childNode)
+ self.notations[tp.getAttribute('name')] = tp
+ elif component == 'complexType':
+ tp = ComplexType(self)
+ tp.fromDom(childNode)
+ self.types[tp.getAttribute('name')] = tp
+ elif component == 'simpleType':
+ tp = SimpleType(self)
+ tp.fromDom(childNode)
+ self.types[tp.getAttribute('name')] = tp
+ else:
+ break
+
+ class Import(XMLSchemaComponent):
+ """<import>
+ parent:
+ schema
+ attributes:
+ id -- ID
+ namespace -- anyURI
+ schemaLocation -- anyURI
+ contents:
+ annotation?
+ """
+ attributes = {'id':None,
+ 'namespace':None,
+ 'schemaLocation':None}
+ contents = {'xsd':['annotation']}
+ tag = 'import'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self._schema = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ if self.attributes['namespace'] == self.getTargetNamespace():
+ raise SchemaError, 'namespace of schema and import match'
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+ def getSchema(self):
+ """if schema is not defined, first look for a Schema class instance
+ in parent Schema. Else if not defined resolve schemaLocation
+ and create a new Schema class instance, and keep a hard reference.
+ """
+ if not self._schema:
+ ns = self.attributes['namespace']
+ schema = self._parent().getImportSchemas().get(ns)
+ if not schema and self._parent()._parent:
+ schema = self._parent()._parent().getImportSchemas().get(ns)
+
+ if not schema:
+ url = self.attributes.get('schemaLocation')
+ if not url:
+ raise SchemaError, 'namespace(%s) is unknown' %ns
+ base_url = self._parent().getBaseUrl()
+ reader = SchemaReader(base_url=base_url)
+ reader._imports = self._parent().getImportSchemas()
+ reader._includes = self._parent().getIncludeSchemas()
+ self._schema = reader.loadFromURL(url)
+ return self._schema or schema
+
+ def loadSchema(self, schema):
+ """
+ """
+ base_url = self._parent().getBaseUrl()
+ reader = SchemaReader(base_url=base_url)
+ reader._imports = self._parent().getImportSchemas()
+ reader._includes = self._parent().getIncludeSchemas()
+ self._schema = schema
+
+ if not self.attributes.has_key('schemaLocation'):
+ raise SchemaError, 'no schemaLocation'
+ reader.loadFromURL(self.attributes.get('schemaLocation'), schema)
+
+
+ class Include(XMLSchemaComponent):
+ """<include schemaLocation>
+ parent:
+ schema
+ attributes:
+ id -- ID
+ schemaLocation -- anyURI, required
+ contents:
+ annotation?
+ """
+ required = ['schemaLocation']
+ attributes = {'id':None,
+ 'schemaLocation':None}
+ contents = {'xsd':['annotation']}
+ tag = 'include'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self._schema = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+ def getSchema(self):
+ """if schema is not defined, first look for a Schema class instance
+ in parent Schema. Else if not defined resolve schemaLocation
+ and create a new Schema class instance.
+ """
+ if not self._schema:
+ schema = self._parent()
+ self._schema = schema.getIncludeSchemas().get(\
+ self.attributes['schemaLocation']
+ )
+ if not self._schema:
+ url = self.attributes['schemaLocation']
+ reader = SchemaReader(base_url=schema.getBaseUrl())
+ reader._imports = schema.getImportSchemas()
+ reader._includes = schema.getIncludeSchemas()
+
+ # create schema before loading so chameleon include
+ # will evalute targetNamespace correctly.
+ self._schema = XMLSchema(schema)
+ reader.loadFromURL(url, self._schema)
+
+ return self._schema
+
+
+class AttributeDeclaration(XMLSchemaComponent,\
+ AttributeMarker,\
+ DeclarationMarker):
+ """<attribute name>
+ parent:
+ schema
+ attributes:
+ id -- ID
+ name -- NCName, required
+ type -- QName
+ default -- string
+ fixed -- string
+ contents:
+ annotation?, simpleType?
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'type':None,
+ 'default':None,
+ 'fixed':None}
+ contents = {'xsd':['annotation','simpleType']}
+ tag = 'attribute'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ """ No list or union support
+ """
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ elif component == 'simpleType':
+ self.content = AnonymousSimpleType(self)
+ self.content.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class LocalAttributeDeclaration(AttributeDeclaration,\
+ AttributeMarker,\
+ LocalMarker,\
+ DeclarationMarker):
+ """<attribute name>
+ parent:
+ complexType, restriction, extension, attributeGroup
+ attributes:
+ id -- ID
+ name -- NCName, required
+ type -- QName
+ form -- ('qualified' | 'unqualified'), schema.attributeFormDefault
+ use -- ('optional' | 'prohibited' | 'required'), optional
+ default -- string
+ fixed -- string
+ contents:
+ annotation?, simpleType?
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'type':None,
+ 'form':lambda self: GetSchema(self).getAttributeFormDefault(),
+ 'use':'optional',
+ 'default':None,
+ 'fixed':None}
+ contents = {'xsd':['annotation','simpleType']}
+
+ def __init__(self, parent):
+ AttributeDeclaration.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ elif component == 'simpleType':
+ self.content = AnonymousSimpleType(self)
+ self.content.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class AttributeWildCard(XMLSchemaComponent,\
+ AttributeMarker,\
+ DeclarationMarker,\
+ WildCardMarker):
+ """<anyAttribute>
+ parents:
+ complexType, restriction, extension, attributeGroup
+ attributes:
+ id -- ID
+ namespace -- '##any' | '##other' |
+ (anyURI* | '##targetNamespace' | '##local'), ##any
+ processContents -- 'lax' | 'skip' | 'strict', strict
+ contents:
+ annotation?
+ """
+ attributes = {'id':None,
+ 'namespace':'##any',
+ 'processContents':'strict'}
+ contents = {'xsd':['annotation']}
+ tag = 'anyAttribute'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class AttributeReference(XMLSchemaComponent,\
+ AttributeMarker,\
+ ReferenceMarker):
+ """<attribute ref>
+ parents:
+ complexType, restriction, extension, attributeGroup
+ attributes:
+ id -- ID
+ ref -- QName, required
+ use -- ('optional' | 'prohibited' | 'required'), optional
+ default -- string
+ fixed -- string
+ contents:
+ annotation?
+ """
+ required = ['ref']
+ attributes = {'id':None,
+ 'ref':None,
+ 'use':'optional',
+ 'default':None,
+ 'fixed':None}
+ contents = {'xsd':['annotation']}
+ tag = 'attribute'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def getAttributeDeclaration(self, attribute='ref'):
+ return XMLSchemaComponent.getAttributeDeclaration(self, attribute)
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class AttributeGroupDefinition(XMLSchemaComponent,\
+ AttributeGroupMarker,\
+ DefinitionMarker):
+ """<attributeGroup name>
+ parents:
+ schema, redefine
+ attributes:
+ id -- ID
+ name -- NCName, required
+ contents:
+ annotation?, (attribute | attributeGroup)*, anyAttribute?
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None}
+ contents = {'xsd':['annotation', 'attribute', 'attributeGroup', 'anyAttribute']}
+ tag = 'attributeGroup'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.attr_content = None
+
+ def getAttributeContent(self):
+ return self.attr_content
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for indx in range(len(contents)):
+ component = SplitQName(contents[indx].getTagName())[1]
+ if (component == 'annotation') and (not indx):
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ elif component == 'attribute':
+ if contents[indx].hasattr('name'):
+ content.append(LocalAttributeDeclaration(self))
+ elif contents[indx].hasattr('ref'):
+ content.append(AttributeReference(self))
+ else:
+ raise SchemaError, 'Unknown attribute type'
+ content[-1].fromDom(contents[indx])
+ elif component == 'attributeGroup':
+ content.append(AttributeGroupReference(self))
+ content[-1].fromDom(contents[indx])
+ elif component == 'anyAttribute':
+ if len(contents) != indx+1:
+ raise SchemaError, 'anyAttribute is out of order in %s' %self.getItemTrace()
+ content.append(AttributeWildCard(self))
+ content[-1].fromDom(contents[indx])
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(contents[indx].getTagName())
+
+ self.attr_content = tuple(content)
+
+class AttributeGroupReference(XMLSchemaComponent,\
+ AttributeGroupMarker,\
+ ReferenceMarker):
+ """<attributeGroup ref>
+ parents:
+ complexType, restriction, extension, attributeGroup
+ attributes:
+ id -- ID
+ ref -- QName, required
+ contents:
+ annotation?
+ """
+ required = ['ref']
+ attributes = {'id':None,
+ 'ref':None}
+ contents = {'xsd':['annotation']}
+ tag = 'attributeGroup'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def getAttributeGroup(self, attribute='ref'):
+ """attribute -- attribute with a QName value (eg. type).
+ collection -- check types collection in parent Schema instance
+ """
+ return XMLSchemaComponent.getAttributeGroup(self, attribute)
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+
+######################################################
+# Elements
+#####################################################
+class IdentityConstrants(XMLSchemaComponent):
+ """Allow one to uniquely identify nodes in a document and ensure the
+ integrity of references between them.
+
+ attributes -- dictionary of attributes
+ selector -- XPath to selected nodes
+ fields -- list of XPath to key field
+ """
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.selector = None
+ self.fields = None
+ self.annotation = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ fields = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ elif component == 'selector':
+ self.selector = self.Selector(self)
+ self.selector.fromDom(i)
+ continue
+ elif component == 'field':
+ fields.append(self.Field(self))
+ fields[-1].fromDom(i)
+ continue
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.fields = tuple(fields)
+
+
+ class Constraint(XMLSchemaComponent):
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+ class Selector(Constraint):
+ """<selector xpath>
+ parent:
+ unique, key, keyref
+ attributes:
+ id -- ID
+ xpath -- XPath subset, required
+ contents:
+ annotation?
+ """
+ required = ['xpath']
+ attributes = {'id':None,
+ 'xpath':None}
+ contents = {'xsd':['annotation']}
+ tag = 'selector'
+
+ class Field(Constraint):
+ """<field xpath>
+ parent:
+ unique, key, keyref
+ attributes:
+ id -- ID
+ xpath -- XPath subset, required
+ contents:
+ annotation?
+ """
+ required = ['xpath']
+ attributes = {'id':None,
+ 'xpath':None}
+ contents = {'xsd':['annotation']}
+ tag = 'field'
+
+
+class Unique(IdentityConstrants):
+ """<unique name> Enforce fields are unique w/i a specified scope.
+
+ parent:
+ element
+ attributes:
+ id -- ID
+ name -- NCName, required
+ contents:
+ annotation?, selector, field+
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None}
+ contents = {'xsd':['annotation', 'selector', 'field']}
+ tag = 'unique'
+
+
+class Key(IdentityConstrants):
+ """<key name> Enforce fields are unique w/i a specified scope, and all
+ field values are present w/i document. Fields cannot
+ be nillable.
+
+ parent:
+ element
+ attributes:
+ id -- ID
+ name -- NCName, required
+ contents:
+ annotation?, selector, field+
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None}
+ contents = {'xsd':['annotation', 'selector', 'field']}
+ tag = 'key'
+
+
+class KeyRef(IdentityConstrants):
+ """<keyref name refer> Ensure a match between two sets of values in an
+ instance.
+ parent:
+ element
+ attributes:
+ id -- ID
+ name -- NCName, required
+ refer -- QName, required
+ contents:
+ annotation?, selector, field+
+ """
+ required = ['name', 'refer']
+ attributes = {'id':None,
+ 'name':None,
+ 'refer':None}
+ contents = {'xsd':['annotation', 'selector', 'field']}
+ tag = 'keyref'
+
+
+class ElementDeclaration(XMLSchemaComponent,\
+ ElementMarker,\
+ DeclarationMarker):
+ """<element name>
+ parents:
+ schema
+ attributes:
+ id -- ID
+ name -- NCName, required
+ type -- QName
+ default -- string
+ fixed -- string
+ nillable -- boolean, false
+ abstract -- boolean, false
+ substitutionGroup -- QName
+ block -- ('#all' | ('substition' | 'extension' | 'restriction')*),
+ schema.blockDefault
+ final -- ('#all' | ('extension' | 'restriction')*),
+ schema.finalDefault
+ contents:
+ annotation?, (simpleType,complexType)?, (key | keyref | unique)*
+
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'type':None,
+ 'default':None,
+ 'fixed':None,
+ 'nillable':0,
+ 'abstract':0,
+ 'substitutionGroup':None,
+ 'block':lambda self: self._parent().getBlockDefault(),
+ 'final':lambda self: self._parent().getFinalDefault()}
+ contents = {'xsd':['annotation', 'simpleType', 'complexType', 'key',\
+ 'keyref', 'unique']}
+ tag = 'element'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+ self.constraints = ()
+
+ def isQualified(self):
+ """Global elements are always qualified.
+ """
+ return True
+
+ def getAttribute(self, attribute):
+ """return attribute.
+ If attribute is type and it's None, and no simple or complex content,
+ return the default type "xsd:anyType"
+ """
+ value = XMLSchemaComponent.getAttribute(self, attribute)
+ if attribute != 'type' or value is not None:
+ return value
+
+ if self.content is not None:
+ return None
+
+ parent = self
+ while 1:
+ nsdict = parent.attributes[XMLSchemaComponent.xmlns]
+ for k,v in nsdict.items():
+ if v not in SCHEMA.XSD_LIST: continue
+ return TypeDescriptionComponent((v, 'anyType'))
+
+ if isinstance(parent, WSDLToolsAdapter)\
+ or not hasattr(parent, '_parent'):
+ break
+
+ parent = parent._parent()
+
+ raise SchemaError, 'failed to locate the XSD namespace'
+
+ def getElementDeclaration(self, attribute):
+ raise Warning, 'invalid operation for <%s>' %self.tag
+
+ def getTypeDefinition(self, attribute=None):
+ """If attribute is None, "type" is assumed, return the corresponding
+ representation of the global type definition (TypeDefinition),
+ or the local definition if don't find "type". To maintain backwards
+ compat, if attribute is provided call base class method.
+ """
+ if attribute:
+ return XMLSchemaComponent.getTypeDefinition(self, attribute)
+ gt = XMLSchemaComponent.getTypeDefinition(self, 'type')
+ if gt:
+ return gt
+ return self.content
+
+ def getConstraints(self):
+ return self._constraints
+ def setConstraints(self, constraints):
+ self._constraints = tuple(constraints)
+ constraints = property(getConstraints, setConstraints, None, "tuple of key, keyref, unique constraints")
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ constraints = []
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ elif component == 'simpleType' and not self.content:
+ self.content = AnonymousSimpleType(self)
+ self.content.fromDom(i)
+ elif component == 'complexType' and not self.content:
+ self.content = LocalComplexType(self)
+ self.content.fromDom(i)
+ elif component == 'key':
+ constraints.append(Key(self))
+ constraints[-1].fromDom(i)
+ elif component == 'keyref':
+ constraints.append(KeyRef(self))
+ constraints[-1].fromDom(i)
+ elif component == 'unique':
+ constraints.append(Unique(self))
+ constraints[-1].fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+ self.constraints = constraints
+
+
+class LocalElementDeclaration(ElementDeclaration,\
+ LocalMarker):
+ """<element>
+ parents:
+ all, choice, sequence
+ attributes:
+ id -- ID
+ name -- NCName, required
+ form -- ('qualified' | 'unqualified'), schema.elementFormDefault
+ type -- QName
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+ default -- string
+ fixed -- string
+ nillable -- boolean, false
+ block -- ('#all' | ('extension' | 'restriction')*), schema.blockDefault
+ contents:
+ annotation?, (simpleType,complexType)?, (key | keyref | unique)*
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'form':lambda self: GetSchema(self).getElementFormDefault(),
+ 'type':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1',
+ 'default':None,
+ 'fixed':None,
+ 'nillable':0,
+ 'abstract':0,
+ 'block':lambda self: GetSchema(self).getBlockDefault()}
+ contents = {'xsd':['annotation', 'simpleType', 'complexType', 'key',\
+ 'keyref', 'unique']}
+
+ def isQualified(self):
+ """
+Local elements can be qualified or unqualifed according
+ to the attribute form, or the elementFormDefault. By default
+ local elements are unqualified.
+ """
+ form = self.getAttribute('form')
+ if form == 'qualified':
+ return True
+ if form == 'unqualified':
+ return False
+ raise SchemaError, 'Bad form (%s) for element: %s' %(form, self.getItemTrace())
+
+
+class ElementReference(XMLSchemaComponent,\
+ ElementMarker,\
+ ReferenceMarker):
+ """<element ref>
+ parents:
+ all, choice, sequence
+ attributes:
+ id -- ID
+ ref -- QName, required
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+ contents:
+ annotation?
+ """
+ required = ['ref']
+ attributes = {'id':None,
+ 'ref':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1'}
+ contents = {'xsd':['annotation']}
+ tag = 'element'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def getElementDeclaration(self, attribute=None):
+ """If attribute is None, "ref" is assumed, return the corresponding
+ representation of the global element declaration (ElementDeclaration),
+ To maintain backwards compat, if attribute is provided call base class method.
+ """
+ if attribute:
+ return XMLSchemaComponent.getElementDeclaration(self, attribute)
+ return XMLSchemaComponent.getElementDeclaration(self, 'ref')
+
+ def fromDom(self, node):
+ self.annotation = None
+ self.setAttributes(node)
+ for i in self.getContents(node):
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class ElementWildCard(LocalElementDeclaration, WildCardMarker):
+ """<any>
+ parents:
+ choice, sequence
+ attributes:
+ id -- ID
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+ namespace -- '##any' | '##other' |
+ (anyURI* | '##targetNamespace' | '##local'), ##any
+ processContents -- 'lax' | 'skip' | 'strict', strict
+ contents:
+ annotation?
+ """
+ required = []
+ attributes = {'id':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1',
+ 'namespace':'##any',
+ 'processContents':'strict'}
+ contents = {'xsd':['annotation']}
+ tag = 'any'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def isQualified(self):
+ """
+ Global elements are always qualified, but if processContents
+ are not strict could have dynamically generated local elements.
+ """
+ return GetSchema(self).isElementFormDefaultQualified()
+
+ def getAttribute(self, attribute):
+ """return attribute.
+ """
+ return XMLSchemaComponent.getAttribute(self, attribute)
+
+ def getTypeDefinition(self, attribute):
+ raise Warning, 'invalid operation for <%s>' % self.tag
+
+ def fromDom(self, node):
+ self.annotation = None
+ self.setAttributes(node)
+ for i in self.getContents(node):
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+######################################################
+# Model Groups
+#####################################################
+class Sequence(XMLSchemaComponent,\
+ SequenceMarker):
+ """<sequence>
+ parents:
+ complexType, extension, restriction, group, choice, sequence
+ attributes:
+ id -- ID
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+
+ contents:
+ annotation?, (element | group | choice | sequence | any)*
+ """
+ attributes = {'id':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1'}
+ contents = {'xsd':['annotation', 'element', 'group', 'choice', 'sequence',\
+ 'any']}
+ tag = 'sequence'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ continue
+ elif component == 'element':
+ if i.hasattr('ref'):
+ content.append(ElementReference(self))
+ else:
+ content.append(LocalElementDeclaration(self))
+ elif component == 'group':
+ content.append(ModelGroupReference(self))
+ elif component == 'choice':
+ content.append(Choice(self))
+ elif component == 'sequence':
+ content.append(Sequence(self))
+ elif component == 'any':
+ content.append(ElementWildCard(self))
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ content[-1].fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+class All(XMLSchemaComponent,\
+ AllMarker):
+ """<all>
+ parents:
+ complexType, extension, restriction, group
+ attributes:
+ id -- ID
+ minOccurs -- '0' | '1', 1
+ maxOccurs -- '1', 1
+
+ contents:
+ annotation?, element*
+ """
+ attributes = {'id':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1'}
+ contents = {'xsd':['annotation', 'element']}
+ tag = 'all'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ continue
+ elif component == 'element':
+ if i.hasattr('ref'):
+ content.append(ElementReference(self))
+ else:
+ content.append(LocalElementDeclaration(self))
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ content[-1].fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+class Choice(XMLSchemaComponent,\
+ ChoiceMarker):
+ """<choice>
+ parents:
+ complexType, extension, restriction, group, choice, sequence
+ attributes:
+ id -- ID
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+
+ contents:
+ annotation?, (element | group | choice | sequence | any)*
+ """
+ attributes = {'id':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1'}
+ contents = {'xsd':['annotation', 'element', 'group', 'choice', 'sequence',\
+ 'any']}
+ tag = 'choice'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ continue
+ elif component == 'element':
+ if i.hasattr('ref'):
+ content.append(ElementReference(self))
+ else:
+ content.append(LocalElementDeclaration(self))
+ elif component == 'group':
+ content.append(ModelGroupReference(self))
+ elif component == 'choice':
+ content.append(Choice(self))
+ elif component == 'sequence':
+ content.append(Sequence(self))
+ elif component == 'any':
+ content.append(ElementWildCard(self))
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ content[-1].fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+class ModelGroupDefinition(XMLSchemaComponent,\
+ ModelGroupMarker,\
+ DefinitionMarker):
+ """<group name>
+ parents:
+ redefine, schema
+ attributes:
+ id -- ID
+ name -- NCName, required
+
+ contents:
+ annotation?, (all | choice | sequence)?
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None}
+ contents = {'xsd':['annotation', 'all', 'choice', 'sequence']}
+ tag = 'group'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ continue
+ elif component == 'all' and not self.content:
+ self.content = All(self)
+ elif component == 'choice' and not self.content:
+ self.content = Choice(self)
+ elif component == 'sequence' and not self.content:
+ self.content = Sequence(self)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class ModelGroupReference(XMLSchemaComponent,\
+ ModelGroupMarker,\
+ ReferenceMarker):
+ """<group ref>
+ parents:
+ choice, complexType, extension, restriction, sequence
+ attributes:
+ id -- ID
+ ref -- NCName, required
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+
+ contents:
+ annotation?
+ """
+ required = ['ref']
+ attributes = {'id':None,
+ 'ref':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1'}
+ contents = {'xsd':['annotation']}
+ tag = 'group'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def getModelGroupReference(self):
+ return self.getModelGroup('ref')
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+
+class ComplexType(XMLSchemaComponent,\
+ DefinitionMarker,\
+ ComplexMarker):
+ """<complexType name>
+ parents:
+ redefine, schema
+ attributes:
+ id -- ID
+ name -- NCName, required
+ mixed -- boolean, false
+ abstract -- boolean, false
+ block -- ('#all' | ('extension' | 'restriction')*), schema.blockDefault
+ final -- ('#all' | ('extension' | 'restriction')*), schema.finalDefault
+
+ contents:
+ annotation?, (simpleContent | complexContent |
+ ((group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute?))
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'mixed':0,
+ 'abstract':0,
+ 'block':lambda self: self._parent().getBlockDefault(),
+ 'final':lambda self: self._parent().getFinalDefault()}
+ contents = {'xsd':['annotation', 'simpleContent', 'complexContent',\
+ 'group', 'all', 'choice', 'sequence', 'attribute', 'attributeGroup',\
+ 'anyAttribute', 'any']}
+ tag = 'complexType'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+ self.attr_content = None
+
+ def isMixed(self):
+ m = self.getAttribute('mixed')
+ if m == 0 or m == False:
+ return False
+ if isinstance(m, basestring) is True:
+ if m in ('false', '0'):
+ return False
+ if m in ('true', '1'):
+ return True
+
+ raise SchemaError, 'invalid value for attribute mixed(%s): %s'\
+ %(m, self.getItemTrace())
+
+ def getAttributeContent(self):
+ return self.attr_content
+
+ def getElementDeclaration(self, attribute):
+ raise Warning, 'invalid operation for <%s>' %self.tag
+
+ def getTypeDefinition(self, attribute):
+ raise Warning, 'invalid operation for <%s>' %self.tag
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ indx = 0
+ num = len(contents)
+ if not num:
+ return
+
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'annotation':
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ indx += 1
+ component = SplitQName(contents[indx].getTagName())[1]
+
+ self.content = None
+ if component == 'simpleContent':
+ self.content = self.__class__.SimpleContent(self)
+ self.content.fromDom(contents[indx])
+ elif component == 'complexContent':
+ self.content = self.__class__.ComplexContent(self)
+ self.content.fromDom(contents[indx])
+ else:
+ if component == 'all':
+ self.content = All(self)
+ elif component == 'choice':
+ self.content = Choice(self)
+ elif component == 'sequence':
+ self.content = Sequence(self)
+ elif component == 'group':
+ self.content = ModelGroupReference(self)
+
+ if self.content:
+ self.content.fromDom(contents[indx])
+ indx += 1
+
+ self.attr_content = []
+ while indx < num:
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'attribute':
+ if contents[indx].hasattr('ref'):
+ self.attr_content.append(AttributeReference(self))
+ else:
+ self.attr_content.append(LocalAttributeDeclaration(self))
+ elif component == 'attributeGroup':
+ self.attr_content.append(AttributeGroupReference(self))
+ elif component == 'anyAttribute':
+ self.attr_content.append(AttributeWildCard(self))
+ else:
+ raise SchemaError, 'Unknown component (%s): %s' \
+ %(contents[indx].getTagName(),self.getItemTrace())
+ self.attr_content[-1].fromDom(contents[indx])
+ indx += 1
+
+ class _DerivedType(XMLSchemaComponent):
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ # XXX remove attribute derivation, inconsistent
+ self.derivation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ continue
+ elif component == 'restriction' and not self.derivation:
+ self.derivation = self.__class__.Restriction(self)
+ elif component == 'extension' and not self.derivation:
+ self.derivation = self.__class__.Extension(self)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.derivation.fromDom(i)
+ self.content = self.derivation
+
+ class ComplexContent(_DerivedType,\
+ ComplexMarker):
+ """<complexContent>
+ parents:
+ complexType
+ attributes:
+ id -- ID
+ mixed -- boolean, false
+
+ contents:
+ annotation?, (restriction | extension)
+ """
+ attributes = {'id':None,
+ 'mixed':0}
+ contents = {'xsd':['annotation', 'restriction', 'extension']}
+ tag = 'complexContent'
+
+ def isMixed(self):
+ m = self.getAttribute('mixed')
+ if m == 0 or m == False:
+ return False
+ if isinstance(m, basestring) is True:
+ if m in ('false', '0'):
+ return False
+ if m in ('true', '1'):
+ return True
+ raise SchemaError, 'invalid value for attribute mixed(%s): %s'\
+ %(m, self.getItemTrace())
+
+ class _DerivationBase(XMLSchemaComponent):
+ """<extension>,<restriction>
+ parents:
+ complexContent
+ attributes:
+ id -- ID
+ base -- QName, required
+
+ contents:
+ annotation?, (group | all | choice | sequence)?,
+ (attribute | attributeGroup)*, anyAttribute?
+ """
+ required = ['base']
+ attributes = {'id':None,
+ 'base':None }
+ contents = {'xsd':['annotation', 'group', 'all', 'choice',\
+ 'sequence', 'attribute', 'attributeGroup', 'anyAttribute']}
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+ self.attr_content = None
+
+ def getAttributeContent(self):
+ return self.attr_content
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ indx = 0
+ num = len(contents)
+ #XXX ugly
+ if not num:
+ return
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'annotation':
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ indx += 1
+ component = SplitQName(contents[indx].getTagName())[1]
+
+ if component == 'all':
+ self.content = All(self)
+ self.content.fromDom(contents[indx])
+ indx += 1
+ elif component == 'choice':
+ self.content = Choice(self)
+ self.content.fromDom(contents[indx])
+ indx += 1
+ elif component == 'sequence':
+ self.content = Sequence(self)
+ self.content.fromDom(contents[indx])
+ indx += 1
+ elif component == 'group':
+ self.content = ModelGroupReference(self)
+ self.content.fromDom(contents[indx])
+ indx += 1
+ else:
+ self.content = None
+
+ self.attr_content = []
+ while indx < num:
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'attribute':
+ if contents[indx].hasattr('ref'):
+ self.attr_content.append(AttributeReference(self))
+ else:
+ self.attr_content.append(LocalAttributeDeclaration(self))
+ elif component == 'attributeGroup':
+ if contents[indx].hasattr('ref'):
+ self.attr_content.append(AttributeGroupReference(self))
+ else:
+ self.attr_content.append(AttributeGroupDefinition(self))
+ elif component == 'anyAttribute':
+ self.attr_content.append(AttributeWildCard(self))
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(contents[indx].getTagName())
+ self.attr_content[-1].fromDom(contents[indx])
+ indx += 1
+
+ class Extension(_DerivationBase,
+ ExtensionMarker):
+ """<extension base>
+ parents:
+ complexContent
+ attributes:
+ id -- ID
+ base -- QName, required
+
+ contents:
+ annotation?, (group | all | choice | sequence)?,
+ (attribute | attributeGroup)*, anyAttribute?
+ """
+ tag = 'extension'
+
+ class Restriction(_DerivationBase,\
+ RestrictionMarker):
+ """<restriction base>
+ parents:
+ complexContent
+ attributes:
+ id -- ID
+ base -- QName, required
+
+ contents:
+ annotation?, (group | all | choice | sequence)?,
+ (attribute | attributeGroup)*, anyAttribute?
+ """
+ tag = 'restriction'
+
+
+ class SimpleContent(_DerivedType,\
+ SimpleMarker):
+ """<simpleContent>
+ parents:
+ complexType
+ attributes:
+ id -- ID
+
+ contents:
+ annotation?, (restriction | extension)
+ """
+ attributes = {'id':None}
+ contents = {'xsd':['annotation', 'restriction', 'extension']}
+ tag = 'simpleContent'
+
+ class Extension(XMLSchemaComponent,\
+ ExtensionMarker):
+ """<extension base>
+ parents:
+ simpleContent
+ attributes:
+ id -- ID
+ base -- QName, required
+
+ contents:
+ annotation?, (attribute | attributeGroup)*, anyAttribute?
+ """
+ required = ['base']
+ attributes = {'id':None,
+ 'base':None }
+ contents = {'xsd':['annotation', 'attribute', 'attributeGroup',
+ 'anyAttribute']}
+ tag = 'extension'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.attr_content = None
+
+ def getAttributeContent(self):
+ return self.attr_content
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ indx = 0
+ num = len(contents)
+
+ if num:
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'annotation':
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ indx += 1
+ component = SplitQName(contents[indx].getTagName())[1]
+
+ content = []
+ while indx < num:
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'attribute':
+ if contents[indx].hasattr('ref'):
+ content.append(AttributeReference(self))
+ else:
+ content.append(LocalAttributeDeclaration(self))
+ elif component == 'attributeGroup':
+ content.append(AttributeGroupReference(self))
+ elif component == 'anyAttribute':
+ content.append(AttributeWildCard(self))
+ else:
+ raise SchemaError, 'Unknown component (%s)'\
+ %(contents[indx].getTagName())
+ content[-1].fromDom(contents[indx])
+ indx += 1
+ self.attr_content = tuple(content)
+
+
+ class Restriction(XMLSchemaComponent,\
+ RestrictionMarker):
+ """<restriction base>
+ parents:
+ simpleContent
+ attributes:
+ id -- ID
+ base -- QName, required
+
+ contents:
+ annotation?, simpleType?, (enumeration | length |
+ maxExclusive | maxInclusive | maxLength | minExclusive |
+ minInclusive | minLength | pattern | fractionDigits |
+ totalDigits | whiteSpace)*, (attribute | attributeGroup)*,
+ anyAttribute?
+ """
+ required = ['base']
+ attributes = {'id':None,
+ 'base':None }
+ contents = {'xsd':['annotation', 'simpleType', 'attribute',\
+ 'attributeGroup', 'anyAttribute'] + RestrictionMarker.facets}
+ tag = 'restriction'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+ self.attr_content = None
+
+ def getAttributeContent(self):
+ return self.attr_content
+
+ def fromDom(self, node):
+ self.content = []
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ indx = 0
+ num = len(contents)
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'annotation':
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ indx += 1
+ component = SplitQName(contents[indx].getTagName())[1]
+
+ content = []
+ while indx < num:
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'attribute':
+ if contents[indx].hasattr('ref'):
+ content.append(AttributeReference(self))
+ else:
+ content.append(LocalAttributeDeclaration(self))
+ elif component == 'attributeGroup':
+ content.append(AttributeGroupReference(self))
+ elif component == 'anyAttribute':
+ content.append(AttributeWildCard(self))
+ elif component == 'simpleType':
+ self.content.append(AnonymousSimpleType(self))
+ self.content[-1].fromDom(contents[indx])
+ else:
+ raise SchemaError, 'Unknown component (%s)'\
+ %(contents[indx].getTagName())
+ content[-1].fromDom(contents[indx])
+ indx += 1
+ self.attr_content = tuple(content)
+
+
+class LocalComplexType(ComplexType,\
+ LocalMarker):
+ """<complexType>
+ parents:
+ element
+ attributes:
+ id -- ID
+ mixed -- boolean, false
+
+ contents:
+ annotation?, (simpleContent | complexContent |
+ ((group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute?))
+ """
+ required = []
+ attributes = {'id':None,
+ 'mixed':0}
+ tag = 'complexType'
+
+
+class SimpleType(XMLSchemaComponent,\
+ DefinitionMarker,\
+ SimpleMarker):
+ """<simpleType name>
+ parents:
+ redefine, schema
+ attributes:
+ id -- ID
+ name -- NCName, required
+ final -- ('#all' | ('extension' | 'restriction' | 'list' | 'union')*),
+ schema.finalDefault
+
+ contents:
+ annotation?, (restriction | list | union)
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'final':lambda self: self._parent().getFinalDefault()}
+ contents = {'xsd':['annotation', 'restriction', 'list', 'union']}
+ tag = 'simpleType'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def getElementDeclaration(self, attribute):
+ raise Warning, 'invalid operation for <%s>' %self.tag
+
+ def getTypeDefinition(self, attribute):
+ raise Warning, 'invalid operation for <%s>' %self.tag
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ for child in contents:
+ component = SplitQName(child.getTagName())[1]
+ if component == 'annotation':
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(child)
+ continue
+ break
+ else:
+ return
+ if component == 'restriction':
+ self.content = self.__class__.Restriction(self)
+ elif component == 'list':
+ self.content = self.__class__.List(self)
+ elif component == 'union':
+ self.content = self.__class__.Union(self)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(component)
+ self.content.fromDom(child)
+
+ class Restriction(XMLSchemaComponent,\
+ RestrictionMarker):
+ """<restriction base>
+ parents:
+ simpleType
+ attributes:
+ id -- ID
+ base -- QName, required or simpleType child
+
+ contents:
+ annotation?, simpleType?, (enumeration | length |
+ maxExclusive | maxInclusive | maxLength | minExclusive |
+ minInclusive | minLength | pattern | fractionDigits |
+ totalDigits | whiteSpace)*
+ """
+ attributes = {'id':None,
+ 'base':None }
+ contents = {'xsd':['annotation', 'simpleType']+RestrictionMarker.facets}
+ tag = 'restriction'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+ self.facets = None
+
+ def getAttributeBase(self):
+ return XMLSchemaComponent.getAttribute(self, 'base')
+
+ def getTypeDefinition(self, attribute='base'):
+ return XMLSchemaComponent.getTypeDefinition(self, attribute)
+
+ def getSimpleTypeContent(self):
+ for el in self.content:
+ if el.isSimple(): return el
+ return None
+
+ def fromDom(self, node):
+ self.facets = []
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for indx in range(len(contents)):
+ component = SplitQName(contents[indx].getTagName())[1]
+ if (component == 'annotation') and (not indx):
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ continue
+ elif (component == 'simpleType') and (not indx or indx == 1):
+ content.append(AnonymousSimpleType(self))
+ content[-1].fromDom(contents[indx])
+ elif component in RestrictionMarker.facets:
+ self.facets.append(contents[indx])
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+ class Union(XMLSchemaComponent,
+ UnionMarker):
+ """<union>
+ parents:
+ simpleType
+ attributes:
+ id -- ID
+ memberTypes -- list of QNames, required or simpleType child.
+
+ contents:
+ annotation?, simpleType*
+ """
+ attributes = {'id':None,
+ 'memberTypes':None }
+ contents = {'xsd':['annotation', 'simpleType']}
+ tag = 'union'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for indx in range(len(contents)):
+ component = SplitQName(contents[indx].getTagName())[1]
+ if (component == 'annotation') and (not indx):
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ elif (component == 'simpleType'):
+ content.append(AnonymousSimpleType(self))
+ content[-1].fromDom(contents[indx])
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+ class List(XMLSchemaComponent,
+ ListMarker):
+ """<list>
+ parents:
+ simpleType
+ attributes:
+ id -- ID
+ itemType -- QName, required or simpleType child.
+
+ contents:
+ annotation?, simpleType?
+ """
+ attributes = {'id':None,
+ 'itemType':None }
+ contents = {'xsd':['annotation', 'simpleType']}
+ tag = 'list'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def getItemType(self):
+ return self.attributes.get('itemType')
+
+ def getTypeDefinition(self, attribute='itemType'):
+ """
+ return the type refered to by itemType attribute or
+ the simpleType content. If returns None, then the
+ type refered to by itemType is primitive.
+ """
+ tp = XMLSchemaComponent.getTypeDefinition(self, attribute)
+ return tp or self.content
+
+ def fromDom(self, node):
+ self.annotation = None
+ self.content = None
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ for indx in range(len(contents)):
+ component = SplitQName(contents[indx].getTagName())[1]
+ if (component == 'annotation') and (not indx):
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ elif (component == 'simpleType'):
+ self.content = AnonymousSimpleType(self)
+ self.content.fromDom(contents[indx])
+ break
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class AnonymousSimpleType(SimpleType,\
+ SimpleMarker,\
+ LocalMarker):
+ """<simpleType>
+ parents:
+ attribute, element, list, restriction, union
+ attributes:
+ id -- ID
+
+ contents:
+ annotation?, (restriction | list | union)
+ """
+ required = []
+ attributes = {'id':None}
+ tag = 'simpleType'
+
+
+class Redefine:
+ """<redefine>
+ parents:
+ attributes:
+
+ contents:
+ """
+ tag = 'redefine'
+
+
+###########################
+###########################
+
+
+if sys.version_info[:2] >= (2, 2):
+ tupleClass = tuple
+else:
+ import UserTuple
+ tupleClass = UserTuple.UserTuple
+
+class TypeDescriptionComponent(tupleClass):
+ """Tuple of length 2, consisting of
+ a namespace and unprefixed name.
+ """
+ def __init__(self, args):
+ """args -- (namespace, name)
+ Remove the name's prefix, irrelevant.
+ """
+ if len(args) != 2:
+ raise TypeError, 'expecting tuple (namespace, name), got %s' %args
+ elif args[1].find(':') >= 0:
+ args = (args[0], SplitQName(args[1])[1])
+ tuple.__init__(self, args)
+ return
+
+ def getTargetNamespace(self):
+ return self[0]
+
+ def getName(self):
+ return self[1]
+
+
diff --git a/ZSI/wstools/XMLname.py b/ZSI/wstools/XMLname.py
new file mode 100644
index 0000000..a26a02a
--- /dev/null
+++ b/ZSI/wstools/XMLname.py
@@ -0,0 +1,90 @@
+"""Translate strings to and from SOAP 1.2 XML name encoding
+
+Implements rules for mapping application defined name to XML names
+specified by the w3 SOAP working group for SOAP version 1.2 in
+Appendix A of "SOAP Version 1.2 Part 2: Adjuncts", W3C Working Draft
+17, December 2001, <http://www.w3.org/TR/soap12-part2/#namemap>
+
+Also see <http://www.w3.org/2000/xp/Group/xmlp-issues>.
+
+Author: Gregory R. Warnes <Gregory.R.Warnes@Pfizer.com>
+Date:: 2002-04-25
+Version 0.9.0
+
+"""
+
+ident = "$Id: XMLname.py 954 2005-02-16 14:45:37Z warnes $"
+
+from re import *
+
+
+def _NCNameChar(x):
+ return x.isalpha() or x.isdigit() or x=="." or x=='-' or x=="_"
+
+
+def _NCNameStartChar(x):
+ return x.isalpha() or x=="_"
+
+
+def _toUnicodeHex(x):
+ hexval = hex(ord(x[0]))[2:]
+ hexlen = len(hexval)
+ # Make hexval have either 4 or 8 digits by prepending 0's
+ if (hexlen==1): hexval = "000" + hexval
+ elif (hexlen==2): hexval = "00" + hexval
+ elif (hexlen==3): hexval = "0" + hexval
+ elif (hexlen==4): hexval = "" + hexval
+ elif (hexlen==5): hexval = "000" + hexval
+ elif (hexlen==6): hexval = "00" + hexval
+ elif (hexlen==7): hexval = "0" + hexval
+ elif (hexlen==8): hexval = "" + hexval
+ else: raise Exception, "Illegal Value returned from hex(ord(x))"
+
+ return "_x"+ hexval + "_"
+
+
+def _fromUnicodeHex(x):
+ return eval( r'u"\u'+x[2:-1]+'"' )
+
+
+def toXMLname(string):
+ """Convert string to a XML name."""
+ if string.find(':') != -1 :
+ (prefix, localname) = string.split(':',1)
+ else:
+ prefix = None
+ localname = string
+
+ T = unicode(localname)
+
+ N = len(localname)
+ X = [];
+ for i in range(N) :
+ if i< N-1 and T[i]==u'_' and T[i+1]==u'x':
+ X.append(u'_x005F_')
+ elif i==0 and N >= 3 and \
+ ( T[0]==u'x' or T[0]==u'X' ) and \
+ ( T[1]==u'm' or T[1]==u'M' ) and \
+ ( T[2]==u'l' or T[2]==u'L' ):
+ X.append(u'_xFFFF_' + T[0])
+ elif (not _NCNameChar(T[i])) or (i==0 and not _NCNameStartChar(T[i])):
+ X.append(_toUnicodeHex(T[i]))
+ else:
+ X.append(T[i])
+
+ if prefix:
+ return "%s:%s" % (prefix, u''.join(X))
+ return u''.join(X)
+
+
+def fromXMLname(string):
+ """Convert XML name to unicode string."""
+
+ retval = sub(r'_xFFFF_','', string )
+
+ def fun( matchobj ):
+ return _fromUnicodeHex( matchobj.group(0) )
+
+ retval = sub(r'_x[0-9A-Za-z]+_', fun, retval )
+
+ return retval
diff --git a/ZSI/wstools/ZPL b/ZSI/wstools/ZPL
new file mode 100644
index 0000000..ec12c9f
--- /dev/null
+++ b/ZSI/wstools/ZPL
@@ -0,0 +1,61 @@
+Zope Public License (ZPL) Version 2.0
+-----------------------------------------------
+
+This software is Copyright (c) Zope Corporation (tm) and
+Contributors. All rights reserved.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the above
+ copyright notice, this list of conditions, and the following
+ disclaimer.
+
+2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+3. The name Zope Corporation (tm) must not be used to
+ endorse or promote products derived from this software
+ without prior written permission from Zope Corporation.
+
+4. The right to distribute this software or to use it for
+ any purpose does not give you the right to use Servicemarks
+ (sm) or Trademarks (tm) of Zope Corporation. Use of them is
+ covered in a separate agreement (see
+ http://www.zope.com/Marks).
+
+5. If any files are modified, you must cause the modified
+ files to carry prominent notices stating that you changed
+ the files and the date of any change.
+
+Disclaimer
+
+ THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
+ AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGE.
+
+
+This software consists of contributions made by Zope
+Corporation and many individuals on behalf of Zope
+Corporation. Specific attributions are listed in the
+accompanying credits file.
+
+
diff --git a/ZSI/wstools/__init__.py b/ZSI/wstools/__init__.py
new file mode 100644
index 0000000..2ca5ced
--- /dev/null
+++ b/ZSI/wstools/__init__.py
@@ -0,0 +1,9 @@
+#! /usr/bin/env python
+"""WSDL parsing services package for Web Services for Python."""
+
+ident = "$Id: __init__.py 840 2004-12-07 15:54:53Z blunck2 $"
+
+import WSDLTools
+import XMLname
+import logging
+
diff --git a/ZSI/wstools/c14n.py b/ZSI/wstools/c14n.py
new file mode 100755
index 0000000..ecc9b01
--- /dev/null
+++ b/ZSI/wstools/c14n.py
@@ -0,0 +1,433 @@
+#! /usr/bin/env python
+'''XML Canonicalization
+
+Patches Applied to xml.dom.ext.c14n:
+ http://sourceforge.net/projects/pyxml/
+
+ [ 1444526 ] c14n.py: http://www.w3.org/TR/xml-exc-c14n/ fix
+ -- includes [ 829905 ] c14n.py fix for bug #825115,
+ Date Submitted: 2003-10-24 23:43
+ -- include dependent namespace declarations declared in ancestor nodes
+ (checking attributes and tags),
+ -- handle InclusiveNamespaces PrefixList parameter
+
+This module generates canonical XML of a document or element.
+ http://www.w3.org/TR/2001/REC-xml-c14n-20010315
+and includes a prototype of exclusive canonicalization
+ http://www.w3.org/Signature/Drafts/xml-exc-c14n
+
+Requires PyXML 0.7.0 or later.
+
+Known issues if using Ft.Lib.pDomlette:
+ 1. Unicode
+ 2. does not white space normalize attributes of type NMTOKEN and ID?
+ 3. seems to be include "\n" after importing external entities?
+
+Note, this version processes a DOM tree, and consequently it processes
+namespace nodes as attributes, not from a node's namespace axis. This
+permits simple document and element canonicalization without
+XPath. When XPath is used, the XPath result node list is passed and used to
+determine if the node is in the XPath result list, but little else.
+
+Authors:
+ "Joseph M. Reagle Jr." <reagle@w3.org>
+ "Rich Salz" <rsalz@zolera.com>
+
+$Date: 2006-03-30 15:47:16 -0800 (Thu, 30 Mar 2006) $ by $Author: boverhof $
+'''
+
+_copyright = '''Copyright 2001, Zolera Systems Inc. All Rights Reserved.
+Copyright 2001, MIT. All Rights Reserved.
+
+Distributed under the terms of:
+ Python 2.0 License or later.
+ http://www.python.org/2.0.1/license.html
+or
+ W3C Software License
+ http://www.w3.org/Consortium/Legal/copyright-software-19980720
+'''
+
+import string
+from xml.dom import Node
+try:
+ from xml.ns import XMLNS
+except:
+ class XMLNS:
+ BASE = "http://www.w3.org/2000/xmlns/"
+ XML = "http://www.w3.org/XML/1998/namespace"
+try:
+ import cStringIO
+ StringIO = cStringIO
+except ImportError:
+ import StringIO
+
+_attrs = lambda E: (E.attributes and E.attributes.values()) or []
+_children = lambda E: E.childNodes or []
+_IN_XML_NS = lambda n: n.name.startswith("xmlns")
+_inclusive = lambda n: n.unsuppressedPrefixes == None
+
+
+# Does a document/PI has lesser/greater document order than the
+# first element?
+_LesserElement, _Element, _GreaterElement = range(3)
+
+def _sorter(n1,n2):
+ '''_sorter(n1,n2) -> int
+ Sorting predicate for non-NS attributes.'''
+
+ i = cmp(n1.namespaceURI, n2.namespaceURI)
+ if i: return i
+ return cmp(n1.localName, n2.localName)
+
+
+def _sorter_ns(n1,n2):
+ '''_sorter_ns((n,v),(n,v)) -> int
+ "(an empty namespace URI is lexicographically least)."'''
+
+ if n1[0] == 'xmlns': return -1
+ if n2[0] == 'xmlns': return 1
+ return cmp(n1[0], n2[0])
+
+def _utilized(n, node, other_attrs, unsuppressedPrefixes):
+ '''_utilized(n, node, other_attrs, unsuppressedPrefixes) -> boolean
+ Return true if that nodespace is utilized within the node'''
+ if n.startswith('xmlns:'):
+ n = n[6:]
+ elif n.startswith('xmlns'):
+ n = n[5:]
+ if (n=="" and node.prefix in ["#default", None]) or \
+ n == node.prefix or n in unsuppressedPrefixes:
+ return 1
+ for attr in other_attrs:
+ if n == attr.prefix: return 1
+ # For exclusive need to look at attributes
+ if unsuppressedPrefixes is not None:
+ for attr in _attrs(node):
+ if n == attr.prefix: return 1
+
+ return 0
+
+
+def _inclusiveNamespacePrefixes(node, context, unsuppressedPrefixes):
+ '''http://www.w3.org/TR/xml-exc-c14n/
+ InclusiveNamespaces PrefixList parameter, which lists namespace prefixes that
+ are handled in the manner described by the Canonical XML Recommendation'''
+ inclusive = []
+ if node.prefix:
+ usedPrefixes = ['xmlns:%s' %node.prefix]
+ else:
+ usedPrefixes = ['xmlns']
+
+ for a in _attrs(node):
+ if a.nodeName.startswith('xmlns') or not a.prefix: continue
+ usedPrefixes.append('xmlns:%s' %a.prefix)
+
+ unused_namespace_dict = {}
+ for attr in context:
+ n = attr.nodeName
+ if n in unsuppressedPrefixes:
+ inclusive.append(attr)
+ elif n.startswith('xmlns:') and n[6:] in unsuppressedPrefixes:
+ inclusive.append(attr)
+ elif n.startswith('xmlns') and n[5:] in unsuppressedPrefixes:
+ inclusive.append(attr)
+ elif attr.nodeName in usedPrefixes:
+ inclusive.append(attr)
+ elif n.startswith('xmlns:'):
+ unused_namespace_dict[n] = attr.value
+
+ return inclusive, unused_namespace_dict
+
+#_in_subset = lambda subset, node: not subset or node in subset
+_in_subset = lambda subset, node: subset is None or node in subset # rich's tweak
+
+
+class _implementation:
+ '''Implementation class for C14N. This accompanies a node during it's
+ processing and includes the parameters and processing state.'''
+
+ # Handler for each node type; populated during module instantiation.
+ handlers = {}
+
+ def __init__(self, node, write, **kw):
+ '''Create and run the implementation.'''
+ self.write = write
+ self.subset = kw.get('subset')
+ self.comments = kw.get('comments', 0)
+ self.unsuppressedPrefixes = kw.get('unsuppressedPrefixes')
+ nsdict = kw.get('nsdict', { 'xml': XMLNS.XML, 'xmlns': XMLNS.BASE })
+
+ # Processing state.
+ self.state = (nsdict, {'xml':''}, {}, {}) #0422
+
+ if node.nodeType == Node.DOCUMENT_NODE:
+ self._do_document(node)
+ elif node.nodeType == Node.ELEMENT_NODE:
+ self.documentOrder = _Element # At document element
+ if not _inclusive(self):
+ inherited,unused = _inclusiveNamespacePrefixes(node, self._inherit_context(node),
+ self.unsuppressedPrefixes)
+ self._do_element(node, inherited, unused=unused)
+ else:
+ inherited = self._inherit_context(node)
+ self._do_element(node, inherited)
+ elif node.nodeType == Node.DOCUMENT_TYPE_NODE:
+ pass
+ else:
+ raise TypeError, str(node)
+
+
+ def _inherit_context(self, node):
+ '''_inherit_context(self, node) -> list
+ Scan ancestors of attribute and namespace context. Used only
+ for single element node canonicalization, not for subset
+ canonicalization.'''
+
+ # Collect the initial list of xml:foo attributes.
+ xmlattrs = filter(_IN_XML_NS, _attrs(node))
+
+ # Walk up and get all xml:XXX attributes we inherit.
+ inherited, parent = [], node.parentNode
+ while parent and parent.nodeType == Node.ELEMENT_NODE:
+ for a in filter(_IN_XML_NS, _attrs(parent)):
+ n = a.localName
+ if n not in xmlattrs:
+ xmlattrs.append(n)
+ inherited.append(a)
+ parent = parent.parentNode
+ return inherited
+
+
+ def _do_document(self, node):
+ '''_do_document(self, node) -> None
+ Process a document node. documentOrder holds whether the document
+ element has been encountered such that PIs/comments can be written
+ as specified.'''
+
+ self.documentOrder = _LesserElement
+ for child in node.childNodes:
+ if child.nodeType == Node.ELEMENT_NODE:
+ self.documentOrder = _Element # At document element
+ self._do_element(child)
+ self.documentOrder = _GreaterElement # After document element
+ elif child.nodeType == Node.PROCESSING_INSTRUCTION_NODE:
+ self._do_pi(child)
+ elif child.nodeType == Node.COMMENT_NODE:
+ self._do_comment(child)
+ elif child.nodeType == Node.DOCUMENT_TYPE_NODE:
+ pass
+ else:
+ raise TypeError, str(child)
+ handlers[Node.DOCUMENT_NODE] = _do_document
+
+
+ def _do_text(self, node):
+ '''_do_text(self, node) -> None
+ Process a text or CDATA node. Render various special characters
+ as their C14N entity representations.'''
+ if not _in_subset(self.subset, node): return
+ s = string.replace(node.data, "&", "&amp;")
+ s = string.replace(s, "<", "&lt;")
+ s = string.replace(s, ">", "&gt;")
+ s = string.replace(s, "\015", "&#xD;")
+ if s: self.write(s)
+ handlers[Node.TEXT_NODE] = _do_text
+ handlers[Node.CDATA_SECTION_NODE] = _do_text
+
+
+ def _do_pi(self, node):
+ '''_do_pi(self, node) -> None
+ Process a PI node. Render a leading or trailing #xA if the
+ document order of the PI is greater or lesser (respectively)
+ than the document element.
+ '''
+ if not _in_subset(self.subset, node): return
+ W = self.write
+ if self.documentOrder == _GreaterElement: W('\n')
+ W('<?')
+ W(node.nodeName)
+ s = node.data
+ if s:
+ W(' ')
+ W(s)
+ W('?>')
+ if self.documentOrder == _LesserElement: W('\n')
+ handlers[Node.PROCESSING_INSTRUCTION_NODE] = _do_pi
+
+
+ def _do_comment(self, node):
+ '''_do_comment(self, node) -> None
+ Process a comment node. Render a leading or trailing #xA if the
+ document order of the comment is greater or lesser (respectively)
+ than the document element.
+ '''
+ if not _in_subset(self.subset, node): return
+ if self.comments:
+ W = self.write
+ if self.documentOrder == _GreaterElement: W('\n')
+ W('<!--')
+ W(node.data)
+ W('-->')
+ if self.documentOrder == _LesserElement: W('\n')
+ handlers[Node.COMMENT_NODE] = _do_comment
+
+
+ def _do_attr(self, n, value):
+ ''''_do_attr(self, node) -> None
+ Process an attribute.'''
+
+ W = self.write
+ W(' ')
+ W(n)
+ W('="')
+ s = string.replace(value, "&", "&amp;")
+ s = string.replace(s, "<", "&lt;")
+ s = string.replace(s, '"', '&quot;')
+ s = string.replace(s, '\011', '&#x9')
+ s = string.replace(s, '\012', '&#xA')
+ s = string.replace(s, '\015', '&#xD')
+ W(s)
+ W('"')
+
+
+ def _do_element(self, node, initial_other_attrs = [], unused = None):
+ '''_do_element(self, node, initial_other_attrs = [], unused = {}) -> None
+ Process an element (and its children).'''
+
+ # Get state (from the stack) make local copies.
+ # ns_parent -- NS declarations in parent
+ # ns_rendered -- NS nodes rendered by ancestors
+ # ns_local -- NS declarations relevant to this element
+ # xml_attrs -- Attributes in XML namespace from parent
+ # xml_attrs_local -- Local attributes in XML namespace.
+ # ns_unused_inherited -- not rendered namespaces, used for exclusive
+ ns_parent, ns_rendered, xml_attrs = \
+ self.state[0], self.state[1].copy(), self.state[2].copy() #0422
+
+ ns_unused_inherited = unused
+ if unused is None:
+ ns_unused_inherited = self.state[3].copy()
+
+ ns_local = ns_parent.copy()
+ inclusive = _inclusive(self)
+ xml_attrs_local = {}
+
+ # Divide attributes into NS, XML, and others.
+ other_attrs = []
+ in_subset = _in_subset(self.subset, node)
+ for a in initial_other_attrs + _attrs(node):
+ if a.namespaceURI == XMLNS.BASE:
+ n = a.nodeName
+ if n == "xmlns:": n = "xmlns" # DOM bug workaround
+ ns_local[n] = a.nodeValue
+ elif a.namespaceURI == XMLNS.XML:
+ if inclusive or (in_subset and _in_subset(self.subset, a)): #020925 Test to see if attribute node in subset
+ xml_attrs_local[a.nodeName] = a #0426
+ else:
+ if _in_subset(self.subset, a): #020925 Test to see if attribute node in subset
+ other_attrs.append(a)
+
+# # TODO: exclusive, might need to define xmlns:prefix here
+# if not inclusive and a.prefix is not None and not ns_rendered.has_key('xmlns:%s' %a.prefix):
+# ns_local['xmlns:%s' %a.prefix] = ??
+
+ #add local xml:foo attributes to ancestor's xml:foo attributes
+ xml_attrs.update(xml_attrs_local)
+
+ # Render the node
+ W, name = self.write, None
+ if in_subset:
+ name = node.nodeName
+ if not inclusive:
+ if node.prefix is not None:
+ prefix = 'xmlns:%s' %node.prefix
+ else:
+ prefix = 'xmlns'
+
+ if not ns_rendered.has_key(prefix) and not ns_local.has_key(prefix):
+ if not ns_unused_inherited.has_key(prefix):
+ raise RuntimeError,\
+ 'For exclusive c14n, unable to map prefix "%s" in %s' %(
+ prefix, node)
+
+ ns_local[prefix] = ns_unused_inherited[prefix]
+ del ns_unused_inherited[prefix]
+
+ W('<')
+ W(name)
+
+ # Create list of NS attributes to render.
+ ns_to_render = []
+ for n,v in ns_local.items():
+
+ # If default namespace is XMLNS.BASE or empty,
+ # and if an ancestor was the same
+ if n == "xmlns" and v in [ XMLNS.BASE, '' ] \
+ and ns_rendered.get('xmlns') in [ XMLNS.BASE, '', None ]:
+ continue
+
+ # "omit namespace node with local name xml, which defines
+ # the xml prefix, if its string value is
+ # http://www.w3.org/XML/1998/namespace."
+ if n in ["xmlns:xml", "xml"] \
+ and v in [ 'http://www.w3.org/XML/1998/namespace' ]:
+ continue
+
+
+ # If not previously rendered
+ # and it's inclusive or utilized
+ if (n,v) not in ns_rendered.items():
+ if inclusive or _utilized(n, node, other_attrs, self.unsuppressedPrefixes):
+ ns_to_render.append((n, v))
+ elif not inclusive:
+ ns_unused_inherited[n] = v
+
+ # Sort and render the ns, marking what was rendered.
+ ns_to_render.sort(_sorter_ns)
+ for n,v in ns_to_render:
+ self._do_attr(n, v)
+ ns_rendered[n]=v #0417
+
+ # If exclusive or the parent is in the subset, add the local xml attributes
+ # Else, add all local and ancestor xml attributes
+ # Sort and render the attributes.
+ if not inclusive or _in_subset(self.subset,node.parentNode): #0426
+ other_attrs.extend(xml_attrs_local.values())
+ else:
+ other_attrs.extend(xml_attrs.values())
+ other_attrs.sort(_sorter)
+ for a in other_attrs:
+ self._do_attr(a.nodeName, a.value)
+ W('>')
+
+ # Push state, recurse, pop state.
+ state, self.state = self.state, (ns_local, ns_rendered, xml_attrs, ns_unused_inherited)
+ for c in _children(node):
+ _implementation.handlers[c.nodeType](self, c)
+ self.state = state
+
+ if name: W('</%s>' % name)
+ handlers[Node.ELEMENT_NODE] = _do_element
+
+
+def Canonicalize(node, output=None, **kw):
+ '''Canonicalize(node, output=None, **kw) -> UTF-8
+
+ Canonicalize a DOM document/element node and all descendents.
+ Return the text; if output is specified then output.write will
+ be called to output the text and None will be returned
+ Keyword parameters:
+ nsdict: a dictionary of prefix:uri namespace entries
+ assumed to exist in the surrounding context
+ comments: keep comments if non-zero (default is 0)
+ subset: Canonical XML subsetting resulting from XPath
+ (default is [])
+ unsuppressedPrefixes: do exclusive C14N, and this specifies the
+ prefixes that should be inherited.
+ '''
+ if output:
+ apply(_implementation, (node, output.write), kw)
+ else:
+ s = StringIO.StringIO()
+ apply(_implementation, (node, s.write), kw)
+ return s.getvalue()
diff --git a/ZSI/wstools/license.txt b/ZSI/wstools/license.txt
new file mode 100644
index 0000000..4f359ba
--- /dev/null
+++ b/ZSI/wstools/license.txt
@@ -0,0 +1,44 @@
+*********
+
+ Copyright (c) 2003, The Regents of the University of California,
+ through Lawrence Berkeley National Laboratory (subject to receipt of
+ any required approvals from the U.S. Dept. of Energy). All rights
+ reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ (3) Neither the name of the University of California, Lawrence
+ Berkeley National Laboratory, U.S. Dept. of Energy nor the names of
+ its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You are under no obligation whatsoever to provide any bug fixes,
+ patches, or upgrades to the features, functionality or performance of
+ the source code ("Enhancements") to anyone; however, if you choose to
+ make your Enhancements available either publicly, or directly to
+ Lawrence Berkeley National Laboratory, without imposing a separate
+ written license agreement for such Enhancements, then you hereby grant
+ the following license: a non-exclusive, royalty-free perpetual
+ license to install, use, modify, prepare derivative works, incorporate
+ into other computer software, distribute, and sublicense such
+ enhancements or derivative works thereof, in binary and source code form.
+
+*********
diff --git a/ZSI/wstools/logging.py b/ZSI/wstools/logging.py
new file mode 100644
index 0000000..240c134
--- /dev/null
+++ b/ZSI/wstools/logging.py
@@ -0,0 +1,110 @@
+# Copyright (c) 2003, The Regents of the University of California,
+# through Lawrence Berkeley National Laboratory (subject to receipt of
+# any required approvals from the U.S. Dept. of Energy). All rights
+# reserved.
+#
+"""Logging"""
+ident = "$Id: logging.py 1204 2006-05-03 00:13:47Z boverhof $"
+import sys
+
+WARN = 1
+DEBUG = 2
+
+
+class ILogger:
+ '''Logger interface, by default this class
+ will be used and logging calls are no-ops.
+ '''
+ level = 0
+ def __init__(self, msg):
+ return
+ def warning(self, *args):
+ return
+ def debug(self, *args):
+ return
+ def error(self, *args):
+ return
+ def setLevel(cls, level):
+ cls.level = level
+ setLevel = classmethod(setLevel)
+
+ debugOn = lambda self: self.level >= DEBUG
+ warnOn = lambda self: self.level >= WARN
+
+
+class BasicLogger(ILogger):
+ last = ''
+
+ def __init__(self, msg, out=sys.stdout):
+ self.msg, self.out = msg, out
+
+ def warning(self, msg, *args):
+ if self.warnOn() is False: return
+ if BasicLogger.last != self.msg:
+ BasicLogger.last = self.msg
+ print >>self, "---- ", self.msg, " ----"
+ print >>self, " %s " %BasicLogger.WARN,
+ print >>self, msg %args
+ WARN = '[WARN]'
+ def debug(self, msg, *args):
+ if self.debugOn() is False: return
+ if BasicLogger.last != self.msg:
+ BasicLogger.last = self.msg
+ print >>self, "---- ", self.msg, " ----"
+ print >>self, " %s " %BasicLogger.DEBUG,
+ print >>self, msg %args
+ DEBUG = '[DEBUG]'
+ def error(self, msg, *args):
+ if BasicLogger.last != self.msg:
+ BasicLogger.last = self.msg
+ print >>self, "---- ", self.msg, " ----"
+ print >>self, " %s " %BasicLogger.ERROR,
+ print >>self, msg %args
+ ERROR = '[ERROR]'
+
+ def write(self, *args):
+ '''Write convenience function; writes strings.
+ '''
+ for s in args: self.out.write(s)
+
+_LoggerClass = BasicLogger
+
+def setBasicLogger():
+ '''Use Basic Logger.
+ '''
+ setLoggerClass(BasicLogger)
+ BasicLogger.setLevel(0)
+
+def setBasicLoggerWARN():
+ '''Use Basic Logger.
+ '''
+ setLoggerClass(BasicLogger)
+ BasicLogger.setLevel(WARN)
+
+def setBasicLoggerDEBUG():
+ '''Use Basic Logger.
+ '''
+ setLoggerClass(BasicLogger)
+ BasicLogger.setLevel(DEBUG)
+
+def setLoggerClass(loggingClass):
+ '''Set Logging Class.
+ '''
+ assert issubclass(loggingClass, ILogger), 'loggingClass must subclass ILogger'
+ global _LoggerClass
+ _LoggerClass = loggingClass
+
+def setLevel(level=0):
+ '''Set Global Logging Level.
+ '''
+ ILogger.level = level
+
+def getLevel():
+ return ILogger.level
+
+def getLogger(msg):
+ '''Return instance of Logging class.
+ '''
+ return _LoggerClass(msg)
+
+
diff --git a/ZSI/wstools/test/.cvsignore b/ZSI/wstools/test/.cvsignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/ZSI/wstools/test/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/ZSI/wstools/test/README b/ZSI/wstools/test/README
new file mode 100644
index 0000000..7c1097a
--- /dev/null
+++ b/ZSI/wstools/test/README
@@ -0,0 +1,52 @@
+Two top level modules have been provided to run the tests. "test_wstools.py"
+is used to run all of the local tests. "test_wstools_net.py" is used to run
+all of the tests that require network access.
+
+Add the -v option for more informative feedback.
+
+ADDING TESTS:
+ 1. For Stand-Alone tests add WSDL FILE to appropriate archive file
+ Need to add a NEW Archive?:
+ config.txt [files] "archive" -- tuple of all archive files,
+ if you need to create a new archive append the archive
+ name to the 'archive' tuple.
+
+ 2. Edit config.txt section(s):
+ option -- name by which service will be referenced in test case.
+ Need an entry under appropriate section(s), this name
+ must be unique within each section it appears but it may
+ appear in multiple sections.
+
+ config.txt "test" sections:
+ Stand-Alone -- add "option" under [services_by_file]
+ eg. amazon = exports/AmazonWebServices.wsdl
+
+ Network -- add "option" under [services_by_http]
+ eg. amazon = http://soap.amazon.com/schemas/AmazonWebServices.wsdl
+
+ Broken -- add "option" under [broken]
+
+ 3. Done
+
+
+CONTENTS OF SAMPLE WSDL/XSD:
+ schema -- Taken from globus-3.0.1(http://www.globus.org)
+ xmethods -- Taken from XMethods(http://www.xmethods.com)
+ airport.wsdl
+ AmazonWebServices.wsdl
+ books.wsdl
+ Distance.wsdl
+ freedb.wsdl
+ globalweather.wsdl
+ IHaddock.wsdl
+ ip2geo.wsdl
+ magic.wsdl
+ query.wsdl
+ RateInfo.wsdl
+ SHA1Encrypt.wsdl
+ siteInspect.wsdl
+ TemperatureService.wsdl
+ usweather.wsdl
+ rtf2html.xml
+ SolveSystem.wsdl.xml
+ zip2geo.wsdl
diff --git a/ZSI/wstools/test/__init__.py b/ZSI/wstools/test/__init__.py
new file mode 100644
index 0000000..d5a5350
--- /dev/null
+++ b/ZSI/wstools/test/__init__.py
@@ -0,0 +1,5 @@
+#! /usr/bin/env python
+"""wstools.WSDLTools.WSDLReader tests directory."""
+
+import utils
+
diff --git a/ZSI/wstools/test/config.txt b/ZSI/wstools/test/config.txt
new file mode 100644
index 0000000..6ebbe19
--- /dev/null
+++ b/ZSI/wstools/test/config.txt
@@ -0,0 +1,362 @@
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See Copyright for copyright notice!
+###########################################################################
+
+###########################################################################
+# Config file for the unit test framework.
+# Sections below.
+###########################################################################
+
+
+
+##########################################################################
+# SECTION [files] - archives of wsdl/xsd files.
+#
+##########################################################################
+[files]
+archives = ('xmethods.tar.gz', 'schema.tar.gz')
+
+##########################################################################
+# SECTION [services_by_file] - all services locally available for
+# testing.
+##########################################################################
+[services_by_file]
+ogsi = schema/ogsi/ogsi_service.wsdl
+airport = xmethods/airport.wsdl
+distance = xmethods/Distance.wsdl
+freedb = xmethods/freedb.wsdl
+globalweather = xmethods/globalweather.wsdl
+IHaddock = xmethods/IHaddock.wsdl
+ip2geo = xmethods/ip2geo.wsdl
+magic = xmethods/magic.wsdl
+query = xmethods/query.wsdl
+RateInfo = xmethods/RateInfo.wsdl
+SHA1Encrypt = xmethods/SHA1Encrypt.wsdl
+siteInsepct = xmethods/siteInspect.wsdl
+TemperatureService = xmethods/TemperatureService.wsdl
+usweather = xmethods/usweather.wsdl
+zip2geo = xmethods/zip2geo.wsdl
+SolveSystem = xmethods/SolveSystem.wsdl.xml
+
+##########################################################################
+# SECTION [services_by_http] -
+##########################################################################
+[services_by_http]
+
+# no schemas
+AbysalSendEmail = http://www.abysal.com/soap/AbysalEmail.wsdl
+BNQuoteService = http://www.xmethods.net/sd/2001/BNQuoteService.wsdl
+BabelFishService = http://www.xmethods.net/sd/2001/BabelFishService.wsdl
+Bible = http://www.stgregorioschurchdc.org/wsdl/Bible.wsdl
+Blast = http://xml.nig.ac.jp/wsdl/Blast.wsdl
+CATrafficService = http://www.xmethods.net/sd/2001/CATrafficService.wsdl
+Calendar = http://www.stgregorioschurchdc.org/wsdl/Calendar.wsdl
+ClustalW = http://xml.nig.ac.jp/wsdl/ClustalW.wsdl
+CountryInfoLookupService = http://www.cs.uga.edu/~sent/xmethods/CountryInfoLookup.wsdl
+CurrencyExchangeService = http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl
+DDBJ = http://xml.nig.ac.jp/wsdl/DDBJ.wsdl
+DiscordianService = http://www.compkarori.com/wsdl/discordian.wsdl
+DistanceService = http://webservices.imacination.com/distance/Distance.jws?wsdl
+DocServService = http://docserv.aurigalogic.com/docserv.wsdl
+EMWebFunctionWS = http://www.eyemaginations.com/cgi-bin/getWSDL.pl?wsdl=WebFunction.wsdl
+Fasta = http://xml.nig.ac.jp/wsdl/Fasta.wsdl
+FaxService = http://oneoutbox.com/wsdl/FaxService.wsdl
+FreeFaxService = http://www.OneOutBox.com/wsdl/FreeFaxService.wsdl
+GetEntry = http://xml.nig.ac.jp/wsdl/GetEntry.wsdl
+IBorlandBabelservice = http://ww6.borland.com/webservices/BorlandBabel/BorlandBabel.exe/wsdl/IBorlandBabel
+IBorlandChessservice = http://www.danmarinescu.com/WebServices/ChessCGIServer.exe/wsdl/IBorlandChess
+IDutchservice = http://www.ebob42.com/cgi-bin/NumberToWordsInDutch.exe/wsdl/IDutch
+IEmailServiceservice = http://webservices.matlus.com/scripts/emailwebservice.dll/wsdl/IEmailService
+IHeadLineservice = http://www.ebob42.com/cgi-bin/DrBobsClinic.exe/wsdl/IHeadline
+IMapQuestservice = http://ww6.borland.com/webservices/MapQuest/MapQuest.exe/wsdl/IMapQuest
+IMsSessionBrokerServiceservice = http://webservices.matlus.com/scripts/sessionservice.dll/wsdl/IMsSessionBrokerService
+IODCODESPOSTAUXservice = http://www.e-naxos.com/scripts/enwscp.dll/wsdl/IODCODESPOSTAUX
+IPGPKeyServerservice = http://www.marotz.se/PGPKeyServer/PGPKeyServiceX.exe/wsdl/IPGPKeyServer
+IPrimeGeneratorservice = http://www.jusufdarmawan.com/wsprimegenerator.exe/wsdl/IPrimeGenerator
+IRomanservice = http://www.ebob42.com/cgi-bin/Romulan.exe/wsdl/IRoman
+ISMSServiceservice = http://sms.idws.com/soap/smsservice.dll/wsdl/ISMSService
+ISlashdotHeadlineProviderservice = http://www.marotz.se/scripts/SlashdotHeadlines.exe/wsdl/ISlashdotHeadlineProvider
+ISwedishZipInfoservice = http://www.marotz.se/scripts/zipinfo.exe/wsdl/ISwedishZipInfo
+ITempConverterservice = http://developerdays.com/cgi-bin/tempconverter.exe/wsdl/ITempConverter
+IWSMazeServerservice = http://www.culand.net/WebServices/bin/WSMaze_Server.dll/wsdl/IWSMazeServer
+IWagAddressServerSingleservice = http://62.212.78.36/cgi-bin/WagAddressServerSingle.exe/wsdl/IWagAddressServerSingle
+IWhoIsservice = http://webservices.matlus.com/scripts/whoiswebservice.dll/wsdl/IWhoIs
+Ieconomicservice = http://www.suiyi.com/soap/economic.dll/wsdl/Ieconomic
+IgetNumbersservice = http://reto.checkit.ch/Scripts/Lotto.dll/wsdl/IgetNumbers
+Iws_Verify_NRICservice = http://www.rightsecurity.biz/NRICWebServices/NRICWebServices.dll/wsdl/Iws_Verify_NRIC
+KRSS_DAML_Service = http://digilander.libero.it/mamo78/KRSS_DAML_Service.wsdl
+MBWSSoapService = http://www.extensio.com:8080/ExtensioInfoServer/mbsoap/MBWSSoapServices.wsdl
+SRS = http://xml.nig.ac.jp/wsdl/SRS.wsdl
+ServiceSMS = http://smsserver.dotnetisp.com/servicesms.asmx?WSDL
+TemperatureService = http://www.xmethods.net/sd/2001/TemperatureService.wsdl
+TxSearch = http://xml.nig.ac.jp/wsdl/TxSearch.wsdl
+UrduSOAP = http://www.apniurdu.com/SOAP/Urdu2.wsdl
+WSFindMP3 = http://xmlrad.com/WSFindMP3Bin/WSFindMP3.dll/WSDL
+WSGenerator = http://xmlrad.com/WSGeneratorBin/WSGenerator.dll/WSDL
+WorldTimeService = http://ws.digiposs.com/WorldTime.jws?wsdl
+XEMBL = http://www.ebi.ac.uk/xembl/XEMBL.wsdl
+XMethodsFilesystemService = http://www.xmethods.net/sd/2001/XMethodsFilesystemService.wsdl
+YIM Service = http://www.scdi.org/~avernet/webservice/yim.wsdl
+YahooUserPingService = http://www.allesta.net:51110/webservices/wsdl/YahooUserPingService.xml
+convert = http://www.cosme.nu/services/convert.php?wsdl
+dns = http://www.cosme.nu/services/dns.php?wsdl
+eBayWatcherService = http://www.xmethods.net/sd/2001/EBayWatcherService.wsdl
+finnwords = http://www.nickhodge.com/nhodge/finnwords/finnwords.wsdl
+pop = http://www.cosme.nu/services/pop.php?wsdl
+
+
+#simple types
+
+ABA = http://www.webservicex.net/aba.asmx?WSDL
+AmazonBox = http://www.xmlme.com/WSAmazonBox.asmx?WSDL
+AustralianPostCode = http://www.webservicex.net/AustralianPostCode.asmx?WSDL
+Autoloan = http://upload.eraserver.net/circle24/autoloan.asmx?wsdl
+BNPrice = http://www.abundanttech.com/webservices/bnprice/bnprice.wsdl
+BankCode = http://appserver.pepperzak.net/bankcode/BankCodeEJBHome/wsdl.jsp
+BarCode = http://www.webservicex.net/barcode.asmx?WSDL
+BibleWebservice = http://www.webservicex.net/BibleWebservice.asmx?wsdl
+Braille = http://www.webservicex.net/braille.asmx?WSDL
+CEqImage = http://www.quisque.com/fr/techno/eqimage/eqimage.asmx?WSDL
+CFRSearch = http://www.oakleaf.ws/cfrsearchws/cfrsearchws.asmx?wsdl
+CFRSect = http://www.oakleaf.ws/cfrsectws/cfrsectws.asmx?wsdl
+CFRToc = http://www.oakleaf.ws/cfrtocws/cfrtocws.asmx?wsdl
+CodeGenerator = http://www.esynaps.com/webservices/codegenerator.asmx?WSDL
+CreditCardValidator = http://www.richsolutions.com/RichPayments/RichCardValidator.asmx?WSDL
+CurrencyConvertor = http://www.webservicex.net/CurrencyConvertor.asmx?wsdl
+Currencyws = http://glkev.webs.innerhost.com/glkev_ws/Currencyws.asmx?WSDL
+DailyDilbert = http://www.esynaps.com/WebServices/DailyDiblert.asmx?WSDL
+DotnetDailyFact = http://www.xmlme.com/WSDailyNet.asmx?WSDL
+EMBLNucleotideSequenceWebService = http://www.webservicex.net/EMBLNucleotideSequenceWebService.asmx?wsdl
+ElectronicProductsFinder = http://www.xmlme.com/WSElectronics.asmx?WSDL
+EncryptionWS = http://test.mapfrepr.net/Encryption/Encryption.asmx?WSDL
+Fax = http://ws.acrosscommunications.com/Fax.asmx?WSDL
+FinanceService = http://www.webservicex.net/FinanceService.asmx?WSDL
+Fortune = http://adrianr.dyndns.org/Fortune/Fortune.wsdl
+GetCustomNews = http://www.xmlme.com/WSCustNews.asmx?WSDL
+GetLocalTime = http://services.develop.co.za/GetLocalTime.asmx?WSDL
+GlobalWeather = http://www.webservicex.net/globalweather.asmx?WSDL
+HCPCS = http://www.webservicex.net/hcpcs.asmx?WSDL
+IBANFunctions = http://www.bitounis.com/IBAN/IBANFuncs.asmx?WSDL
+ICD10 = http://www.webservicex.net/icd10.asmx?WSDL
+ICD9 = http://www.webservicex.net/icd9.asmx?WSDL
+ICD9Drug = http://www.webservicex.net/icd9drug.asmx?WSDL
+ICD9ToICD10 = http://www.webservicex.net/icd9toicd10.asmx?WSDL
+ICQ = http://ws.acrosscommunications.com/ICQ.asmx?WSDL
+ISearchSwedishPersonservice = http://www.marotz.se/scripts/searchperson.exe/wsdl/ISearchSwedishPerson
+InstantMessageAlert = http://www.bindingpoint.com/ws/imalert/imalert.asmx?wsdl
+LocalTime = http://www.ripedev.com/webservices/LocalTime.asmx?WSDL
+MSProxy = http://www.esynaps.com/WebServices/MsProxy.asmx?WSDL
+MXChecker = http://beta2.eraserver.net/webservices/mxchecker/mxchecker.asmx?WSDL
+NAICS = http://www.webservicex.net/NAICS.asmx?wsdl
+NFLNews = http://www.esynaps.com/WebServices/NFLNews.asmx?WSDL
+NumPager = http://ws.acrosscommunications.com/NumPager.asmx?WSDL
+OTNNews = http://otn.oracle.com/ws/otnnews?WSDL
+Paracite = http://paracite.ecs.soton.ac.uk/paracite.wsdl
+Phone = http://ws.acrosscommunications.com/Phone.asmx?WSDL
+Puki = http://www.barnaland.is/dev/puki.asmx?WSDL
+QueryIP = http://ws.cdyne.com/whoisforip/queryip.asmx?wsdl
+Quotes = http://www.seshakiran.com/QuoteService/QuotesService.asmx?wsdl
+QuranVerse = http://aspnet.lamaan.com/webservices/QuranVerse.asmx?WSDL
+RSAFuncs = http://www.bitounis.com/RSAFunctions/RSAFuncs.asmx?WSDL
+RSStoHTML = http://www.webservicex.net/RssToHTML.asmx?WSDL
+#SMS = http://ws.acrosscommunications.com/SMS.asmx?WSDL
+#SMS_1 = http://www.barnaland.is/dev/sms.asmx?WSDL
+SQLDataSoap = http://www.SoapClient.com/xml/SQLDataSoap.wsdl
+SecureXML = http://www.securexml.net/securexml/securexml.wsdl
+SendSMSWorld = http://www.webservicex.net/sendsmsworld.asmx?WSDL
+Shakespeare = http://www.xmlme.com/WSShakespeare.asmx?WSDL
+SportingGoodsFinder = http://www.xmlme.com/WSSportingGoods.asmx?WSDL
+StockQuote = http://www.webservicex.net/stockquote.asmx?WSDL
+StockQuotes = http://www.gama-system.com/webservices/stockquotes.asmx?wsdl
+TAP = http://ws.acrosscommunications.com/TAP.asmx?WSDL
+UDDIBusinessFinder = http://www.webservicex.net/UDDIBusinessFinder.asmx?WSDL
+UKLocation = http://www.webservicex.net/uklocation.asmx?WSDL
+UNSPSCConvert = http://www.codemechanisms.co.uk/WebServices/UNSPSC.asmx?WSDL
+USWeather = http://www.webservicex.net/usweather.asmx?WSDL
+ValidateEmail = http://www.webservicex.net/ValidateEmail.asmx?WSDL
+VideoGamesFinder = http://www.xmlme.com/WSVideoGames.asmx?WSDL
+WebChart = http://www.gxchart.com/webchart.wsdl
+WebSearchWS = http://www.esynaps.com/WebServices/SearchWS.asmx?WSDL
+WhoIS = http://ws.cdyne.com/whoisquery/whois.asmx?wsdl
+WhoIsService = http://www.esynaps.com/WebServices/WhoIsService.asmx?WSDL
+XmlDailyFact = http://www.xmlme.com/WSDailyXml.asmx?WSDL
+XmlTracking = http://www.baxglobal.com/xmltracking/xmltracking.asmx?wsdl
+XreOnline = http://www.codecube.net/services/xreonline.asmx?WSDL
+ZipCodesService = http://webservices.instantlogic.com/zipcodes.ils?wsdl
+airport = http://www.webservicex.net/airport.asmx?wsdl
+bork = http://www.x-ws.de/cgi-bin/bork/service.wsdl
+chat = http://www.x-ws.de/cgi-bin/eliza/chat.wsdl
+country = http://www.webservicex.net/country.asmx?wsdl
+eSynapsFeed = http://www.esynaps.com/WebServices/eSynapsFeed.asmx?WSDL
+eSynapsSerach = http://www.esynaps.com/WebServices/eSynapsSearch.asmx?WSDL
+engtoarabic = http://www.dl-me.com/etoaservice/engtoarabic.asmx?WSDL
+fWArticleService = http://www.framewerks.com/WebServices/fWArticleService/fwArticles.asmx?WSDL
+fax = http://www.webservicex.net/fax.asmx?wsdl
+foxcentral = http://www.foxcentral.net/foxcentral.wsdl
+iifws = http://www.inkostar.com/wsdl/iifws/iifws.wsdl
+imstatus = http://www.x-ws.de/cgi-bin/msn/imstatus.wsdl
+periodictable = http://www.webservicex.net/periodictable.asmx?wsdl
+piglatin = http://www.aspxpressway.com/maincontent/webservices/piglatin.asmx?wsdl
+unitext = http://www.dl-me.com/webservices/unitext.asmx?wsdl
+wwhelpservice = http://www.west-wind.com/wconnect/soap/wwhelpservice.wsdl
+xmlserver = http://xml.redcoal.net/SMSSOAP/xmlserver.wsdl
+
+# complex types
+
+AddFinderService = http://www.lixusnet.com/lixusnet/AddFinder.jws?wsdl
+AddressFinder = http://arcweb.esri.com/services/v2/AddressFinder.wsdl
+AddressLookup = http://ws.cdyne.com/psaddress/addresslookup.asmx?wsdl
+AmazonQuery = http://majordojo.com/amazon_query/amazon_query.wsdl
+AmazonSearch = http://soap.amazon.com/schemas/AmazonWebServices.wsdl
+BondService = http://www.financialwebservices.ltd.uk/axis/services/bond?wsdl
+BusinessNews = http://glkev.webs.innerhost.com/glkev_ws/businessnews.asmx?WSDL
+CarRentalQuotesService = http://wavendon.dsdata.co.uk/axis/services/CarRentalQuotes?wsdl
+CupScores = http://scores.serviceobjects.com/CupScores.asmx?WSDL
+DOTSAddressValidate = http://ws2.serviceobjects.net/av/AddressValidate.asmx?WSDL
+DOTSDomainSpy = http://ws2.serviceobjects.net/ds/domainspy.asmx?WSDL
+DOTSEmailValidate = http://ws2.serviceobjects.net/ev/EmailValidate.asmx?WSDL
+DOTSFastQuote = http://ws2.serviceobjects.net/sq/FastQuote.asmx?WSDL
+DOTSFastTax = http://ws2.serviceobjects.net/ft/FastTax.asmx?WSDL
+DOTSFastWeather = http://ws2.serviceobjects.net/fw/FastWeather.asmx?WSDL
+DOTSGeoCash = http://ws2.serviceobjects.net/gc/GeoCash.asmx?WSDL
+DOTSGeoPhone = http://ws2.serviceobjects.net/gp/GeoPhone.asmx?WSDL
+DOTSGeoPinPoint = http://ws2.serviceobjects.net/gpp/GeoPinPoint.asmx?WSDL
+DOTSLotteryNumbers = http://ws2.serviceobjects.net/ln/lotterynumbers.asmx?WSDL
+DOTSPackageTracking = http://ws2.serviceobjects.net/pt/PackTrack.asmx?WSDL
+DOTSPatentOffice = http://ws2.serviceobjects.net/uspo/USPatentOffice.asmx?WSDL
+DOTSPhoneAppend = http://ws2.serviceobjects.net/pa/phoneappend.asmx?wsdl
+DOTSShippingComparison = http://ws2.serviceobjects.net/pc/packcost.asmx?WSDL
+DOTSUPC = http://ws2.serviceobjects.net/upc/UPC.asmx?WSDL
+DOTSYellowPages = http://ws2.serviceobjects.net/yp/YellowPages.asmx?WSDL
+Dispenser = http://www.blackstoneonline.com/webservices/dispenser.xml
+DocConverterService = http://telecommerce.danet.de/axis/services/DocConverterServicePort?wsdl
+FOPService = http://live.capescience.com/wsdl/FOPService.wsdl
+FedRoutingDirectoryService = http://demo.soapam.com/services/FedEpayDirectory/FedEpayDirectoryService.wsdl
+GMChart = http://service.graphmagic.com/GMService/GraphMagic.asmx?wsdl
+GeoPlaces = http://www.codebump.com/services/placelookup.asmx?wsdl
+GlobalWeather = http://live.capescience.com/wsdl/GlobalWeather.wsdl
+GoogleSearch = http://api.google.com/GoogleSearch.wsdl
+HPcatalogService = http://www.lixusnet.com/lixusnet/HPcatalog.jws?wsdl
+HTMLeMail = http://www.framewerks.com/WebServices/HTMLeMail/HTMLeMail.asmx?WSDL
+HelpfulFunctions = http://www.framewerks.com/WebServices/helpfulfunctions/helpfulfunctions.asmx?WSDL
+HistoricalStockQuotes = http://glkev.webs.innerhost.com/glkev_ws/HistoricalStockQuotes.asmx?WSDL
+Horoscope = http://www.swanandmokashi.com/HomePage/WebServices/Horoscope.asmx?WSDL
+IACHSOAPservice = http://soap.achchex.com/exec/achsoap.dll/wsdl/IACHSOAP
+IP2Geo = http://ws.cdyne.com/ip2geo/ip2geo.asmx?wsdl
+ISoapFindMP3service = http://www.agnisoft.com/soap/mssoapmp3search.xml
+ITeeChartservice = http://www.berneda.com/scripts/TeeChartSOAP.exe/wsdl/ITeeChart
+IZPOP3service = http://www.zanetti-dev.com/scripts/zpop3ws.exe/wsdl/IZPOP3
+LookyBookService = http://www.winisp.net/cheeso/books/books.asmx?WSDL
+MailLocate = http://www.maillocate.com/soap/index.php?wsdl
+NavBarServer = http://ws.xara.com/navbar/navbar.wsdl
+Online Messenger Service = http://www.nims.nl/soap/oms.wsdl
+OnlineMessengerService = http://www.nims.nl/soap/oms2.wsdl
+Option_x0020_Pricing_x0020_Calculator = http://www.indobiz.com/OptionPricing.asmx?WSDL
+PersonLookup = http://www.barnaland.is/dev/personlookup.asmx?WSDL
+Phonebook = http://www.barnaland.is/dev/phonebook.asmx?WSDL
+PopulationWS = http://www.abundanttech.com/webservices/population/population.wsdl
+QueryInterfaceService = http://www.transactionalweb.com/SOAP/globalskilocator.wsdl
+QuizService = http://java.rus.uni-stuttgart.de/quiz/quiz.wsdl
+QuoteOfTheDay = http://www.swanandmokashi.com/HomePage/WebServices/QuoteOfTheDay.asmx?WSDL
+RateInfoClass = http://www.xeeinc.com/RateInformation/RateInfo.asmx?WSDL
+RateInfoClass_1 = http://www.xeeinc.com/RateInformation/Rateinfo.asmx?WSDL
+RecipeService = http://icuisine.net/webservices/RecipeService.asmx?WSDL
+RenderServer3D = http://ws.xara.com/graphicrender/render3d.wsdl
+RichPayments = http://www.richsolutions.com/richpayments/richpay.asmx?WSDL
+SBGGetAirFareQuoteService = http://wavendon.dsdata.co.uk:8080/axis/services/SBGGetAirFareQuote?wsdl
+SMS = http://www.abctext.com/webservices/SMS.asmx?WSDL
+SalesRankNPrice = http://www.PerfectXML.NET/WebServices/SalesRankNPrice/BookService.asmx?WSDL
+SendSMS = http://www.webservicex.net/SendSMS.asmx?WSDL
+Server = http://addison.ra.cwru.edu/orc/calendar_copy/server.php?wsdl
+Service = http://www.ejse.com/WeatherService/Service.asmx?WSDL
+SpamKillerService = http://wavendon.dsdata.co.uk/axis/services/SpamKiller?wsdl
+StockQuotes = http://www.swanandmokashi.com/HomePage/WebServices/StockQuotes.asmx?WSDL
+TWSFissionDotNet = http://www.sidespace.com/ws/fission/fissiondotnet.php?wsdl
+TerraService = http://terraservice.net/TerraService.asmx?WSDL
+Transform = http://transform.dataconcert.com/transform.wsdl
+UPSTracking = http://glkev.webs.innerhost.com/glkev_ws/UPSTracking.asmx?WSDL
+URLjr_Library = http://urljr.com/soap
+WeatherFetcher = http://glkev.webs.innerhost.com/glkev_ws/WeatherFetcher.asmx?WSDL
+WeatherService = http://www.hkwizard.com/WeatherService.asmx?wsdl
+WebServiceOfTheDay = http://www.webserviceoftheday.com/ws/soap/wsotd.asmx?wsdl
+WeblogsSubscriber = http://soap.4s4c.com/weblogs/subscribe.wsdl
+WhoIs = http://ws2.serviceobjects.net/whi/WhoIs.asmx?WSDL
+WhoisDataService = http://wavendon.dsdata.co.uk/axis/services/WhoisData?wsdl
+WolframSearchService = http://webservices.wolfram.com/services/SearchServices/WolframSearch.wsdl
+XMethodsQuery = http://www.xmethods.net/wsdl/query.wsdl
+XigniteEdgar = http://www.xignite.com/xEdgar.asmx?WSDL
+XigniteNews = http://www.xignite.com/xnews.asmx?WSDL
+XigniteOptions = http://www.xignite.com/xoptions.asmx?WSDL
+XigniteQuotes = http://www.xignite.com/xquotes.asmx?WSDL
+XigniteRealTime = http://www.xignite.com/xrealtime.asmx?WSDL
+XigniteRetirement = http://www.xignite.com/xretirement.asmx?WSDL
+XigniteSecurity = http://www.xignite.com/xsecurity.asmx?WSDL
+XigniteSimulation = http://www.xignite.com/xsimulation.asmx?WSDL
+XigniteStatistics = http://www.xignite.com/xstatistics.asmx?WSDL
+XigniteSurvey = http://www.xignite.com/xSurvey.asmx?WSDL
+XigniteWorldNews = http://www.xignite.com/xworldnews.asmx?WSDL
+YourHost = http://www.esynaps.com/webservices/YourHostInfo.asmx?WSDL
+Zip2Geo = http://ws.cdyne.com/ziptogeo/zip2geo.asmx?wsdl
+ZipCode = http://www.ripedev.com/webservices/ZipCode.asmx?WSDL
+ZipCodeResolver = http://webservices.eraserver.net/zipcoderesolver/zipcoderesolver.asmx?WSDL
+ZipCodes = http://www.codebump.com/services/zipcodelookup.asmx?wsdl
+ZipcodeLookupService = http://www.winisp.net/cheeso/zips/ZipService.asmx?WSDL
+certServices = http://soapclient.com/xml/certService.wsdl
+check = http://ws.cdyne.com/SpellChecker/check.asmx?wsdl
+com.systinet.demo.freedb.FreeDBService = http://soap.systinet.net/demos/FreeDB/wsdl
+com.systinet.demo.ftp.FTPService = http://soap.systinet.net/demos/FTPService/wsdl
+com.systinet.demo.newsfeed.version1.NewsfeedService = http://soap.systinet.net/demos/Newsfeed/wsdl
+com.systinet.demo.rpmfind.RpmService = http://soap.systinet.net/demos/RpmFinder/wsdl
+com.systinet.demo.search.w3c.W3CSearchService = http://soap.systinet.net/demos/W3CSearch/wsdl
+com.systinet.demo.search.zvon.ZVONSearchService = http://soap.systinet.net/demos/ZVONSearch/wsdl
+dic2 = http://www.dl-me.com/webservices/dic2.asmx?WSDL
+eSynapsMonitor = http://www.esynaps.com/WebServices/eSynapsMonitor.wsdl
+ev = http://ws.cdyne.com/emailverify/ev.asmx?wsdl
+getQuakeDataService = http://webservices.tei.or.th/getQuakeData.cfc?wsdl
+getSessionReport = http://sandbox.grandcentral.com/services/reports?WSDL
+pwspNoCentrbankCurRates = http://server1.pointwsp.net/ws/finance/currency.asmx?WSDL
+sekeywordService = http://www.aspiringgeek.com/cfc/keyword/sekeyword.cfc?wsdl
+threatService = http://www.boyzoid.com/threat.cfc?wsdl
+xmethods_gcd = http://samples.bowstreet.com/bowstreet5/webengine/xmethods/gcd/Action!getWSDL
+
+
+
+##########################################################################
+# SECTION [reader_errors] -
+# unable to load file
+##########################################################################
+[reader_errors]
+
+BusinessFinder(UDDI)-WebService = http://www.esynaps.com/WebServices/BusinessList.asmx?WSDL
+ColdFusionTip-of-the-Day = http://www.forta.com/cf/tips/syndicate.cfc?wsdl
+ComputerDictionarySearch = http://dotnet.cyberthink.net/computerdictionary/computerdictionary.asmx?wsdl
+DynamicChartingofXMLData = http://webservices.isitedesign.com/ws/chartWS.cfc?wsdl
+EmailServices = http://soap.einsteinware.com/email/emailservices.asmx?WSDL
+ExpressionEvaluator = http://www.onepercentsoftware.com/axis/services/EvaluationService?wsdl
+FonttoGraphic = http://ws.cdyne.com/FontToGraphic/ftg.asmx?wsdl
+HolidayInformation = http://wsdl.wsdlfeeds.com/holidays.cfc?wsdl
+Html2Xml = http://www.dev1.eraserver.net/REFLECTIONIT/Html2xml.asmx?WSDL
+HuZip = http://www.c6.hu/ws/huzip.wsdl
+HuarananetPresstechnologynews = http://www22.brinkster.com/horaciovallejo/netpress1.asmx?wsdl
+InfosVille = http://www.dotnetisp.com/webservices/dotnetisp/ville.asmx?WSDL
+ItalianFiscalCode = http://www.pinellus.com/cfc/Cod_fiscale.cfc?wsdl
+LinearSystemsSolver = http://www.cs.fsu.edu/~engelen/lu.wsdl
+LiveScoreService = http://www.freshscore.com/service/FreshScoreLiveScores.asmx?WSDL
+LogFileParser = http://www.bitounis.com/W3CParser/LogFileParser.asmx?WSDL
+MP3.comMusicCharts = http://webservices.mp3.com/MP3Charts.wsdl
+MachNumberWebService = http://www.cgi101.com/~msmithso/wsdl/mach.wsdl
+MagicSquares = http://www.cs.fsu.edu/~engelen/magic.wsdl
+MysicSearchEngine = http://mysic.com/Webservices/MysicSearchEngine.asmx?WSDL
+NASCARWinstonCupStatistics = http://soap.einsteinware.com/nascar/nascardataservice.asmx?WSDL
+OpenDirectoryProject = http://wsdl.wsdlfeeds.com/odp.cfc?wsdl
+SchemaWebWebService = http://www.schemaweb.info/webservices/soap/SchemaWebSoap.asmx?wsdl
+SlashdotNewsFeed = http://webservices.isitedesign.com/ws/slashdotnews.cfc?wsdl
+SpamKiller = http://soap.prowizorka.com/spam/wsdl/ISpamCheck
+SpellCheck = http://www.worldwidedesktop.com/spellcheck/spellcheckservice.asmx?wsdl
+SpellChecker = http://wsdl.wsdlfeeds.com/spell.cfc?wsdl
+USAZipcodeInformation = http://www.webservicex.net/uszip.asmx?WSDL
+WebEvents = http://www.bitounis.com/WebEvents/events.asmx?WSDL
+WebRTF2HTML = http://www.infoaccelerator.net/cfc/rtf2html.cfc?WSDL
+cp2ville = http://www.dotnetisp.com/webservices/dotnetisp/codepostal.asmx?WSDL
+src2html = http://www.dotnetisp.com/webservices/dotnetisp/src2html.asmx?WSDL
diff --git a/ZSI/wstools/test/schema.tar.gz b/ZSI/wstools/test/schema.tar.gz
new file mode 100644
index 0000000..d6fe7db
--- /dev/null
+++ b/ZSI/wstools/test/schema.tar.gz
Binary files differ
diff --git a/ZSI/wstools/test/test_t1.py b/ZSI/wstools/test/test_t1.py
new file mode 100644
index 0000000..5c33899
--- /dev/null
+++ b/ZSI/wstools/test/test_t1.py
@@ -0,0 +1,20 @@
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import unittest
+import test_wsdl
+import utils
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(test_wsdl.makeTestSuite("services_by_file"))
+ return suite
+
+def main():
+ loader = utils.MatchTestLoader(True, None, "makeTestSuite")
+ unittest.main(defaultTest="makeTestSuite", testLoader=loader)
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/wstools/test/test_wsdl.py b/ZSI/wstools/test/test_wsdl.py
new file mode 100644
index 0000000..90b0c4d
--- /dev/null
+++ b/ZSI/wstools/test/test_wsdl.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+import sys, unittest
+import ConfigParser
+from ZSI.wstools.Utility import DOM
+from ZSI.wstools.WSDLTools import WSDLReader
+from ZSI.wstools.TimeoutSocket import TimeoutError
+
+class WSDLToolsTestCase(unittest.TestCase):
+
+ def __init__(self, methodName='runTest'):
+ unittest.TestCase.__init__(self, methodName)
+
+ def setUp(self):
+ self.path = nameGenerator.next()
+ print self.path
+ sys.stdout.flush()
+
+ def __str__(self):
+ teststr = unittest.TestCase.__str__(self)
+ if hasattr(self, "path"):
+ return "%s: %s" % (teststr, self.path )
+ else:
+ return "%s" % (teststr)
+
+ def checkWSDLCollection(self, tag_name, component, key='name'):
+ if self.wsdl is None:
+ return
+ definition = self.wsdl.document.documentElement
+ version = DOM.WSDLUriToVersion(definition.namespaceURI)
+ nspname = DOM.GetWSDLUri(version)
+ for node in DOM.getElements(definition, tag_name, nspname):
+ name = DOM.getAttr(node, key)
+ comp = component[name]
+ self.failUnlessEqual(eval('comp.%s' %key), name)
+
+ def checkXSDCollection(self, tag_name, component, node, key='name'):
+ for cnode in DOM.getElements(node, tag_name):
+ name = DOM.getAttr(cnode, key)
+ component[name]
+
+ def test_all(self):
+ try:
+ if self.path[:7] == 'http://':
+ self.wsdl = WSDLReader().loadFromURL(self.path)
+ else:
+ self.wsdl = WSDLReader().loadFromFile(self.path)
+
+ except TimeoutError:
+ print "connection timed out"
+ sys.stdout.flush()
+ return
+ except:
+ self.path = self.path + ": load failed, unable to start"
+ raise
+
+ try:
+ self.checkWSDLCollection('service', self.wsdl.services)
+ except:
+ self.path = self.path + ": wsdl.services"
+ raise
+
+ try:
+ self.checkWSDLCollection('message', self.wsdl.messages)
+ except:
+ self.path = self.path + ": wsdl.messages"
+ raise
+
+ try:
+ self.checkWSDLCollection('portType', self.wsdl.portTypes)
+ except:
+ self.path = self.path + ": wsdl.portTypes"
+ raise
+
+ try:
+ self.checkWSDLCollection('binding', self.wsdl.bindings)
+ except:
+ self.path = self.path + ": wsdl.bindings"
+ raise
+
+ try:
+ self.checkWSDLCollection('import', self.wsdl.imports, key='namespace')
+ except:
+ self.path = self.path + ": wsdl.imports"
+ raise
+
+ try:
+ for key in self.wsdl.types.keys():
+ schema = self.wsdl.types[key]
+ self.failUnlessEqual(key, schema.getTargetNamespace())
+
+ definition = self.wsdl.document.documentElement
+ version = DOM.WSDLUriToVersion(definition.namespaceURI)
+ nspname = DOM.GetWSDLUri(version)
+ for node in DOM.getElements(definition, 'types', nspname):
+ for snode in DOM.getElements(node, 'schema'):
+ tns = DOM.findTargetNS(snode)
+ schema = self.wsdl.types[tns]
+ self.schemaAttributesDeclarations(schema, snode)
+ self.schemaAttributeGroupDeclarations(schema, snode)
+ self.schemaElementDeclarations(schema, snode)
+ self.schemaTypeDefinitions(schema, snode)
+ except:
+ self.path = self.path + ": wsdl.types"
+ raise
+
+ if self.wsdl.extensions:
+ print 'No check for WSDLTools(%s) Extensions:' %(self.wsdl.name)
+ for ext in self.wsdl.extensions: print '\t', ext
+
+ def schemaAttributesDeclarations(self, schema, node):
+ self.checkXSDCollection('attribute', schema.attr_decl, node)
+
+ def schemaAttributeGroupDeclarations(self, schema, node):
+ self.checkXSDCollection('group', schema.attr_groups, node)
+
+ def schemaElementDeclarations(self, schema, node):
+ self.checkXSDCollection('element', schema.elements, node)
+
+ def schemaTypeDefinitions(self, schema, node):
+ self.checkXSDCollection('complexType', schema.types, node)
+ self.checkXSDCollection('simpleType', schema.types, node)
+
+
+def setUpOptions(section):
+ cp = ConfigParser.ConfigParser()
+ cp.read('config.txt')
+ if not cp.sections():
+ print 'fatal error: configuration file config.txt not present'
+ sys.exit(0)
+ if not cp.has_section(section):
+ print '%s section not present in configuration file, exiting' % section
+ sys.exit(0)
+ return cp, len(cp.options(section))
+
+def getOption(cp, section):
+ for name, value in cp.items(section):
+ yield value
+
+def makeTestSuite(section='services_by_file'):
+ global nameGenerator
+
+ cp, numTests = setUpOptions(section)
+ nameGenerator = getOption(cp, section)
+ suite = unittest.TestSuite()
+ for i in range(0, numTests):
+ suite.addTest(unittest.makeSuite(WSDLToolsTestCase, 'test_'))
+ return suite
+
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+
+if __name__ == "__main__" : main()
diff --git a/ZSI/wstools/test/test_wstools.py b/ZSI/wstools/test/test_wstools.py
new file mode 100644
index 0000000..0e0f958
--- /dev/null
+++ b/ZSI/wstools/test/test_wstools.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+import unittest, tarfile, os, ConfigParser
+import test_wsdl
+
+
+SECTION='files'
+CONFIG_FILE = 'config.txt'
+
+def extractFiles(section, option):
+ config = ConfigParser.ConfigParser()
+ config.read(CONFIG_FILE)
+ archives = config.get(section, option)
+ archives = eval(archives)
+ for file in archives:
+ tar = tarfile.open(file)
+ if not os.access(tar.membernames[0], os.R_OK):
+ for i in tar.getnames():
+ tar.extract(i)
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(test_wsdl.makeTestSuite("services_by_file"))
+ return suite
+
+def main():
+ extractFiles(SECTION, 'archives')
+ unittest.main(defaultTest="makeTestSuite")
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/wstools/test/test_wstools_net.py b/ZSI/wstools/test/test_wstools_net.py
new file mode 100644
index 0000000..880cff3
--- /dev/null
+++ b/ZSI/wstools/test/test_wstools_net.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import unittest
+import test_wsdl
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(test_wsdl.makeTestSuite("services_by_http"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/wstools/test/xmethods.tar.gz b/ZSI/wstools/test/xmethods.tar.gz
new file mode 100644
index 0000000..4521a9a
--- /dev/null
+++ b/ZSI/wstools/test/xmethods.tar.gz
Binary files differ
diff --git a/ZSI/zsi/ZSI/ServiceContainer.py b/ZSI/zsi/ZSI/ServiceContainer.py
new file mode 100644
index 0000000..a12d803
--- /dev/null
+++ b/ZSI/zsi/ZSI/ServiceContainer.py
@@ -0,0 +1,498 @@
+#! /usr/bin/env python
+'''Simple Service Container
+ -- use with wsdl2py generated modules.
+'''
+
+import urlparse, types, os, sys, cStringIO as StringIO, thread,re
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+from ZSI import ParseException, FaultFromException, FaultFromZSIException, Fault
+from ZSI import _copyright, _seqtypes, _get_element_nsuri_name, resolvers
+from ZSI import _get_idstr
+from ZSI.address import Address
+from ZSI.parse import ParsedSoap
+from ZSI.writer import SoapWriter
+from ZSI.dispatch import _ModPythonSendXML, _ModPythonSendFault, _CGISendXML, _CGISendFault
+from ZSI.dispatch import SOAPRequestHandler as BaseSOAPRequestHandler
+
+"""
+Functions:
+ _Dispatch
+ AsServer
+ GetSOAPContext
+
+Classes:
+ SOAPContext
+ NoSuchService
+ PostNotSpecified
+ SOAPActionNotSpecified
+ ServiceSOAPBinding
+ SimpleWSResource
+ SOAPRequestHandler
+ ServiceContainer
+"""
+class NoSuchService(Exception): pass
+class UnknownRequestException(Exception): pass
+class PostNotSpecified(Exception): pass
+class SOAPActionNotSpecified(Exception): pass
+class WSActionException(Exception): pass
+class WSActionNotSpecified(WSActionException): pass
+class NotAuthorized(Exception): pass
+class ServiceAlreadyPresent(Exception): pass
+
+
+class SOAPContext:
+ def __init__(self, container, xmldata, ps, connection, httpheaders,
+ soapaction):
+
+ self.container = container
+ self.xmldata = xmldata
+ self.parsedsoap = ps
+ self.connection = connection
+ self.httpheaders= httpheaders
+ self.soapaction = soapaction
+
+_contexts = dict()
+def GetSOAPContext():
+ global _contexts
+ return _contexts[thread.get_ident()]
+
+def _Dispatch(ps, server, SendResponse, SendFault, post, action, nsdict={}, **kw):
+ '''Send ParsedSoap instance to ServiceContainer, which dispatches to
+ appropriate service via post, and method via action. Response is a
+ self-describing pyobj, which is passed to a SoapWriter.
+
+ Call SendResponse or SendFault to send the reply back, appropriately.
+ server -- ServiceContainer instance
+
+ '''
+ localURL = 'http://%s:%d%s' %(server.server_name,server.server_port,post)
+ address = action
+ service = server.getNode(post)
+ isWSResource = False
+ if isinstance(service, SimpleWSResource):
+ isWSResource = True
+ service.setServiceURL(localURL)
+ address = Address()
+ try:
+ address.parse(ps)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+ if action and action != address.getAction():
+ e = WSActionException('SOAP Action("%s") must match WS-Action("%s") if specified.' \
+ %(action,address.getAction()))
+ return SendFault(FaultFromException(e, 0, None), **kw)
+ action = address.getAction()
+
+ if isinstance(service, ServiceInterface) is False:
+ e = NoSuchService('no service at POST(%s) in container: %s' %(post,server))
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+ if not service.authorize(None, post, action):
+ return SendFault(Fault(Fault.Server, "Not authorized"), code=401)
+ #try:
+ # raise NotAuthorized()
+ #except Exception, e:
+ #return SendFault(FaultFromException(e, 0, None), code=401, **kw)
+ ##return SendFault(FaultFromException(NotAuthorized(), 0, None), code=401, **kw)
+
+ try:
+ method = service.getOperation(ps, address)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+ try:
+ if isWSResource is True:
+ result = method(ps, address)
+ else:
+ result = method(ps)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+ # Verify if Signed
+ service.verify(ps)
+
+ # If No response just return.
+ if result is None:
+ return
+
+ sw = SoapWriter(nsdict=nsdict)
+ try:
+ sw.serialize(result)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+ if isWSResource is True:
+ action = service.getResponseAction(action)
+ addressRsp = Address(action=action)
+ try:
+ addressRsp.setResponseFromWSAddress(address, localURL)
+ addressRsp.serialize(sw)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+ # Create Signatures
+ service.sign(sw)
+
+ try:
+ soapdata = str(sw)
+ return SendResponse(soapdata, **kw)
+ except Exception, e:
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+
+def AsServer(port=80, services=()):
+ '''port --
+ services -- list of service instances
+ '''
+ address = ('', port)
+ sc = ServiceContainer(address, services)
+ #for service in services:
+ # path = service.getPost()
+ # sc.setNode(service, path)
+ sc.serve_forever()
+
+
+class ServiceInterface:
+ '''Defines the interface for use with ServiceContainer Handlers.
+
+ class variables:
+ soapAction -- dictionary of soapAction keys, and operation name values.
+ These are specified in the WSDL soap bindings. There must be a
+ class method matching the operation name value. If WS-Action is
+ used the keys are WS-Action request values, according to the spec
+ if soapAction and WS-Action is specified they must be equal.
+
+ wsAction -- dictionary of operation name keys and WS-Action
+ response values. These values are specified by the portType.
+
+ root -- dictionary of root element keys, and operation name values.
+
+ '''
+ soapAction = {}
+ wsAction = {}
+ root = {}
+
+ def __init__(self, post):
+ self.post = post
+
+ def authorize(self, auth_info, post, action):
+ return 1
+
+ def __str__(self):
+ return '%s(%s) POST(%s)' %(self.__class__.__name__, _get_idstr(self), self.post)
+
+ def sign(self, sw):
+ return
+
+ def verify(self, ps):
+ return
+
+ def getPost(self):
+ return self.post
+
+ def getOperation(self, ps, action):
+ '''Returns a method of class.
+ action -- soapAction value
+ '''
+ opName = self.getOperationName(ps, action)
+ return getattr(self, opName)
+
+ def getOperationName(self, ps, action):
+ '''Returns operation name.
+ action -- soapAction value
+ '''
+ method = self.root.get(_get_element_nsuri_name(ps.body_root)) or \
+ self.soapAction.get(action)
+ if method is None:
+ raise UnknownRequestException, \
+ 'failed to map request to a method: action(%s), root%s' %(action,_get_element_nsuri_name(ps.body_root))
+ return method
+
+
+class ServiceSOAPBinding(ServiceInterface):
+ '''Binding defines the set of wsdl:binding operations, it takes as input a
+ ParsedSoap instance and parses it into a pyobj. It returns a response pyobj.
+ '''
+ def __init__(self, post):
+ ServiceInterface.__init__(self, post)
+
+ def __call___(self, action, ps):
+ return self.getOperation(ps, action)(ps)
+
+
+class SimpleWSResource(ServiceSOAPBinding):
+ '''Simple WSRF service, performs method resolutions based
+ on WS-Action values rather than SOAP Action.
+
+ class variables:
+ encoding
+ wsAction -- Must override to set output Action values.
+ soapAction -- Must override to set input Action values.
+ '''
+ encoding = "UTF-8"
+
+ def __init__(self, post=None):
+ '''
+ post -- POST value
+ '''
+ assert isinstance(self.soapAction, dict), "soapAction must be a dict"
+ assert isinstance(self.wsAction, dict), "wsAction must be a dict"
+ ServiceSOAPBinding.__init__(self, post)
+
+ def __call___(self, action, ps, address):
+ return self.getOperation(ps, action)(ps, address)
+
+ def getServiceURL(self):
+ return self._url
+
+ def setServiceURL(self, url):
+ self._url = url
+
+ def getOperation(self, ps, address):
+ '''Returns a method of class.
+ address -- ws-address
+ '''
+ action = address.getAction()
+ opName = self.getOperationName(ps, action)
+ return getattr(self, opName)
+
+ def getResponseAction(self, ps, action):
+ '''Returns response WS-Action if available
+ action -- request WS-Action value.
+ '''
+ opName = self.getOperationName(ps, action)
+ if self.wsAction.has_key(opName) is False:
+ raise WSActionNotSpecified, 'wsAction dictionary missing key(%s)' %opName
+ return self.wsAction[opName]
+
+ def do_POST(self):
+ '''The POST command. This is called by HTTPServer, not twisted.
+ action -- SOAPAction(HTTP header) or wsa:Action(SOAP:Header)
+ '''
+ global _contexts
+
+ soapAction = self.headers.getheader('SOAPAction')
+ post = self.path
+ if not post:
+ raise PostNotSpecified, 'HTTP POST not specified in request'
+ if soapAction:
+ soapAction = soapAction.strip('\'"')
+ post = post.strip('\'"')
+ try:
+ ct = self.headers['content-type']
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, self.rfile)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve, readerclass=DomletteReader)
+ else:
+ length = int(self.headers['content-length'])
+ ps = ParsedSoap(self.rfile.read(length), readerclass=DomletteReader)
+ except ParseException, e:
+ self.send_fault(FaultFromZSIException(e))
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ self.send_fault(FaultFromException(e, 1, sys.exc_info()[2]))
+ else:
+ # Keep track of calls
+ thread_id = thread.get_ident()
+ _contexts[thread_id] = SOAPContext(self.server, xml, ps,
+ self.connection,
+ self.headers, soapAction)
+
+ try:
+ _Dispatch(ps, self.server, self.send_xml, self.send_fault,
+ post=post, action=soapAction)
+ except Exception, e:
+ self.send_fault(FaultFromException(e, 0, sys.exc_info()[2]))
+
+ # Clean up after the call
+ if _contexts.has_key(thread_id):
+ del _contexts[thread_id]
+
+
+class SOAPRequestHandler(BaseSOAPRequestHandler):
+ '''SOAP handler.
+ '''
+ def do_POST(self):
+ '''The POST command.
+ action -- SOAPAction(HTTP header) or wsa:Action(SOAP:Header)
+ '''
+ soapAction = self.headers.getheader('SOAPAction')
+ post = self.path
+ if not post:
+ raise PostNotSpecified, 'HTTP POST not specified in request'
+ if soapAction:
+ soapAction = soapAction.strip('\'"')
+ post = post.strip('\'"')
+ try:
+ ct = self.headers['content-type']
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, self.rfile)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ length = int(self.headers['content-length'])
+ xml = self.rfile.read(length)
+ ps = ParsedSoap(xml)
+ except ParseException, e:
+ self.send_fault(FaultFromZSIException(e))
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ self.send_fault(FaultFromException(e, 1, sys.exc_info()[2]))
+ else:
+ # Keep track of calls
+ thread_id = thread.get_ident()
+ _contexts[thread_id] = SOAPContext(self.server, xml, ps,
+ self.connection,
+ self.headers, soapAction)
+
+ try:
+ _Dispatch(ps, self.server, self.send_xml, self.send_fault,
+ post=post, action=soapAction)
+ except Exception, e:
+ self.send_fault(FaultFromException(e, 0, sys.exc_info()[2]))
+
+ # Clean up after the call
+ if _contexts.has_key(thread_id):
+ del _contexts[thread_id]
+
+ def do_GET(self):
+ '''The GET command.
+ '''
+ if self.path.lower().endswith("?wsdl"):
+ service_path = self.path[:-5]
+ service = self.server.getNode(service_path)
+ if hasattr(service, "_wsdl"):
+ wsdl = service._wsdl
+ # update the soap:location tag in the wsdl to the actual server
+ # location
+ # - default to 'http' as protocol, or use server-specified protocol
+ proto = 'http'
+ if hasattr(self.server,'proto'):
+ proto = self.server.proto
+ serviceUrl = '%s://%s:%d%s' % (proto,
+ self.server.server_name,
+ self.server.server_port,
+ service_path)
+ soapAddress = '<soap:address location="%s"/>' % serviceUrl
+ wsdlre = re.compile('\<soap:address[^\>]*>',re.IGNORECASE)
+ wsdl = re.sub(wsdlre,soapAddress,wsdl)
+ self.send_xml(wsdl)
+ else:
+ self.send_error(404, "WSDL not available for that service [%s]." % self.path)
+ else:
+ self.send_error(404, "Service not found [%s]." % self.path)
+
+class ServiceContainer(HTTPServer):
+ '''HTTPServer that stores service instances according
+ to POST values. An action value is instance specific,
+ and specifies an operation (function) of an instance.
+ '''
+ class NodeTree:
+ '''Simple dictionary implementation of a node tree
+ '''
+ def __init__(self):
+ self.__dict = {}
+
+ def __str__(self):
+ return str(self.__dict)
+
+ def listNodes(self):
+ print self.__dict.keys()
+
+ def getNode(self, url):
+ path = urlparse.urlsplit(url)[2]
+ if path.startswith("/"):
+ path = path[1:]
+
+ if self.__dict.has_key(path):
+ return self.__dict[path]
+ else:
+ raise NoSuchService, 'No service(%s) in ServiceContainer' %path
+
+ def setNode(self, service, url):
+ path = urlparse.urlsplit(url)[2]
+ if path.startswith("/"):
+ path = path[1:]
+
+ if not isinstance(service, ServiceSOAPBinding):
+ raise TypeError, 'A Service must implement class ServiceSOAPBinding'
+ if self.__dict.has_key(path):
+ raise ServiceAlreadyPresent, 'Service(%s) already in ServiceContainer' % path
+ else:
+ self.__dict[path] = service
+
+ def removeNode(self, url):
+ path = urlparse.urlsplit(url)[2]
+ if path.startswith("/"):
+ path = path[1:]
+
+ if self.__dict.has_key(path):
+ node = self.__dict[path]
+ del self.__dict[path]
+ return node
+ else:
+ raise NoSuchService, 'No service(%s) in ServiceContainer' %path
+
+ def __init__(self, server_address, services=[], RequestHandlerClass=SOAPRequestHandler):
+ '''server_address --
+ RequestHandlerClass --
+ '''
+ HTTPServer.__init__(self, server_address, RequestHandlerClass)
+ self._nodes = self.NodeTree()
+ map(lambda s: self.setNode(s), services)
+
+ def __str__(self):
+ return '%s(%s) nodes( %s )' %(self.__class__, _get_idstr(self), str(self._nodes))
+
+ def __call__(self, ps, post, action, address=None):
+ '''ps -- ParsedSoap representing the request
+ post -- HTTP POST --> instance
+ action -- Soap Action header --> method
+ address -- Address instance representing WS-Address
+ '''
+ method = self.getCallBack(ps, post, action)
+ if isinstance(method.im_self, SimpleWSResource):
+ return method(ps, address)
+ return method(ps)
+
+
+ def setNode(self, service, url=None):
+ if url is None:
+ url = service.getPost()
+ self._nodes.setNode(service, url)
+
+ def getNode(self, url):
+ return self._nodes.getNode(url)
+
+ def removeNode(self, url):
+ self._nodes.removeNode(url)
+
+
+class SimpleWSResource(ServiceSOAPBinding):
+
+ def getNode(self, post):
+ '''post -- POST HTTP value
+ '''
+ return self._nodes.getNode(post)
+
+ def setNode(self, service, post):
+ '''service -- service instance
+ post -- POST HTTP value
+ '''
+ self._nodes.setNode(service, post)
+
+ def getCallBack(self, ps, post, action):
+ '''post -- POST HTTP value
+ action -- SOAP Action value
+ '''
+ node = self.getNode(post)
+ if node is None:
+ raise NoSuchFunction
+ if node.authorize(None, post, action):
+ return node.getOperation(ps, action)
+ else:
+ raise NotAuthorized, "Authorization failed for method %s" % action
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/ServiceProxy.py b/ZSI/zsi/ZSI/ServiceProxy.py
new file mode 100755
index 0000000..44b89be
--- /dev/null
+++ b/ZSI/zsi/ZSI/ServiceProxy.py
@@ -0,0 +1,305 @@
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+
+from ZSI.wstools.Utility import SplitQName
+from ZSI.wstools.Namespaces import WSDL
+from ZSI import *
+from ZSI.client import *
+from ZSI.TC import Any
+from ZSI.typeinterpreter import BaseTypeInterpreter
+import wstools
+from wstools.Utility import DOM
+from urlparse import urlparse
+import weakref
+
+class ServiceProxy:
+ """A ServiceProxy provides a convenient way to call a remote web
+ service that is described with WSDL. The proxy exposes methods
+ that reflect the methods of the remote web service."""
+
+ def __init__(self, wsdl, service=None, port=None, tracefile=None,
+ nsdict=None, transdict=None):
+ """
+ Parameters:
+ wsdl -- WSDLTools.WSDL instance or URL of WSDL.
+ service -- service name or index
+ port -- port name or index
+ tracefile --
+ nsdict -- key prefix to namespace mappings for serialization
+ in SOAP Envelope.
+ transdict -- arguments to pass into HTTPConnection constructor.
+ """
+ self._tracefile = tracefile
+ self._nsdict = nsdict or {}
+ self._transdict = transdict
+ self._wsdl = wsdl
+ if isinstance(wsdl, basestring) is True:
+ self._wsdl = wstools.WSDLTools.WSDLReader().loadFromURL(wsdl)
+
+ assert isinstance(self._wsdl, wstools.WSDLTools.WSDL), 'expecting a WSDL instance'
+ self._service = self._wsdl.services[service or 0]
+ self.__doc__ = self._service.documentation
+ self._port = self._service.ports[port or 0]
+ self._name = self._service.name
+ self._methods = {}
+
+ binding = self._port.getBinding()
+ portType = binding.getPortType()
+ for port in self._service.ports:
+ for item in port.getPortType().operations:
+ callinfo = wstools.WSDLTools.callInfoFromWSDL(port, item.name)
+ method = MethodProxy(self, callinfo)
+ setattr(self, item.name, method)
+ self._methods.setdefault(item.name, []).append(method)
+
+ def _call(self, name, *args, **kwargs):
+ """Call the named remote web service method."""
+ if len(args) and len(kwargs):
+ raise TypeError(
+ 'Use positional or keyword argument only.'
+ )
+
+ callinfo = getattr(self, name).callinfo
+
+ # go through the list of defined methods, and look for the one with
+ # the same number of arguments as what was passed. this is a weak
+ # check that should probably be improved in the future to check the
+ # types of the arguments to allow for polymorphism
+ for method in self._methods[name]:
+ if len(method.callinfo.inparams) == len(kwargs):
+ callinfo = method.callinfo
+
+ soapAction = callinfo.soapAction
+ url = callinfo.location
+ (protocol, host, uri, query, fragment, identifier) = urlparse(url)
+ port = None
+ if host.find(':') >= 0:
+ host, port = host.split(':')
+
+ if protocol == 'http':
+ transport = httplib.HTTPConnection
+ elif protocol == 'https':
+ transport = httplib.HTTPSConnection
+ else:
+ raise RuntimeError, 'Unknown protocol %s' %protocol
+
+ binding = Binding(host=host, tracefile=self._tracefile,
+ transport=transport,transdict=self._transdict,
+ port=port, url=url, nsdict=self._nsdict,
+ soapaction=soapAction,)
+
+ request, response = self._getTypeCodes(callinfo)
+ if len(kwargs): args = kwargs
+ if request is None:
+ request = Any(oname=name)
+ binding.Send(url=url, opname=None, obj=args,
+ nsdict=self._nsdict, soapaction=soapAction, requesttypecode=request)
+ return binding.Receive(replytype=response)
+
+
+ def _getTypeCodes(self, callinfo):
+ """Returns typecodes representing input and output messages, if request and/or
+ response fails to be generated return None for either or both.
+
+ callinfo -- WSDLTools.SOAPCallInfo instance describing an operation.
+ """
+ prefix = None
+ self._resetPrefixDict()
+ if callinfo.use == 'encoded':
+ prefix = self._getPrefix(callinfo.namespace)
+ try:
+ requestTC = self._getTypeCode(parameters=callinfo.getInParameters(), literal=(callinfo.use=='literal'))
+ except EvaluateException, ex:
+ print "DEBUG: Request Failed to generate --", ex
+ requestTC = None
+
+ self._resetPrefixDict()
+ try:
+ replyTC = self._getTypeCode(parameters=callinfo.getOutParameters(), literal=(callinfo.use=='literal'))
+ except EvaluateException, ex:
+ print "DEBUG: Response Failed to generate --", ex
+ replyTC = None
+
+ request = response = None
+ if callinfo.style == 'rpc':
+ if requestTC: request = TC.Struct(pyclass=None, ofwhat=requestTC, pname=callinfo.methodName)
+ if replyTC: response = TC.Struct(pyclass=None, ofwhat=replyTC, pname='%sResponse' %callinfo.methodName)
+ else:
+ if requestTC: request = requestTC[0]
+ if replyTC: response = replyTC[0]
+
+ #THIS IS FOR RPC/ENCODED, DOC/ENCODED Wrapper
+ if request and prefix and callinfo.use == 'encoded':
+ request.oname = '%(prefix)s:%(name)s xmlns:%(prefix)s="%(namespaceURI)s"' \
+ %{'prefix':prefix, 'name':request.aname, 'namespaceURI':callinfo.namespace}
+
+ return request, response
+
+ def _getTypeCode(self, parameters, literal=False):
+ """Returns typecodes representing a parameter set
+ parameters -- list of WSDLTools.ParameterInfo instances representing
+ the parts of a WSDL Message.
+ """
+ ofwhat = []
+ for part in parameters:
+ namespaceURI,localName = part.type
+
+ if part.element_type:
+ #global element
+ element = self._wsdl.types[namespaceURI].elements[localName]
+ tc = self._getElement(element, literal=literal, local=False, namespaceURI=namespaceURI)
+ else:
+ #local element
+ name = part.name
+ typeClass = self._getTypeClass(namespaceURI, localName)
+ if not typeClass:
+ tp = self._wsdl.types[namespaceURI].types[localName]
+ tc = self._getType(tp, name, literal, local=True, namespaceURI=namespaceURI)
+ else:
+ tc = typeClass(name)
+ ofwhat.append(tc)
+ return ofwhat
+
+ def _globalElement(self, typeCode, namespaceURI, literal):
+ """namespaces typecodes representing global elements with
+ literal encoding.
+ typeCode -- typecode representing an element.
+ namespaceURI -- namespace
+ literal -- True/False
+ """
+ if literal:
+ typeCode.oname = '%(prefix)s:%(name)s xmlns:%(prefix)s="%(namespaceURI)s"' \
+ %{'prefix':self._getPrefix(namespaceURI), 'name':typeCode.oname, 'namespaceURI':namespaceURI}
+
+ def _getPrefix(self, namespaceURI):
+ """Retrieves a prefix/namespace mapping.
+ namespaceURI -- namespace
+ """
+ prefixDict = self._getPrefixDict()
+ if prefixDict.has_key(namespaceURI):
+ prefix = prefixDict[namespaceURI]
+ else:
+ prefix = 'ns1'
+ while prefix in prefixDict.values():
+ prefix = 'ns%d' %int(prefix[-1]) + 1
+ prefixDict[namespaceURI] = prefix
+ return prefix
+
+ def _getPrefixDict(self):
+ """Used to hide the actual prefix dictionary.
+ """
+ if not hasattr(self, '_prefixDict'):
+ self.__prefixDict = {}
+ return self.__prefixDict
+
+ def _resetPrefixDict(self):
+ """Clears the prefix dictionary, this needs to be done
+ before creating a new typecode for a message
+ (ie. before, and after creating a new message typecode)
+ """
+ self._getPrefixDict().clear()
+
+ def _getElement(self, element, literal=False, local=False, namespaceURI=None):
+ """Returns a typecode instance representing the passed in element.
+ element -- XMLSchema.ElementDeclaration instance
+ literal -- literal encoding?
+ local -- is locally defined?
+ namespaceURI -- namespace
+ """
+ if not element.isElement():
+ raise TypeError, 'Expecting an ElementDeclaration'
+
+ tc = None
+ elementName = element.getAttribute('name')
+ tp = element.getTypeDefinition('type')
+
+ typeObj = None
+ if not (tp or element.content):
+ nsuriType,localName = element.getAttribute('type')
+ typeClass = self._getTypeClass(nsuriType,localName)
+
+ typeObj = typeClass(elementName)
+ elif not tp:
+ tp = element.content
+
+ if not typeObj:
+ typeObj = self._getType(tp, elementName, literal, local, namespaceURI)
+
+ minOccurs = int(element.getAttribute('minOccurs'))
+ typeObj.optional = not minOccurs
+ typeObj.minOccurs = minOccurs
+
+ maxOccurs = element.getAttribute('maxOccurs')
+ typeObj.repeatable = (maxOccurs == 'unbounded') or (int(maxOccurs) > 1)
+
+ return typeObj
+
+ def _getType(self, tp, name, literal, local, namespaceURI):
+ """Returns a typecode instance representing the passed in type and name.
+ tp -- XMLSchema.TypeDefinition instance
+ name -- element name
+ literal -- literal encoding?
+ local -- is locally defined?
+ namespaceURI -- namespace
+ """
+ ofwhat = []
+ if not (tp.isDefinition() and tp.isComplex()):
+ raise EvaluateException, 'only supporting complexType definition'
+ elif tp.content.isComplex():
+ if hasattr(tp.content, 'derivation') and tp.content.derivation.isRestriction():
+ derived = tp.content.derivation
+ typeClass = self._getTypeClass(*derived.getAttribute('base'))
+ if typeClass == TC.Array:
+ attrs = derived.attr_content[0].attributes[WSDL.BASE]
+ prefix, localName = SplitQName(attrs['arrayType'])
+ nsuri = derived.attr_content[0].getXMLNS(prefix=prefix)
+ localName = localName.split('[')[0]
+ simpleTypeClass = self._getTypeClass(namespaceURI=nsuri, localName=localName)
+ if simpleTypeClass:
+ ofwhat = simpleTypeClass()
+ else:
+ tp = self._wsdl.types[nsuri].types[localName]
+ ofwhat = self._getType(tp=tp, name=None, literal=literal, local=True, namespaceURI=nsuri)
+ else:
+ raise EvaluateException, 'only support soapenc:Array restrictions'
+ return typeClass(atype=name, ofwhat=ofwhat, pname=name, childNames='item')
+ else:
+ raise EvaluateException, 'complexContent only supported for soapenc:Array derivations'
+ elif tp.content.isModelGroup():
+ modelGroup = tp.content
+ for item in modelGroup.content:
+ ofwhat.append(self._getElement(item, literal=literal, local=True))
+
+ tc = TC.Struct(pyclass=None, ofwhat=ofwhat, pname=name)
+ if not local:
+ self._globalElement(tc, namespaceURI=namespaceURI, literal=literal)
+ return tc
+
+ raise EvaluateException, 'only supporting complexType w/ model group, or soapenc:Array restriction'
+
+ def _getTypeClass(self, namespaceURI, localName):
+ """Returns a typecode class representing the type we are looking for.
+ localName -- name of the type we are looking for.
+ namespaceURI -- defining XMLSchema targetNamespace.
+ """
+ bti = BaseTypeInterpreter()
+ simpleTypeClass = bti.get_typeclass(localName, namespaceURI)
+ return simpleTypeClass
+
+
+class MethodProxy:
+ """ """
+ def __init__(self, parent, callinfo):
+ self.__name__ = callinfo.methodName
+ self.__doc__ = callinfo.documentation
+ self.callinfo = callinfo
+ self.parent = weakref.ref(parent)
+
+ def __call__(self, *args, **kwargs):
+ return self.parent()._call(self.__name__, *args, **kwargs)
diff --git a/ZSI/zsi/ZSI/TC.py b/ZSI/zsi/ZSI/TC.py
new file mode 100644
index 0000000..cb93931
--- /dev/null
+++ b/ZSI/zsi/ZSI/TC.py
@@ -0,0 +1,1807 @@
+#! /usr/bin/env python
+# $Header$
+'''General typecodes.
+'''
+
+from ZSI import _copyright, _children, _child_elements, \
+ _floattypes, _stringtypes, _seqtypes, _find_attr, _find_attrNS, _find_attrNodeNS, \
+ _find_arraytype, _find_default_namespace, _find_href, _find_encstyle, \
+ _resolve_prefix, _find_xsi_attr, _find_type, \
+ _find_xmlns_prefix, _get_element_nsuri_name, _get_idstr, \
+ _Node, EvaluateException, \
+ _valid_encoding, ParseException
+
+from ZSI.wstools.Namespaces import SCHEMA, SOAP
+from ZSI.wstools.Utility import SplitQName
+from ZSI.wstools.c14n import Canonicalize
+from ZSI.wstools.logging import getLogger as _GetLogger
+
+import re, types, time, copy
+
+from base64 import decodestring as b64decode, encodestring as b64encode
+from urllib import unquote as urldecode, quote as urlencode
+from binascii import unhexlify as hexdecode, hexlify as hexencode
+
+
+_is_xsd_or_soap_ns = lambda ns: ns in [
+ SCHEMA.XSD3, SOAP.ENC, SCHEMA.XSD1, SCHEMA.XSD2, ]
+_find_nil = lambda E: _find_xsi_attr(E, "null") or _find_xsi_attr(E, "nil")
+
+def _get_xsitype(pyclass):
+ '''returns the xsi:type as a tuple, coupled with ZSI.schema
+ '''
+ if hasattr(pyclass,'type') and type(pyclass.type) in _seqtypes:
+ return pyclass.type
+ elif hasattr(pyclass,'type') and hasattr(pyclass, 'schema'):
+ return (pyclass.schema, pyclass.type)
+
+ return (None,None)
+
+
+# value returned when xsi:nil="true"
+Nilled = None
+UNBOUNDED = 'unbounded'
+
+
+class TypeCode:
+ '''The parent class for all parseable SOAP types.
+ Class data:
+ typechecks -- do init-time type checking if non-zero
+ Class data subclasses may define:
+ tag -- global element declaration
+ type -- global type definition
+ parselist -- list of valid SOAP types for this class, as
+ (uri,name) tuples, where a uri of None means "all the XML
+ Schema namespaces"
+ errorlist -- parselist in a human-readable form; will be
+ generated if/when needed
+ seriallist -- list of Python types or user-defined classes
+ that this typecode can serialize.
+ logger -- logger instance for this class.
+ '''
+ tag = None
+ type = (None,None)
+ typechecks = True
+ attribute_typecode_dict = None
+ logger = _GetLogger('ZSI.TC.TypeCode')
+
+ def __init__(self, pname=None, aname=None, minOccurs=1,
+ maxOccurs=1, nillable=False, typed=True, unique=True,
+ pyclass=None, attrs_aname='_attrs', **kw):
+ '''Baseclass initialization.
+ Instance data (and usually keyword arg)
+ pname -- the parameter name (localname).
+ nspname -- the namespace for the parameter;
+ None to ignore the namespace
+ typed -- output xsi:type attribute
+ unique -- data item is not aliased (no href/id needed)
+ minOccurs -- min occurances
+ maxOccurs -- max occurances
+ nillable -- is item nillable?
+ attrs_aname -- This is variable name to dictionary of attributes
+ encoded -- encoded namespaceURI (specify if use is encoded)
+ '''
+ if type(pname) in _seqtypes:
+ self.nspname, self.pname = pname
+ else:
+ self.nspname, self.pname = None, pname
+
+ if self.pname:
+ self.pname = str(self.pname).split(':')[-1]
+
+ self.aname = aname or self.pname
+ self.minOccurs = minOccurs
+ self.maxOccurs = maxOccurs
+ self.nillable = nillable
+ self.typed = typed
+ self.unique = unique
+ self.attrs_aname = attrs_aname
+ self.pyclass = pyclass
+
+ # Need this stuff for rpc/encoded.
+ encoded = kw.get('encoded')
+ if encoded is not None:
+ self.nspname = kw['encoded']
+
+ def parse(self, elt, ps):
+ '''
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ raise EvaluateException("Unimplemented evaluation", ps.Backtrace(elt))
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ '''
+ Parameters:
+ elt -- the current DOMWrapper element
+ sw -- soapWriter object
+ pyobj -- python object to serialize
+
+ '''
+ raise EvaluateException("Unimplemented evaluation", sw.Backtrace(elt))
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ Parameters:
+ text -- text content
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ raise EvaluateException("Unimplemented evaluation", ps.Backtrace(elt))
+
+ def serialize_as_nil(self, elt):
+ '''
+ Parameters:
+ elt -- the current DOMWrapper element
+ '''
+ elt.setAttributeNS(SCHEMA.XSI3, 'nil', '1')
+
+ def SimpleHREF(self, elt, ps, tag):
+ '''Simple HREF for non-string and non-struct and non-array.
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ tag --
+ '''
+ if len(_children(elt)): return elt
+ href = _find_href(elt)
+ if not href:
+ if self.minOccurs is 0: return None
+ raise EvaluateException('Required' + tag + ' missing',
+ ps.Backtrace(elt))
+ return ps.FindLocalHREF(href, elt, 0)
+
+ def get_parse_and_errorlist(self):
+ """Get the parselist and human-readable version, errorlist is returned,
+ because it is used in error messages.
+ """
+ d = self.__class__.__dict__
+ parselist = d.get('parselist')
+ errorlist = d.get('errorlist')
+ if parselist and not errorlist:
+ errorlist = []
+ for t in parselist:
+ if t[1] not in errorlist: errorlist.append(t[1])
+ errorlist = ' or '.join(errorlist)
+ d['errorlist'] = errorlist
+ return (parselist, errorlist)
+
+ def checkname(self, elt, ps):
+ '''See if the name and type of the "elt" element is what we're
+ looking for. Return the element's type.
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+
+ parselist,errorlist = self.get_parse_and_errorlist()
+ ns, name = _get_element_nsuri_name(elt)
+ if ns == SOAP.ENC:
+ # Element is in SOAP namespace, so the name is a type.
+ if parselist and \
+ (None, name) not in parselist and (ns, name) not in parselist:
+ raise EvaluateException(
+ 'Element mismatch (got %s wanted %s) (SOAP encoding namespace)' % \
+ (name, errorlist), ps.Backtrace(elt))
+ return (ns, name)
+
+ # Not a type, check name matches.
+ if self.nspname and ns != self.nspname:
+ raise EvaluateException('Element NS mismatch (got %s wanted %s)' % \
+ (ns, self.nspname), ps.Backtrace(elt))
+
+ if self.pname and name != self.pname:
+ raise EvaluateException('Element Name mismatch (got %s wanted %s)' % \
+ (name, self.pname), ps.Backtrace(elt))
+ return self.checktype(elt, ps)
+
+ def checktype(self, elt, ps):
+ '''See if the type of the "elt" element is what we're looking for.
+ Return the element's type.
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ typeName = _find_type(elt)
+ if typeName is None or typeName == "":
+ return (None,None)
+
+ # Parse the QNAME.
+ prefix,typeName = SplitQName(typeName)
+ uri = ps.GetElementNSdict(elt).get(prefix)
+ if uri is None:
+ raise EvaluateException('Malformed type attribute (bad NS)',
+ ps.Backtrace(elt))
+
+ #typeName = list[1]
+ parselist,errorlist = self.get_parse_and_errorlist()
+ if not parselist or \
+ (uri,typeName) in parselist or \
+ (_is_xsd_or_soap_ns(uri) and (None,typeName) in parselist):
+ return (uri,typeName)
+ raise EvaluateException(
+ 'Type mismatch (%s namespace) (got %s wanted %s)' % \
+ (uri, typeName, errorlist), ps.Backtrace(elt))
+
+ def name_match(self, elt):
+ '''Simple boolean test to see if we match the element name.
+ Parameters:
+ elt -- the DOM element being parsed
+ '''
+ return self.pname == elt.localName and \
+ self.nspname in [None, elt.namespaceURI]
+
+ def nilled(self, elt, ps):
+ '''Is the element NIL, and is that okay?
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ if _find_nil(elt) not in [ "true", "1"]: return False
+ if self.nillable is False:
+ raise EvaluateException('Non-nillable element is NIL',
+ ps.Backtrace(elt))
+ return True
+
+ def simple_value(self, elt, ps, mixed=False):
+ '''Get the value of the simple content of this element.
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ mixed -- ignore element content, optional text node
+ '''
+ if not _valid_encoding(elt):
+ raise EvaluateException('Invalid encoding', ps.Backtrace(elt))
+ c = _children(elt)
+ if mixed is False:
+ if len(c) == 0:
+ raise EvaluateException('Value missing', ps.Backtrace(elt))
+ for c_elt in c:
+ if c_elt.nodeType == _Node.ELEMENT_NODE:
+ raise EvaluateException('Sub-elements in value',
+ ps.Backtrace(c_elt))
+
+ # It *seems* to be consensus that ignoring comments and
+ # concatenating the text nodes is the right thing to do.
+ return ''.join([E.nodeValue for E in c
+ if E.nodeType
+ in [ _Node.TEXT_NODE, _Node.CDATA_SECTION_NODE ]])
+
+ def parse_attributes(self, elt, ps):
+ '''find all attributes specified in the attribute_typecode_dict in
+ current element tag, if an attribute is found set it in the
+ self.attributes dictionary. Default to putting in String.
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ if self.attribute_typecode_dict is None:
+ return
+
+ attributes = {}
+ for attr,what in self.attribute_typecode_dict.items():
+ namespaceURI,localName = None,attr
+ if type(attr) in _seqtypes:
+ namespaceURI,localName = attr
+ value = _find_attrNodeNS(elt, namespaceURI, localName)
+ self.logger.debug("Parsed Attribute (%s,%s) -- %s",
+ namespaceURI, localName, value)
+
+ # For Now just set it w/o any type interpretation.
+ if value is None: continue
+ attributes[attr] = what.text_to_data(value, elt, ps)
+
+ return attributes
+
+ def set_attributes(self, el, pyobj):
+ '''Instance data attributes contains a dictionary
+ of keys (namespaceURI,localName) and attribute values.
+ These values can be self-describing (typecode), or use
+ attribute_typecode_dict to determine serialization.
+ Paramters:
+ el -- MessageInterface representing the element
+ pyobj --
+ '''
+ if not hasattr(pyobj, self.attrs_aname):
+ return
+
+ if not isinstance(getattr(pyobj, self.attrs_aname), dict):
+ raise TypeError,\
+ 'pyobj.%s must be a dictionary of names and values'\
+ % self.attrs_aname
+
+ for attr, value in getattr(pyobj, self.attrs_aname).items():
+ namespaceURI,localName = None, attr
+ if type(attr) in _seqtypes:
+ namespaceURI, localName = attr
+
+ what = None
+ if getattr(self, 'attribute_typecode_dict', None) is not None:
+ what = self.attribute_typecode_dict.get(attr)
+ if what is None and namespaceURI is None:
+ what = self.attribute_typecode_dict.get(localName)
+
+ # allow derived type
+ if hasattr(value, 'typecode') and not isinstance(what, AnyType):
+ if what is not None and not isinstance(value.typecode, what):
+ raise EvaluateException, \
+ 'self-describing attribute must subclass %s'\
+ %what.__class__
+
+ what = value.typecode
+
+ self.logger.debug("attribute create -- %s", value)
+ if isinstance(what, QName):
+ what.set_prefix(el, value)
+
+ #format the data
+ if what is None:
+ value = str(value)
+ else:
+ value = what.get_formatted_content(value)
+
+ el.setAttributeNS(namespaceURI, localName, value)
+
+ def set_attribute_xsi_type(self, el, **kw):
+ '''if typed, set the xsi:type attribute
+ Paramters:
+ el -- MessageInterface representing the element
+ '''
+ if kw.get('typed', self.typed):
+ namespaceURI,typeName = kw.get('type', _get_xsitype(self))
+ if namespaceURI and typeName:
+ self.logger.debug("attribute: (%s, %s)", namespaceURI, typeName)
+ el.setAttributeType(namespaceURI, typeName)
+
+ def set_attribute_href(self, el, objid):
+ '''set href attribute
+ Paramters:
+ el -- MessageInterface representing the element
+ objid -- ID type, unique id
+ '''
+ el.setAttributeNS(None, 'href', "#%s" %objid)
+
+ def set_attribute_id(self, el, objid):
+ '''set id attribute
+ Paramters:
+ el -- MessageInterface representing the element
+ objid -- ID type, unique id
+ '''
+ if self.unique is False:
+ el.setAttributeNS(None, 'id', "%s" %objid)
+
+ def get_name(self, name, objid):
+ '''
+ Paramters:
+ name -- element tag
+ objid -- ID type, unique id
+ '''
+ if type(name) is tuple:
+ return name
+
+ ns = self.nspname
+ n = name or self.pname or ('E' + objid)
+ return ns,n
+
+ def has_attributes(self):
+ '''Return True if Attributes are declared outside
+ the scope of SOAP ('root', 'id', 'href'), and some
+ attributes automatically handled (xmlns, xsi:type).
+ '''
+ if self.attribute_typecode_dict is None: return False
+ return len(self.attribute_typecode_dict) > 0
+
+
+class SimpleType(TypeCode):
+ '''SimpleType -- consist exclusively of a tag, attributes, and a value
+ class attributes:
+ empty_content -- value representing an empty element.
+ '''
+ empty_content = None
+ logger = _GetLogger('ZSI.TC.SimpleType')
+
+ def parse(self, elt, ps):
+ self.checkname(elt, ps)
+ if len(_children(elt)) == 0:
+ href = _find_href(elt)
+ if not href:
+ if self.nilled(elt, ps) is False:
+ # No content, no HREF, not NIL: empty string
+ return self.text_to_data(self.empty_content, elt, ps)
+
+ # No content, no HREF, and is NIL...
+ if self.nillable is True:
+ return Nilled
+ raise EvaluateException('Requiredstring missing',
+ ps.Backtrace(elt))
+
+ if href[0] != '#':
+ return ps.ResolveHREF(href, self)
+
+ elt = ps.FindLocalHREF(href, elt)
+ self.checktype(elt, ps)
+ if self.nilled(elt, ps): return Nilled
+ if len(_children(elt)) == 0:
+ v = self.empty_content
+ else:
+ v = self.simple_value(elt, ps)
+ else:
+ v = self.simple_value(elt, ps)
+
+ pyobj = self.text_to_data(v, elt, ps)
+
+ # parse all attributes contained in attribute_typecode_dict
+ # (user-defined attributes), the values (if not None) will
+ # be keyed in self.attributes dictionary.
+ if self.attribute_typecode_dict is not None:
+ attributes = self.parse_attributes(elt, ps)
+ if attributes:
+ setattr(pyobj, self.attrs_aname, attributes)
+
+ return pyobj
+
+ def get_formatted_content(self, pyobj):
+ raise NotImplementedError, 'method get_formatted_content is not implemented'
+
+ def serialize_text_node(self, elt, sw, pyobj):
+ '''Serialize without an element node.
+ '''
+ textNode = None
+ if pyobj is not None:
+ text = self.get_formatted_content(pyobj)
+ if type(text) not in _stringtypes:
+ raise TypeError, 'pyobj must be a formatted string'
+
+ textNode = elt.createAppendTextNode(text)
+
+ return textNode
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ '''Handles the start and end tags, and attributes. callout
+ to get_formatted_content to get the textNode value.
+ Parameters:
+ elt -- ElementProxy/DOM element
+ sw -- SoapWriter instance
+ pyobj -- processed content
+
+ KeyWord Parameters:
+ name -- substitute name, (nspname,name) or name
+ orig --
+
+ '''
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+
+ # nillable
+ el = elt.createAppendElement(ns, n)
+ if self.nillable is True and pyobj is Nilled:
+ self.serialize_as_nil(el)
+ return None
+
+ # other attributes
+ self.set_attributes(el, pyobj)
+
+ # soap href attribute
+ unique = self.unique or kw.get('unique', False)
+ if unique is False and sw.Known(orig or pyobj):
+ self.set_attribute_href(el, objid)
+ return None
+
+ # xsi:type attribute
+ if kw.get('typed', self.typed) is True:
+ self.set_attribute_xsi_type(el, **kw)
+
+ # soap id attribute
+ if self.unique is False:
+ self.set_attribute_id(el, objid)
+
+ #Content, <empty tag/>c
+ self.serialize_text_node(el, sw, pyobj)
+
+ return el
+
+
+class Any(TypeCode):
+ '''When the type isn't defined in the schema, but must be specified
+ in the incoming operation.
+ parsemap -- a type to class mapping (updated by descendants), for
+ parsing
+ serialmap -- same, for (outgoing) serialization
+ '''
+ logger = _GetLogger('ZSI.TC.Any')
+ parsemap, serialmap = {}, {}
+
+ def __init__(self, pname=None, aslist=False, minOccurs=0, **kw):
+ TypeCode.__init__(self, pname, minOccurs=minOccurs, **kw)
+ self.aslist = aslist
+ self.kwargs = {'aslist':aslist}
+ self.kwargs.update(kw)
+ self.unique = False
+
+ # input arg v should be a list of tuples (name, value).
+ def listify(self, v):
+ if self.aslist: return [ k for j,k in v ]
+ return dict(v)
+
+ def parse_into_dict_or_list(self, elt, ps):
+ c = _child_elements(elt)
+ count = len(c)
+ v = []
+ if count == 0:
+ href = _find_href(elt)
+ if not href: return v
+ elt = ps.FindLocalHREF(href, elt)
+ self.checktype(elt, ps)
+ c = _child_elements(elt)
+ count = len(c)
+ if count == 0: return self.listify(v)
+ if self.nilled(elt, ps): return Nilled
+
+ for c_elt in c:
+ v.append((str(c_elt.localName), self.__class__(**self.kwargs).parse(c_elt, ps)))
+
+ return self.listify(v)
+
+ def parse(self, elt, ps):
+ (ns,type) = self.checkname(elt, ps)
+ if not type and self.nilled(elt, ps): return Nilled
+ if len(_children(elt)) == 0:
+ href = _find_href(elt)
+ if not href:
+ if self.minOccurs < 1:
+ if _is_xsd_or_soap_ns(ns):
+ parser = Any.parsemap.get((None,type))
+ if parser: return parser.parse(elt, ps)
+ if ((ns,type) == (SOAP.ENC,'Array') or
+ (_find_arraytype(elt) or '').endswith('[0]')):
+ return []
+ return None
+ raise EvaluateException('Required Any missing',
+ ps.Backtrace(elt))
+ elt = ps.FindLocalHREF(href, elt)
+ (ns,type) = self.checktype(elt, ps)
+ if not type and elt.namespaceURI == SOAP.ENC:
+ ns,type = SOAP.ENC, elt.localName
+ if not type or (ns,type) == (SOAP.ENC,'Array'):
+ if self.aslist or _find_arraytype(elt):
+ return [ self.__class__(**self.kwargs).parse(e, ps)
+ for e in _child_elements(elt) ]
+ if len(_child_elements(elt)) == 0:
+ raise EvaluateException("Any cannot parse untyped element",
+ ps.Backtrace(elt))
+ return self.parse_into_dict_or_list(elt, ps)
+ parser = Any.parsemap.get((ns,type))
+ if not parser and _is_xsd_or_soap_ns(ns):
+ parser = Any.parsemap.get((None,type))
+ if not parser:
+ raise EvaluateException('''Any can't parse element''',
+ ps.Backtrace(elt))
+ return parser.parse(elt, ps)
+
+ def get_formatted_content(self, pyobj):
+ tc = type(pyobj)
+ if tc == types.InstanceType:
+ tc = pyobj.__class__
+ if hasattr(pyobj, 'typecode'):
+ #serializer = pyobj.typecode.serialmap.get(tc)
+ serializer = pyobj.typecode
+ else:
+ serializer = Any.serialmap.get(tc)
+ if not serializer:
+ tc = (types.ClassType, pyobj.__class__.__name__)
+ serializer = Any.serialmap.get(tc)
+ else:
+ serializer = Any.serialmap.get(tc)
+ if not serializer and isinstance(pyobj, time.struct_time):
+ from ZSI.TCtimes import gDateTime
+ serializer = gDateTime()
+ if serializer:
+ return serializer.get_formatted_content(pyobj)
+ raise EvaluateException, 'Failed to find serializer for pyobj %s' %pyobj
+
+ def serialize(self, elt, sw, pyobj, name=None, **kw):
+ if hasattr(pyobj, 'typecode') and pyobj.typecode is not self:
+ pyobj.typecode.serialize(elt, sw, pyobj, **kw)
+ return
+
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ kw.setdefault('typed', self.typed)
+ tc = type(pyobj)
+ self.logger.debug('Any serialize -- %s', tc)
+ if tc in _seqtypes:
+ if self.aslist:
+ array = elt.createAppendElement(ns, n)
+ array.setAttributeType(SOAP.ENC, "Array")
+ array.setAttributeNS(self.nspname, 'SOAP-ENC:arrayType',
+ "xsd:anyType[" + str(len(pyobj)) + "]" )
+ for o in pyobj:
+ #TODO maybe this should take **self.kwargs...
+ serializer = getattr(o, 'typecode', self.__class__()) # also used by _AnyLax()
+ serializer.serialize(array, sw, o, name='element', **kw)
+ else:
+ struct = elt.createAppendElement(ns, n)
+ for o in pyobj:
+ #TODO maybe this should take **self.kwargs...
+ serializer = getattr(o, 'typecode', self.__class__()) # also used by _AnyLax()
+ serializer.serialize(struct, sw, o, **kw)
+ return
+
+ kw['name'] = (ns,n)
+ if tc == types.DictType:
+ el = elt.createAppendElement(ns, n)
+ parentNspname = self.nspname # temporarily clear nspname for dict elements
+ self.nspname = None
+ for o,m in pyobj.items():
+ if type(o) != types.StringType and type(o) != types.UnicodeType:
+ raise Exception, 'Dictionary implementation requires keys to be of type string (or unicode).' %pyobj
+ kw['name'] = o
+ kw.setdefault('typed', True)
+ self.serialize(el, sw, m, **kw)
+ # restore nspname
+ self.nspname = parentNspname
+ return
+
+ if tc == types.InstanceType:
+ tc = pyobj.__class__
+ if hasattr(pyobj, 'typecode'):
+ #serializer = pyobj.typecode.serialmap.get(tc)
+ serializer = pyobj.typecode
+ else:
+ serializer = Any.serialmap.get(tc)
+ if not serializer:
+ tc = (types.ClassType, pyobj.__class__.__name__)
+ serializer = Any.serialmap.get(tc)
+ else:
+ serializer = Any.serialmap.get(tc)
+ if not serializer and isinstance(pyobj, time.struct_time):
+ from ZSI.TCtimes import gDateTime
+ serializer = gDateTime()
+
+ if not serializer:
+ # Last-chance; serialize instances as dictionary
+ if pyobj is None:
+ self.serialize_as_nil(elt.createAppendElement(ns, n))
+ elif type(pyobj) != types.InstanceType:
+ raise EvaluateException('''Any can't serialize ''' + \
+ repr(pyobj))
+ else:
+ self.serialize(elt, sw, pyobj.__dict__, **kw)
+ else:
+ # Try to make the element name self-describing
+ tag = getattr(serializer, 'tag', None)
+ if self.pname is not None:
+ #serializer.nspname = self.nspname
+ #serializer.pname = self.pname
+ if "typed" not in kw:
+ kw['typed'] = False
+ elif tag:
+ if tag.find(':') == -1: tag = 'SOAP-ENC:' + tag
+ kw['name'] = tag
+ kw['typed'] = False
+
+ serializer.unique = self.unique
+ serializer.serialize(elt, sw, pyobj, **kw)
+ # Reset TypeCode
+ #serializer.nspname = None
+ #serializer.pname = None
+
+
+class String(SimpleType):
+ '''A string type.
+ '''
+ empty_content = ''
+ parselist = [ (None,'string') ]
+ seriallist = [ types.StringType, types.UnicodeType ]
+ type = (SCHEMA.XSD3, 'string')
+ logger = _GetLogger('ZSI.TC.String')
+
+ def __init__(self, pname=None, strip=True, **kw):
+ TypeCode.__init__(self, pname, **kw)
+ if kw.has_key('resolver'): self.resolver = kw['resolver']
+ self.strip = strip
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ if self.strip: text = text.strip()
+ if self.pyclass is not None:
+ return self.pyclass(text)
+ return text
+
+ def get_formatted_content(self, pyobj):
+ if type(pyobj) not in _stringtypes:
+ pyobj = str(pyobj)
+ if type(pyobj) == types.UnicodeType: pyobj = pyobj.encode('utf-8')
+ return pyobj
+
+
+class URI(String):
+ '''A URI.
+ '''
+ parselist = [ (None,'anyURI'),(SCHEMA.XSD3, 'anyURI')]
+ type = (SCHEMA.XSD3, 'anyURI')
+ logger = _GetLogger('ZSI.TC.URI')
+
+ def text_to_data(self, text, elt, ps):
+ '''text --> typecode specific data.
+ '''
+ val = String.text_to_data(self, text, elt, ps)
+ return urldecode(val)
+
+ def get_formatted_content(self, pyobj):
+ '''typecode data --> text
+ '''
+ pyobj = String.get_formatted_content(self, pyobj)
+ return urlencode(pyobj)
+
+
+
+class QName(String):
+ '''A QName type
+ '''
+ parselist = [ (None,'QName') ]
+ type = (SCHEMA.XSD3, 'QName')
+ logger = _GetLogger('ZSI.TC.QName')
+
+ def __init__(self, pname=None, strip=1, **kw):
+ String.__init__(self, pname, strip, **kw)
+ self.prefix = None
+
+ def get_formatted_content(self, pyobj):
+ value = pyobj
+ if isinstance(pyobj, tuple):
+ namespaceURI,localName = pyobj
+ if self.prefix is not None:
+ value = "%s:%s" %(self.prefix,localName)
+ return String.get_formatted_content(self, value)
+
+ def set_prefix(self, elt, pyobj):
+ '''use this method to set the prefix of the QName,
+ method looks in DOM to find prefix or set new prefix.
+ This method must be called before get_formatted_content.
+ '''
+ if isinstance(pyobj, tuple):
+ namespaceURI,localName = pyobj
+ self.prefix = elt.getPrefix(namespaceURI)
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ prefix,localName = SplitQName(text)
+ nsdict = ps.GetElementNSdict(elt)
+ try:
+ namespaceURI = nsdict[prefix]
+ except KeyError, ex:
+ raise EvaluateException('cannot resolve prefix(%s)'%prefix,
+ ps.Backtrace(elt))
+
+ v = (namespaceURI,localName)
+ if self.pyclass is not None:
+ return self.pyclass(v)
+ return v
+
+ def serialize_text_node(self, elt, sw, pyobj):
+ '''Serialize without an element node.
+ '''
+ self.set_prefix(elt, pyobj)
+ return String.serialize_text_node(self, elt, sw, pyobj)
+
+
+class Token(String):
+ '''an xsd:token type
+ '''
+ parselist = [ (None, 'token') ]
+ type = (SCHEMA.XSD3, 'token')
+ logger = _GetLogger('ZSI.TC.Token')
+
+
+class Base64String(String):
+ '''A Base64 encoded string.
+ '''
+ parselist = [ (None,'base64Binary'), (SOAP.ENC, 'base64') ]
+ type = (SOAP.ENC, 'base64')
+ logger = _GetLogger('ZSI.TC.Base64String')
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ val = b64decode(text.replace(' ', '').replace('\n','').replace('\r',''))
+ if self.pyclass is not None:
+ return self.pyclass(val)
+ return val
+
+ def get_formatted_content(self, pyobj):
+ pyobj = '\n' + b64encode(pyobj)
+ return String.get_formatted_content(self, pyobj)
+
+
+class Base64Binary(String):
+ parselist = [ (None,'base64Binary'), ]
+ type = (SCHEMA.XSD3, 'base64Binary')
+ logger = _GetLogger('ZSI.TC.Base64Binary')
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ val = b64decode(text)
+ if self.pyclass is not None:
+ return self.pyclass(val)
+ return val
+
+ def get_formatted_content(self, pyobj):
+ pyobj = b64encode(pyobj).strip()
+ return pyobj
+
+
+class HexBinaryString(String):
+ '''Hex-encoded binary (yuk).
+ '''
+ parselist = [ (None,'hexBinary') ]
+ type = (SCHEMA.XSD3, 'hexBinary')
+ logger = _GetLogger('ZSI.TC.HexBinaryString')
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ val = hexdecode(text)
+ if self.pyclass is not None:
+ return self.pyclass(val)
+ return val
+
+ def get_formatted_content(self, pyobj):
+ pyobj = hexencode(pyobj).upper()
+ return String.get_formatted_content(self, pyobj)
+
+
+class XMLString(String):
+ '''A string that represents an XML document
+ '''
+ logger = _GetLogger('ZSI.TC.XMLString')
+
+ def __init__(self, pname=None, readerclass=None, **kw):
+ String.__init__(self, pname, **kw)
+ self.readerclass = readerclass
+
+ def parse(self, elt, ps):
+ if not self.readerclass:
+ from xml.dom.ext.reader import PyExpat
+ self.readerclass = PyExpat.Reader
+ v = String.parse(self, elt, ps)
+ return self.readerclass().fromString(v)
+
+ def get_formatted_content(self, pyobj):
+ pyobj = Canonicalize(pyobj)
+ return String.get_formatted_content(self, pyobj)
+
+
+class Enumeration(String):
+ '''A string type, limited to a set of choices.
+ '''
+ logger = _GetLogger('ZSI.TC.Enumeration')
+
+ def __init__(self, choices, pname=None, **kw):
+ String.__init__(self, pname, **kw)
+ t = type(choices)
+ if t in _seqtypes:
+ self.choices = tuple(choices)
+ elif TypeCode.typechecks:
+ raise TypeError(
+ 'Enumeration choices must be list or sequence, not ' + str(t))
+ if TypeCode.typechecks:
+ for c in self.choices:
+ if type(c) not in _stringtypes:
+ raise TypeError(
+ 'Enumeration choice ' + str(c) + ' is not a string')
+
+ def parse(self, elt, ps):
+ val = String.parse(self, elt, ps)
+ if val not in self.choices:
+ raise EvaluateException('Value not in enumeration list',
+ ps.Backtrace(elt))
+ return val
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ if pyobj not in self.choices:
+ raise EvaluateException('Value not in enumeration list',
+ ps.Backtrace(elt))
+ String.serialize(self, elt, sw, pyobj, name=name, orig=orig, **kw)
+
+
+
+# This is outside the Integer class purely for code esthetics.
+_ignored = []
+
+class Integer(SimpleType):
+ '''Common handling for all integers.
+ '''
+
+ ranges = {
+ 'unsignedByte': (0, 255),
+ 'unsignedShort': (0, 65535),
+ 'unsignedInt': (0, 4294967295L),
+ 'unsignedLong': (0, 18446744073709551615L),
+
+ 'byte': (-128, 127),
+ 'short': (-32768, 32767),
+ 'int': (-2147483648L, 2147483647),
+ 'long': (-9223372036854775808L, 9223372036854775807L),
+
+ 'negativeInteger': (_ignored, -1),
+ 'nonPositiveInteger': (_ignored, 0),
+ 'nonNegativeInteger': (0, _ignored),
+ 'positiveInteger': (1, _ignored),
+
+ 'integer': (_ignored, _ignored)
+ }
+ parselist = [ (None,k) for k in ranges.keys() ]
+ seriallist = [ types.IntType, types.LongType ]
+ logger = _GetLogger('ZSI.TC.Integer')
+
+ def __init__(self, pname=None, format='%d', **kw):
+ TypeCode.__init__(self, pname, **kw)
+ self.format = format
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ if self.pyclass is not None:
+ v = self.pyclass(text)
+ else:
+ try:
+ v = int(text)
+ except:
+ try:
+ v = long(text)
+ except:
+ raise EvaluateException('Unparseable integer',
+ ps.Backtrace(elt))
+ return v
+
+ def parse(self, elt, ps):
+ (ns,type) = self.checkname(elt, ps)
+ if self.nilled(elt, ps): return Nilled
+ elt = self.SimpleHREF(elt, ps, 'integer')
+ if not elt: return None
+
+ if type is None:
+ type = self.type[1]
+ elif self.type[1] is not None and type != self.type[1]:
+ raise EvaluateException('Integer type mismatch; ' \
+ 'got %s wanted %s' % (type,self.type[1]), ps.Backtrace(elt))
+
+ v = self.simple_value(elt, ps)
+ v = self.text_to_data(v, elt, ps)
+
+ (rmin, rmax) = Integer.ranges.get(type, (_ignored, _ignored))
+ if rmin != _ignored and v < rmin:
+ raise EvaluateException('Underflow, less than ' + repr(rmin),
+ ps.Backtrace(elt))
+ if rmax != _ignored and v > rmax:
+ raise EvaluateException('Overflow, greater than ' + repr(rmax),
+ ps.Backtrace(elt))
+ return v
+
+ def get_formatted_content(self, pyobj):
+ return self.format %pyobj
+
+
+
+# See credits, below.
+def _make_inf():
+ x = 2.0
+ x2 = x * x
+ i = 0
+ while i < 100 and x != x2:
+ x = x2
+ x2 = x * x
+ i = i + 1
+ if x != x2:
+ raise ValueError("This machine's floats go on forever!")
+ return x
+
+# This is outside the Decimal class purely for code esthetics.
+_magicnums = { }
+try:
+ _magicnums['INF'] = float('INF')
+ _magicnums['-INF'] = float('-INF')
+except:
+ _magicnums['INF'] = _make_inf()
+ _magicnums['-INF'] = -_magicnums['INF']
+
+# The following comment and code was written by Tim Peters in
+# article <001401be92d2$09dcb800$5fa02299@tim> in comp.lang.python,
+# also available at the following URL:
+# http://groups.google.com/groups?selm=001401be92d2%2409dcb800%245fa02299%40tim
+# Thanks, Tim!
+
+# NaN-testing.
+#
+# The usual method (x != x) doesn't work.
+# Python forces all comparisons thru a 3-outcome cmp protocol; unordered
+# isn't a possible outcome. The float cmp outcome is essentially defined
+# by this C expression (combining some cross-module implementation
+# details, and where px and py are pointers to C double):
+# px == py ? 0 : *px < *py ? -1 : *px > *py ? 1 : 0
+# Comparing x to itself thus always yields 0 by the first clause, and so
+# x != x is never true.
+# If px and py point to distinct NaN objects, a strange thing happens:
+# 1. On scrupulous 754 implementations, *px < *py returns false, and so
+# does *px > *py. Python therefore returns 0, i.e. "equal"!
+# 2. On Pentium HW, an unordered outcome sets an otherwise-impossible
+# combination of condition codes, including both the "less than" and
+# "equal to" flags. Microsoft C generates naive code that accepts
+# the "less than" flag at face value, and so the *px < *py clause
+# returns true, and Python returns -1, i.e. "not equal".
+# So with a proper C 754 implementation Python returns the wrong result,
+# and under MS's improper 754 implementation Python yields the right
+# result -- both by accident. It's unclear who should be shot <wink>.
+#
+# Anyway, the point of all that was to convince you it's tricky getting
+# the right answer in a portable way!
+def isnan(x):
+ """x -> true iff x is a NaN."""
+ # multiply by 1.0 to create a distinct object (x < x *always*
+ # false in Python, due to object identity forcing equality)
+ if x * 1.0 < x:
+ # it's a NaN and this is MS C on a Pentium
+ return 1
+ # Else it's non-NaN, or NaN on a non-MS+Pentium combo.
+ # If it's non-NaN, then x == 1.0 and x == 2.0 can't both be true,
+ # so we return false. If it is NaN, then assuming a good 754 C
+ # implementation Python maps both unordered outcomes to true.
+ return 1.0 == x and x == 2.0
+
+
+class Decimal(SimpleType):
+ '''Parent class for floating-point numbers.
+ '''
+
+ parselist = [ (None,'decimal'), (None,'float'), (None,'double') ]
+ seriallist = _floattypes
+ type = None
+ ranges = {
+ 'float': ( 7.0064923216240861E-46,
+ -3.4028234663852886E+38, 3.4028234663852886E+38 ),
+ 'double': ( 2.4703282292062327E-324,
+ -1.7976931348623158E+308, 1.7976931348623157E+308),
+ }
+ zeropat = re.compile('[1-9]')
+ logger = _GetLogger('ZSI.TC.Decimal')
+
+ def __init__(self, pname=None, format='%f', **kw):
+ TypeCode.__init__(self, pname, **kw)
+ self.format = format
+
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ v = text
+ if self.pyclass is not None:
+ return self.pyclass(v)
+
+ m = _magicnums.get(v)
+ if m: return m
+
+ try:
+ return float(v)
+ except:
+ raise EvaluateException('Unparseable floating point number',
+ ps.Backtrace(elt))
+
+ def parse(self, elt, ps):
+ (ns,type) = self.checkname(elt, ps)
+ elt = self.SimpleHREF(elt, ps, 'floating-point')
+ if not elt: return None
+ tag = getattr(self.__class__, 'type')
+ if tag:
+ if type is None:
+ type = tag
+ elif tag != (ns,type):
+ raise EvaluateException('Floating point type mismatch; ' \
+ 'got (%s,%s) wanted %s' % (ns,type,tag), ps.Backtrace(elt))
+ # Special value?
+ if self.nilled(elt, ps): return Nilled
+ v = self.simple_value(elt, ps)
+ try:
+ fp = self.text_to_data(v, elt, ps)
+ except EvaluateException, ex:
+ ex.args.append(ps.Backtrace(elt))
+ raise ex
+
+ m = _magicnums.get(v)
+ if m:
+ return m
+
+ if str(fp).lower() in [ 'inf', '-inf', 'nan', '-nan' ]:
+ raise EvaluateException('Floating point number parsed as "' + \
+ str(fp) + '"', ps.Backtrace(elt))
+ if fp == 0 and Decimal.zeropat.search(v):
+ raise EvaluateException('Floating point number parsed as zero',
+ ps.Backtrace(elt))
+ (rtiny, rneg, rpos) = Decimal.ranges.get(type, (None, None, None))
+ if rneg and fp < 0 and fp < rneg:
+ raise EvaluateException('Negative underflow', ps.Backtrace(elt))
+ if rtiny and fp > 0 and fp < rtiny:
+ raise EvaluateException('Positive underflow', ps.Backtrace(elt))
+ if rpos and fp > 0 and fp > rpos:
+ raise EvaluateException('Overflow', ps.Backtrace(elt))
+ return fp
+
+ def get_formatted_content(self, pyobj):
+ if pyobj == _magicnums['INF']:
+ return 'INF'
+ elif pyobj == _magicnums['-INF']:
+ return '-INF'
+ elif isnan(pyobj):
+ return 'NaN'
+ else:
+ return self.format %pyobj
+
+
+class Boolean(SimpleType):
+ '''A boolean.
+ '''
+
+ parselist = [ (None,'boolean') ]
+ seriallist = [ bool ]
+ type = (SCHEMA.XSD3, 'boolean')
+ logger = _GetLogger('ZSI.TC.Boolean')
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ v = text
+ if v == 'false':
+ if self.pyclass is None:
+ return False
+ return self.pyclass(False)
+
+ if v == 'true':
+ if self.pyclass is None:
+ return True
+ return self.pyclass(True)
+
+ try:
+ v = int(v)
+ except:
+ try:
+ v = long(v)
+ except:
+ raise EvaluateException('Unparseable boolean',
+ ps.Backtrace(elt))
+
+ if v:
+ if self.pyclass is None:
+ return True
+ return self.pyclass(True)
+
+ if self.pyclass is None:
+ return False
+ return self.pyclass(False)
+
+ def parse(self, elt, ps):
+ self.checkname(elt, ps)
+ elt = self.SimpleHREF(elt, ps, 'boolean')
+ if not elt: return None
+ if self.nilled(elt, ps): return Nilled
+
+ v = self.simple_value(elt, ps).lower()
+ return self.text_to_data(v, elt, ps)
+
+ def get_formatted_content(self, pyobj):
+ if pyobj: return 'true'
+ return 'false'
+
+
+#XXX NOT FIXED YET
+class XML(TypeCode):
+ '''Opaque XML which shouldn't be parsed.
+ comments -- preserve comments
+ inline -- don't href/id when serializing
+ resolver -- object to resolve href's
+ wrapped -- put a wrapper element around it
+ '''
+
+ # Clone returned data?
+ copyit = 0
+ logger = _GetLogger('ZSI.TC.XML')
+
+ def __init__(self, pname=None, comments=0, inline=0, wrapped=True, **kw):
+ TypeCode.__init__(self, pname, **kw)
+ self.comments = comments
+ self.inline = inline
+ if kw.has_key('resolver'): self.resolver = kw['resolver']
+ self.wrapped = wrapped
+ self.copyit = kw.get('copyit', XML.copyit)
+
+ def parse(self, elt, ps):
+ if self.wrapped is False:
+ return elt
+ c = _child_elements(elt)
+ if not c:
+ href = _find_href(elt)
+ if not href:
+ if self.minOccurs == 0: return None
+ raise EvaluateException('Embedded XML document missing',
+ ps.Backtrace(elt))
+ if href[0] != '#':
+ return ps.ResolveHREF(href, self)
+ elt = ps.FindLocalHREF(href, elt)
+ c = _child_elements(elt)
+ if _find_encstyle(elt) != "":
+ #raise EvaluateException('Embedded XML has unknown encodingStyle',
+ # ps.Backtrace(elt)
+ pass
+ if len(c) != 1:
+ raise EvaluateException('Embedded XML has more than one child',
+ ps.Backtrace(elt))
+ if self.copyit: return c[0].cloneNode(1)
+ return c[0]
+
+ def serialize(self, elt, sw, pyobj, name=None, unsuppressedPrefixes=[], **kw):
+ if self.wrapped is False:
+ Canonicalize(pyobj, sw, unsuppressedPrefixes=unsuppressedPrefixes,
+ comments=self.comments)
+ return
+
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ xmlelt = elt.createAppendElement(ns, n)
+
+ if type(pyobj) in _stringtypes:
+ self.set_attributes(xmlelt, pyobj)
+ self.set_attribute_href(xmlelt, objid)
+ elif kw.get('inline', self.inline):
+ self.cb(xmlelt, sw, pyobj, unsuppressedPrefixes)
+ else:
+ self.set_attributes(xmlelt, pyobj)
+ self.set_attribute_href(xmlelt, objid)
+ sw.AddCallback(self.cb, pyobj, unsuppressedPrefixes)
+
+ def cb(self, elt, sw, pyobj, unsuppressedPrefixes=[]):
+ if sw.Known(pyobj):
+ return
+
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ xmlelt = elt.createAppendElement(ns, n)
+ self.set_attribute_id(xmlelt, objid)
+ xmlelt.setAttributeNS(SOAP.ENC, 'encodingStyle', '""')
+ Canonicalize(pyobj, sw, unsuppressedPrefixes=unsuppressedPrefixes,
+ comments=self.comments)
+
+
+class AnyType(TypeCode):
+ """XML Schema xsi:anyType type definition wildCard.
+ class variables:
+ all -- specifies use of all namespaces.
+ other -- specifies use of other namespaces
+ type --
+ """
+ all = '#all'
+ other = '#other'
+ type = (SCHEMA.XSD3, 'anyType')
+ logger = _GetLogger('ZSI.TC.AnyType')
+
+ def __init__(self, pname=None, namespaces=['#all'],
+ minOccurs=1, maxOccurs=1, strip=1, **kw):
+ TypeCode.__init__(self, pname=pname, minOccurs=minOccurs,
+ maxOccurs=maxOccurs, **kw)
+ self.namespaces = namespaces
+
+ def get_formatted_content(self, pyobj):
+ # TODO: not sure this makes sense,
+ # parse side will be clueless, but oh well..
+ what = getattr(pyobj, 'typecode', Any())
+ return what.get_formatted_content(pyobj)
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data. Used only with
+ attributes so will not know anything about this content so
+ why guess?
+ Parameters:
+ text -- text content
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ return text
+
+ def serialize(self, elt, sw, pyobj, **kw):
+ nsuri,typeName = _get_xsitype(pyobj)
+ if self.all not in self.namespaces and nsuri not in self.namespaces:
+ raise EvaluateException(
+ '<anyType> unsupported use of namespaces "%s"' %self.namespaces)
+
+ what = getattr(pyobj, 'typecode', None)
+ if what is None:
+ # TODO: resolve this, "strict" processing but no
+ # concrete schema makes little sense.
+ #what = _AnyStrict(pname=(self.nspname,self.pname))
+ what = Any(pname=(self.nspname,self.pname), unique=True,
+ aslist=False)
+ kw['typed'] = True
+ what.serialize(elt, sw, pyobj, **kw)
+ return
+
+ # Namespace if element AnyType was namespaced.
+ what.serialize(elt, sw, pyobj,
+ name=(self.nspname or what.nspname, self.pname or what.pname), **kw)
+
+ def parse(self, elt, ps):
+ #element name must be declared ..
+ nspname,pname = _get_element_nsuri_name(elt)
+ if nspname != self.nspname or pname != self.pname:
+ raise EvaluateException('<anyType> instance is (%s,%s) found (%s,%s)' %(
+ self.nspname,self.pname,nspname,pname), ps.Backtrace(elt))
+
+ #locate xsi:type
+ prefix, typeName = SplitQName(_find_type(elt))
+ namespaceURI = _resolve_prefix(elt, prefix)
+ pyclass = GTD(namespaceURI, typeName)
+ if not pyclass:
+ if _is_xsd_or_soap_ns(namespaceURI):
+ pyclass = _AnyStrict
+ elif (str(namespaceURI).lower()==str(Apache.Map.type[0]).lower())\
+ and (str(typeName).lower() ==str(Apache.Map.type[1]).lower()):
+ pyclass = Apache.Map
+ else:
+ # Unknown type, so parse into a dictionary
+ pyobj = Any().parse_into_dict_or_list(elt, ps)
+ return pyobj
+
+ what = pyclass(pname=(self.nspname,self.pname))
+ pyobj = what.parse(elt, ps)
+ return pyobj
+
+
+class AnyElement(AnyType):
+ """XML Schema xsi:any element declaration wildCard.
+ class variables:
+ tag -- global element declaration
+ """
+ tag = (SCHEMA.XSD3, 'any')
+ logger = _GetLogger('ZSI.TC.AnyElement')
+
+ def __init__(self, namespaces=['#all'],pname=None,
+ minOccurs=1, maxOccurs=1, strip=1, processContents='strict',
+ **kw):
+
+ if processContents not in ('lax', 'skip', 'strict'):
+ raise ValueError('processContents(%s) must be lax, skip, or strict')
+
+ self.processContents = processContents
+ AnyType.__init__(self, namespaces=namespaces,pname=pname,
+ minOccurs=minOccurs, maxOccurs=maxOccurs, strip=strip, **kw)
+
+ def serialize(self, elt, sw, pyobj, **kw):
+ '''Must provice typecode to AnyElement for serialization, else
+ try to use TC.Any to serialize instance which will serialize
+ based on the data type of pyobj w/o reference to XML schema
+ instance.
+ '''
+ if isinstance(pyobj, TypeCode):
+ raise TypeError, 'pyobj is a typecode instance.'
+
+ what = getattr(pyobj, 'typecode', None)
+ if what is not None and type(pyobj) is types.InstanceType:
+ tc = pyobj.__class__
+ what = Any.serialmap.get(tc)
+ if not what:
+ tc = (types.ClassType, pyobj.__class__.__name__)
+ what = Any.serialmap.get(tc)
+
+ # failed to find a registered type for class
+ if what is None:
+ #TODO: seems incomplete. what about facets.
+ if self.processContents == 'strict':
+ what = _AnyStrict(pname=(self.nspname,self.pname))
+ else:
+ what = _AnyLax(pname=(self.nspname,self.pname))
+
+ self.logger.debug('serialize with %s', what.__class__.__name__)
+ what.serialize(elt, sw, pyobj, **kw)
+
+ def parse(self, elt, ps):
+ '''
+ processContents -- 'lax' | 'skip' | 'strict', 'strict'
+ 1) if 'skip' check namespaces, and return the DOM node.
+ 2) if 'lax' look for declaration, or definition. If
+ not found return DOM node.
+ 3) if 'strict' get declaration, or raise.
+ '''
+ skip = self.processContents == 'skip'
+ nspname,pname = _get_element_nsuri_name(elt)
+ what = GED(nspname, pname)
+ if not skip and what is not None:
+ pyobj = what.parse(elt, ps)
+ try:
+ pyobj.typecode = what
+ except AttributeError, ex:
+ # Assume this means builtin type.
+ pyobj = WrapImmutable(pyobj, what)
+ return pyobj
+
+ # Allow use of "<any>" element declarations w/ local
+ # element declarations
+ prefix, typeName = SplitQName(_find_type(elt))
+ if not skip and typeName:
+ namespaceURI = _resolve_prefix(elt, prefix or 'xmlns')
+ # First look thru user defined namespaces, if don't find
+ # look for 'primitives'.
+ pyclass = GTD(namespaceURI, typeName) or Any
+ what = pyclass(pname=(nspname,pname))
+ pyobj = what.parse(elt, ps)
+ try:
+ pyobj.typecode = what
+ except AttributeError, ex:
+ # Assume this means builtin type.
+ pyobj = WrapImmutable(pyobj, what)
+
+ what.typed = True
+ return pyobj
+
+ if skip:
+ what = XML(pname=(nspname,pname), wrapped=False)
+ elif self.processContents == 'lax':
+ what = _AnyLax(pname=(nspname,pname))
+ else:
+ what = _AnyStrict(pname=(nspname,pname))
+
+ try:
+ pyobj = what.parse(elt, ps)
+ except EvaluateException, ex:
+ self.logger.error("Give up, parse (%s,%s) as a String",
+ what.nspname, what.pname)
+ what = String(pname=(nspname,pname), typed=False)
+ pyobj = WrapImmutable(what.parse(elt, ps), what)
+
+ return pyobj
+
+
+class Union(SimpleType):
+ '''simpleType Union
+
+ class variables:
+ memberTypes -- list [(namespace,name),] tuples, each representing a type defintion.
+ '''
+ memberTypes = None
+ logger = _GetLogger('ZSI.TC.Union')
+
+ def __init__(self, pname=None, minOccurs=1, maxOccurs=1, **kw):
+ SimpleType.__init__(self, pname=pname, minOccurs=minOccurs, maxOccurs=maxOccurs, **kw)
+ self.memberTypeCodes = []
+
+ def setMemberTypeCodes(self):
+ if len(self.memberTypeCodes) > 0:
+ return
+ if self.__class__.memberTypes is None:
+ raise EvaluateException, 'uninitialized class variable memberTypes [(namespace,name),]'
+ for nsuri,name in self.__class__.memberTypes:
+ tcclass = GTD(nsuri,name)
+ if tcclass is None:
+ tc = Any.parsemap.get((nsuri,name))
+ typecode = tc.__class__(pname=(self.nspname,self.pname))
+ else:
+ typecode = tcclass(pname=(self.nspname,self.pname))
+
+ if typecode is None:
+ raise EvaluateException, \
+ 'Typecode class for Union memberType (%s,%s) is missing' %(nsuri,name)
+ if isinstance(typecode, Struct):
+ raise EvaluateException, \
+ 'Illegal: Union memberType (%s,%s) is complexType' %(nsuri,name)
+ self.memberTypeCodes.append(typecode)
+
+ def parse(self, elt, ps, **kw):
+ '''attempt to parse sequentially. No way to know ahead of time
+ what this instance represents. Must be simple type so it can
+ not have attributes nor children, so this isn't too bad.
+ '''
+ self.setMemberTypeCodes()
+ (nsuri,typeName) = self.checkname(elt, ps)
+
+ #if (nsuri,typeName) not in self.memberTypes:
+ # raise EvaluateException(
+ # 'Union Type mismatch got (%s,%s) not in %s' % \
+ # (nsuri, typeName, self.memberTypes), ps.Backtrace(elt))
+
+ for indx in range(len(self.memberTypeCodes)):
+ typecode = self.memberTypeCodes[indx]
+ try:
+ pyobj = typecode.parse(elt, ps)
+ except ParseException, ex:
+ continue
+ except Exception, ex:
+ continue
+
+ if indx > 0:
+ self.memberTypeCodes.remove(typecode)
+ self.memberTypeCodes.insert(0, typecode)
+ break
+
+ else:
+ raise
+
+ return pyobj
+
+ def get_formatted_content(self, pyobj, **kw):
+ self.setMemberTypeCodes()
+ for indx in range(len(self.memberTypeCodes)):
+ typecode = self.memberTypeCodes[indx]
+ try:
+ content = typecode.get_formatted_content(copy.copy(pyobj))
+ break
+ except ParseException, ex:
+ pass
+
+ if indx > 0:
+ self.memberTypeCodes.remove(typecode)
+ self.memberTypeCodes.insert(0, typecode)
+
+ else:
+ raise
+
+ return content
+
+
+class List(SimpleType):
+ '''simpleType List
+ Class data:
+ itemType -- sequence (namespaceURI,name) or a TypeCode instance
+ representing the type definition
+ '''
+ itemType = None
+ logger = _GetLogger('ZSI.TC.List')
+
+ def __init__(self, pname=None, itemType=None, **kw):
+ '''Currently need to require maxOccurs=1, so list
+ is interpreted as a single unit of data.
+ '''
+ assert kw.get('maxOccurs',1) == 1, \
+ 'Currently only supporting SimpleType Lists with maxOccurs=1'
+
+ SimpleType.__init__(self, pname=pname, **kw)
+ self.itemType = itemType or self.itemType
+ self.itemTypeCode = self.itemType
+
+ itemTypeCode = None
+ if type(self.itemTypeCode) in _seqtypes:
+ namespaceURI,name = self.itemTypeCode
+ try:
+ itemTypeCode = GTD(*self.itemType)(None)
+ except:
+ if _is_xsd_or_soap_ns(namespaceURI) is False:
+ raise
+ for pyclass in TYPES:
+ if pyclass.type == self.itemTypeCode:
+ itemTypeCode = pyclass(None)
+ break
+ elif pyclass.type[1] == name:
+ itemTypeCode = pyclass(None)
+
+ if itemTypeCode is None:
+ raise EvaluateException('Filed to locate %s' %self.itemTypeCode)
+
+ if hasattr(itemTypeCode, 'text_to_data') is False:
+ raise EvaluateException('TypeCode class %s missing text_to_data method' %itemTypeCode)
+
+ self.itemTypeCode = itemTypeCode
+
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data. items in
+ list are space separated.
+ '''
+ v = []
+ for item in text.split(' '):
+ v.append(self.itemTypeCode.text_to_data(item, elt, ps))
+
+ if self.pyclass is not None:
+ return self.pyclass(v)
+ return v
+
+ def parse(self, elt, ps):
+ '''elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ self.checkname(elt, ps)
+ if len(_children(elt)) == 0:
+ href = _find_href(elt)
+ if not href:
+ if self.nilled(elt, ps) is False:
+ # No content, no HREF, not NIL: empty string
+ return ""
+ # No content, no HREF, and is NIL...
+ if self.nillable is True:
+ return Nilled
+ raise EvaluateException('Required string missing',
+ ps.Backtrace(elt))
+ if href[0] != '#':
+ return ps.ResolveHREF(href, self)
+ elt = ps.FindLocalHREF(href, elt)
+ self.checktype(elt, ps)
+
+ if self.nilled(elt, ps): return Nilled
+ if len(_children(elt)) == 0: return ''
+
+ v = self.simple_value(elt, ps)
+ return self.text_to_data(v, elt, ps)
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ '''elt -- the current DOMWrapper element
+ sw -- soapWriter object
+ pyobj -- python object to serialize
+ '''
+ if type(pyobj) not in _seqtypes:
+ raise EvaluateException, 'expecting a list'
+
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ el = elt.createAppendElement(ns, n)
+ if self.nillable is True and pyobj is None:
+ self.serialize_as_nil(el)
+ return None
+
+ tc = self.itemTypeCode
+ s = StringIO()
+ for item in pyobj:
+ s.write(tc.get_formatted_content(item))
+ s.write(' ')
+
+ el.createAppendTextNode(textNode)
+
+
+class _AnyStrict(Any):
+ ''' Handles an unspecified types when using a concrete schemas and
+ processContents = "strict".
+ '''
+ #WARNING: unstable
+ logger = _GetLogger('ZSI.TC._AnyStrict')
+
+ def __init__(self, pname=None, aslist=False, **kw):
+ TypeCode.__init__(self, pname=pname, **kw)
+ self.aslist = aslist
+ self.unique = True
+
+ def serialize(self, elt, sw, pyobj, name=None, **kw):
+ if not (type(pyobj) is dict and not self.aslist):
+ Any.serialize(self, elt=elt,sw=sw,pyobj=pyobj,name=name, **kw)
+
+ raise EvaluateException(
+ 'Serializing dictionaries not implemented when processContents=\"strict\".' +
+ 'Try as a list or use processContents=\"lax\".'
+ )
+
+
+class _AnyLax(Any):
+ ''' Handles unspecified types when using a concrete schemas and
+ processContents = "lax".
+ '''
+ logger = _GetLogger('ZSI.TC._AnyLax')
+
+ def __init__(self, pname=None, aslist=False, **kw):
+ TypeCode.__init__(self, pname=pname, **kw)
+ self.aslist = aslist
+ self.unique = True
+
+ def parse_into_dict_or_list(self, elt, ps):
+ c = _child_elements(elt)
+ count = len(c)
+ v = []
+ if count == 0:
+ href = _find_href(elt)
+ if not href: return {}
+ elt = ps.FindLocalHREF(href, elt)
+ self.checktype(elt, ps)
+ c = _child_elements(elt)
+ count = len(c)
+ if count == 0: return self.listify([])
+ if self.nilled(elt, ps): return Nilled
+
+ # group consecutive elements with the same name together
+ # We treat consecutive elements with the same name as lists.
+ groupedElements = [] # tuples of (name, elementList)
+ previousName = ""
+ currentElementList = None
+ for ce in _child_elements(elt):
+ name = ce.localName
+ if (name != previousName): # new name, so new group
+ if currentElementList != None: # store previous group if there is one
+ groupedElements.append( (previousName, currentElementList) )
+ currentElementList = list()
+ currentElementList.append(ce) # append to list
+ previousName = name
+ # add the last group if necessary
+ if currentElementList != None: # store previous group if there is one
+ groupedElements.append( (previousName, currentElementList) )
+
+ # parse the groups of names
+ if len(groupedElements) < 1: # should return earlier
+ return None
+ # return a list if there is one name and multiple data
+ elif (len(groupedElements) == 1) and (len(groupedElements[0][0]) > 1):
+ self.aslist = False
+ # else return a dictionary
+
+ for name,eltList in groupedElements:
+ lst = []
+ for elt in eltList:
+ #aslist = self.aslist
+ lst.append( self.parse(elt, ps) )
+ #self.aslist = aslist # restore the aslist setting
+ if len(lst) > 1: # consecutive elements with the same name means a list
+ v.append( (name, lst) )
+ elif len(lst) == 1:
+ v.append( (name, lst[0]) )
+
+ return self.listify(v)
+
+ def checkname(self, elt, ps):
+ '''See if the name and type of the "elt" element is what we're
+ looking for. Return the element's type.
+ Since this is _AnyLax, it's ok if names don't resolve.
+ '''
+
+ parselist,errorlist = self.get_parse_and_errorlist()
+ ns, name = _get_element_nsuri_name(elt)
+ if ns == SOAP.ENC:
+ if parselist and \
+ (None, name) not in parselist and (ns, name) not in parselist:
+ raise EvaluateException(
+ 'Element mismatch (got %s wanted %s) (SOAP encoding namespace)' % \
+ (name, errorlist), ps.Backtrace(elt))
+ return (ns, name)
+
+ # Not a type, check name matches.
+ if self.nspname and ns != self.nspname:
+ raise EvaluateException('Element NS mismatch (got %s wanted %s)' % \
+ (ns, self.nspname), ps.Backtrace(elt))
+
+ return self.checktype(elt, ps)
+
+
+def RegisterType(C, clobber=0, *args, **keywords):
+ instance = apply(C, args, keywords)
+ for t in C.__dict__.get('parselist', []):
+ prev = Any.parsemap.get(t)
+ if prev:
+ if prev.__class__ == C: continue
+ if not clobber:
+ raise TypeError(
+ str(C) + ' duplicating parse registration for ' + str(t))
+ Any.parsemap[t] = instance
+ for t in C.__dict__.get('seriallist', []):
+ ti = type(t)
+ if ti in [ types.TypeType, types.ClassType]:
+ key = t
+ elif ti in _stringtypes:
+ key = (types.ClassType, t)
+ else:
+ raise TypeError(str(t) + ' is not a class name')
+ prev = Any.serialmap.get(key)
+ if prev:
+ if prev.__class__ == C: continue
+ if not clobber:
+ raise TypeError(
+ str(C) + ' duplicating serial registration for ' + str(t))
+ Any.serialmap[key] = instance
+
+
+from TCnumbers import *
+from TCtimes import *
+from schema import GTD, GED, WrapImmutable
+from TCcompound import *
+from TCapache import *
+
+# aliases backwards compatiblity
+_get_type_definition, _get_global_element_declaration, Wrap = GTD, GED, WrapImmutable
+
+f = lambda x: type(x) == types.ClassType and issubclass(x, TypeCode) and getattr(x, 'type', None) is not None
+TYPES = filter(f, map(lambda y:eval(y),dir()))
+
+
+if __name__ == '__main__': print _copyright
+
diff --git a/ZSI/zsi/ZSI/TCapache.py b/ZSI/zsi/ZSI/TCapache.py
new file mode 100644
index 0000000..fe39a00
--- /dev/null
+++ b/ZSI/zsi/ZSI/TCapache.py
@@ -0,0 +1,75 @@
+#! /usr/bin/env python
+# $Header$
+'''Apache typecodes.
+'''
+
+from ZSI import _copyright, _child_elements, _get_idstr
+from ZSI.TC import TypeCode, Struct as _Struct, Any as _Any
+
+class Apache:
+ NS = "http://xml.apache.org/xml-soap"
+
+class _Map(TypeCode):
+ '''Apache's "Map" type.
+ '''
+ parselist = [ (Apache.NS, 'Map') ]
+
+ def __init__(self, pname=None, aslist=0, **kw):
+ TypeCode.__init__(self, pname, **kw)
+ self.aslist = aslist
+ self.tc = _Struct(None, [ _Any('key'), _Any('value') ], inline=1)
+
+ def parse(self, elt, ps):
+ self.checkname(elt, ps)
+ if self.nilled(elt, ps): return None
+ p = self.tc.parse
+ if self.aslist:
+ v = []
+ for c in _child_elements(elt):
+ d = p(c, ps)
+ v.append((d['key'], d['value']))
+ else:
+ v = {}
+ for c in _child_elements(elt):
+ d = p(c, ps)
+ v[d['key']] = d['value']
+ return v
+
+ def serialize(self, elt, sw, pyobj, name=None, **kw):
+ objid = _get_idstr(pyobj)
+ n = name or self.pname or ('E' + objid)
+
+ # nillable
+ el = elt.createAppendElement(self.nspname, n)
+ if self.nillable is True and pyobj is None:
+ self.serialize_as_nil(el)
+ return None
+
+ # other attributes
+ self.set_attributes(el, pyobj)
+
+ # soap href attribute
+ unique = self.unique or kw.get('unique', False)
+ if unique is False and sw.Known(orig or pyobj):
+ self.set_attribute_href(el, objid)
+ return None
+
+ # xsi:type attribute
+ if kw.get('typed', self.typed) is True:
+ self.set_attribute_xsi_type(el, **kw)
+
+ # soap id attribute
+ if self.unique is False:
+ self.set_attribute_id(el, objid)
+
+ if self.aslist:
+ for k,v in pyobj:
+ self.tc.serialize(el, sw, {'key': k, 'value': v}, name='item')
+ else:
+ for k,v in pyobj.items():
+ self.tc.serialize(el, sw, {'key': k, 'value': v}, name='item')
+
+
+Apache.Map = _Map
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/TCcompound.py b/ZSI/zsi/ZSI/TCcompound.py
new file mode 100644
index 0000000..905d090
--- /dev/null
+++ b/ZSI/zsi/ZSI/TCcompound.py
@@ -0,0 +1,674 @@
+#! /usr/bin/env python
+# $Header$
+'''Compound typecodes.
+'''
+
+from ZSI import _copyright, _children, _child_elements, \
+ _inttypes, _stringtypes, _seqtypes, _find_arraytype, _find_href, \
+ _find_type, _find_xmlns_prefix, _get_idstr, EvaluateException, \
+ ParseException
+
+from TC import _get_element_nsuri_name, \
+ _get_xsitype, TypeCode, Any, AnyElement, AnyType, \
+ Nilled, UNBOUNDED
+
+from schema import ElementDeclaration, TypeDefinition, \
+ _get_substitute_element, _get_type_definition
+
+from ZSI.wstools.Namespaces import SCHEMA, SOAP
+from ZSI.wstools.Utility import SplitQName
+from ZSI.wstools.logging import getLogger as _GetLogger
+import re, types
+
+_find_arrayoffset = lambda E: E.getAttributeNS(SOAP.ENC, "offset")
+_find_arrayposition = lambda E: E.getAttributeNS(SOAP.ENC, "position")
+
+_offset_pat = re.compile(r'\[[0-9]+\]')
+_position_pat = _offset_pat
+
+def _check_typecode_list(ofwhat, tcname):
+ '''Check a list of typecodes for compliance with Struct
+ requirements.'''
+ for o in ofwhat:
+ if callable(o): #skip if _Mirage
+ continue
+ if not isinstance(o, TypeCode):
+ raise TypeError(
+ tcname + ' ofwhat outside the TypeCode hierarchy, ' +
+ str(o.__class__))
+ if o.pname is None and not isinstance(o, AnyElement):
+ raise TypeError(tcname + ' element ' + str(o) + ' has no name')
+
+
+def _get_type_or_substitute(typecode, pyobj, sw, elt):
+ '''return typecode or substitute type for wildcard or
+ derived type. For serialization only.
+ '''
+ sub = getattr(pyobj, 'typecode', typecode)
+ if sub is typecode or sub is None:
+ return typecode
+
+ # Element WildCard
+ if isinstance(typecode, AnyElement):
+ return sub
+
+ # Global Element Declaration
+ if isinstance(sub, ElementDeclaration):
+ if (typecode.nspname,typecode.pname) == (sub.nspname,sub.pname):
+ raise TypeError(\
+ 'bad usage, failed to serialize element reference (%s, %s), in: %s' %
+ (typecode.nspname, typecode.pname, sw.Backtrace(elt),))
+
+ raise TypeError(\
+ 'failed to serialize (%s, %s) illegal sub GED (%s,%s): %s' %
+ (typecode.nspname, typecode.pname, sub.nspname, sub.pname,
+ sw.Backtrace(elt),))
+
+ # Local Element
+ if not isinstance(typecode, AnyType) and not isinstance(sub, typecode.__class__):
+ raise TypeError(\
+ 'failed to serialize substitute %s for %s, not derivation: %s' %
+ (sub, typecode, sw.Backtrace(elt),))
+
+ sub.nspname = typecode.nspname
+ sub.pname = typecode.pname
+ sub.aname = typecode.aname
+ sub.minOccurs = 1
+ sub.maxOccurs = 1
+ return sub
+
+
+def _get_any_instances(ofwhat, d):
+ '''Run thru list ofwhat.anames and find unmatched keys in value
+ dictionary d. Assume these are element wildcard instances.
+ '''
+ any_keys = []
+ anames = map(lambda what: what.aname, ofwhat)
+ for aname,pyobj in d.items():
+ if isinstance(pyobj, AnyType) or aname in anames or pyobj is None:
+ continue
+ any_keys.append(aname)
+ return any_keys
+
+
+
+
+class ComplexType(TypeCode):
+ '''Represents an element of complexType, potentially containing other
+ elements.
+ '''
+ logger = _GetLogger('ZSI.TCcompound.ComplexType')
+
+ def __init__(self, pyclass, ofwhat, pname=None, inorder=False, inline=False,
+ mutable=True, mixed=False, mixed_aname='_text', **kw):
+ '''pyclass -- the Python class to hold the fields
+ ofwhat -- a list of fields to be in the complexType
+ inorder -- fields must be in exact order or not
+ inline -- don't href/id when serializing
+ mutable -- object could change between multiple serializations
+ type -- the (URI,localname) of the datatype
+ mixed -- mixed content model? True/False
+ mixed_aname -- if mixed is True, specify text content here. Default _text
+ '''
+ TypeCode.__init__(self, pname, pyclass=pyclass, **kw)
+ self.inorder = inorder
+ self.inline = inline
+ self.mutable = mutable
+ self.mixed = mixed
+ self.mixed_aname = None
+ if mixed is True:
+ self.mixed_aname = mixed_aname
+
+ if self.mutable is True: self.inline = True
+ self.type = kw.get('type') or _get_xsitype(self)
+ t = type(ofwhat)
+ if t not in _seqtypes:
+ raise TypeError(
+ 'Struct ofwhat must be list or sequence, not ' + str(t))
+ self.ofwhat = tuple(ofwhat)
+ if TypeCode.typechecks:
+ # XXX Not sure how to determine if new-style class..
+ if self.pyclass is not None and \
+ type(self.pyclass) is not types.ClassType and not isinstance(self.pyclass, object):
+ raise TypeError('pyclass must be None or an old-style/new-style class, not ' +
+ str(type(self.pyclass)))
+ _check_typecode_list(self.ofwhat, 'ComplexType')
+
+ def parse(self, elt, ps):
+ debug = self.logger.debugOn()
+ debug and self.logger.debug('parse')
+
+ xtype = self.checkname(elt, ps)
+ if self.type and xtype not in [ self.type, (None,None) ]:
+ if not isinstance(self, TypeDefinition):
+ raise EvaluateException(\
+ 'ComplexType for %s has wrong type(%s), looking for %s' %
+ (self.pname, self.checktype(elt,ps), self.type),
+ ps.Backtrace(elt))
+ else:
+ #TODO: mabye change MRO to handle this
+ debug and self.logger.debug('delegate to substitute type')
+ what = TypeDefinition.getSubstituteType(self, elt, ps)
+ return what.parse(elt, ps)
+
+ href = _find_href(elt)
+ if href:
+ if _children(elt):
+ raise EvaluateException('Struct has content and HREF',
+ ps.Backtrace(elt))
+ elt = ps.FindLocalHREF(href, elt)
+ c = _child_elements(elt)
+ count = len(c)
+ if self.nilled(elt, ps): return Nilled
+
+ # Create the object.
+ v = {}
+
+ # parse all attributes contained in attribute_typecode_dict (user-defined attributes),
+ # the values (if not None) will be keyed in self.attributes dictionary.
+ attributes = self.parse_attributes(elt, ps)
+ if attributes:
+ v[self.attrs_aname] = attributes
+
+ #MIXED
+ if self.mixed is True:
+ v[self.mixed_aname] = self.simple_value(elt,ps, mixed=True)
+
+ # Clone list of kids (we null it out as we process)
+ c, crange = c[:], range(len(c))
+ # Loop over all items we're expecting
+
+ if debug:
+ self.logger.debug("ofwhat: %s",str(self.ofwhat))
+
+ any = None
+ for i,what in [ (i, self.ofwhat[i]) for i in range(len(self.ofwhat)) ]:
+
+ # retrieve typecode if it is hidden
+ if callable(what): what = what()
+
+ # Loop over all available kids
+ if debug:
+ self.logger.debug("what: (%s,%s)", what.nspname, what.pname)
+
+ for j,c_elt in [ (j, c[j]) for j in crange if c[j] ]:
+ if debug:
+ self.logger.debug("child node: (%s,%s)", c_elt.namespaceURI,
+ c_elt.tagName)
+ if what.name_match(c_elt):
+ # Parse value, and mark this one done.
+ try:
+ value = what.parse(c_elt, ps)
+ except EvaluateException, e:
+ #what = _get_substitute_element(c_elt, what)
+ #value = what.parse(c_elt, ps)
+ raise
+ if what.maxOccurs > 1:
+ if v.has_key(what.aname):
+ v[what.aname].append(value)
+ else:
+ v[what.aname] = [value]
+ c[j] = None
+ continue
+ else:
+ v[what.aname] = value
+ c[j] = None
+ break
+ else:
+ if debug:
+ self.logger.debug("no element (%s,%s)",
+ what.nspname, what.pname)
+
+ # No match; if it was supposed to be here, that's an error.
+ if self.inorder is True and i == j:
+ raise EvaluateException('Out of order complexType',
+ ps.Backtrace(c_elt))
+ else:
+ # only supporting 1 <any> declaration in content.
+ if isinstance(what,AnyElement):
+ any = what
+ elif hasattr(what, 'default'):
+ v[what.aname] = what.default
+ elif what.minOccurs > 0 and not v.has_key(what.aname):
+ raise EvaluateException('Element "' + what.aname + \
+ '" missing from complexType', ps.Backtrace(elt))
+
+ # Look for wildcards and unprocessed children
+ # XXX Stick all this stuff in "any", hope for no collisions
+ if any is not None:
+ occurs = 0
+ v[any.aname] = []
+ for j,c_elt in [ (j, c[j]) for j in crange if c[j] ]:
+ value = any.parse(c_elt, ps)
+ if any.maxOccurs == UNBOUNDED or any.maxOccurs > 1:
+ v[any.aname].append(value)
+ else:
+ v[any.aname] = value
+
+ occurs += 1
+
+ # No such thing as nillable <any>
+ if any.maxOccurs == 1 and occurs == 0:
+ v[any.aname] = None
+ elif occurs < any.minOccurs or (any.maxOccurs!=UNBOUNDED and any.maxOccurs<occurs):
+ raise EvaluateException('occurances of <any> elements(#%d) bound by (%d,%s)' %(
+ occurs, any.minOccurs,str(any.maxOccurs)), ps.Backtrace(elt))
+
+ if not self.pyclass:
+ return v
+
+ # type definition must be informed of element tag (nspname,pname),
+ # element declaration is initialized with a tag.
+ try:
+ pyobj = self.pyclass()
+ except Exception, e:
+ raise TypeError("Constructing element (%s,%s) with pyclass(%s), %s" \
+ %(self.nspname, self.pname, self.pyclass.__name__, str(e)))
+ for key in v.keys():
+ setattr(pyobj, key, v[key])
+ return pyobj
+
+ def serialize(self, elt, sw, pyobj, inline=False, name=None, **kw):
+ if inline or self.inline:
+ self.cb(elt, sw, pyobj, name=name, **kw)
+ else:
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ el = elt.createAppendElement(ns, n)
+ el.setAttributeNS(None, 'href', "#%s" %objid)
+ sw.AddCallback(self.cb, elt, sw, pyobj)
+
+ def cb(self, elt, sw, pyobj, name=None, **kw):
+ debug = self.logger.debugOn()
+ if debug:
+ self.logger.debug("cb: %s" %str(self.ofwhat))
+
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ if pyobj is None:
+ if self.nillable is True:
+ elem = elt.createAppendElement(ns, n)
+ self.serialize_as_nil(elem)
+ return
+ raise EvaluateException, 'element(%s,%s) is not nillable(%s)' %(
+ self.nspname,self.pname,self.nillable)
+
+ if self.mutable is False and sw.Known(pyobj):
+ return
+
+ if debug:
+ self.logger.debug("element: (%s, %s)", str(ns), n)
+
+ if n is not None:
+ elem = elt.createAppendElement(ns, n)
+ self.set_attributes(elem, pyobj)
+ if kw.get('typed', self.typed) is True:
+ self.set_attribute_xsi_type(elem)
+
+ #MIXED For now just stick it in front.
+ if self.mixed is True and self.mixed_aname is not None:
+ if hasattr(pyobj, self.mixed_aname):
+ textContent = getattr(pyobj, self.mixed_aname)
+ if hasattr(textContent, 'typecode'):
+ textContent.typecode.serialize_text_node(elem, sw, textContent)
+ elif type(textContent) in _stringtypes:
+ if debug:
+ self.logger.debug("mixed text content:\n\t%s",
+ textContent)
+ elem.createAppendTextNode(textContent)
+ else:
+ raise EvaluateException('mixed test content in element (%s,%s) must be a string type' %(
+ self.nspname,self.pname), sw.Backtrace(elt))
+ else:
+ if debug:
+ self.logger.debug("mixed NO text content in %s",
+ self.mixed_aname)
+ else:
+ #For information items w/o tagNames
+ # ie. model groups,SOAP-ENC:Header
+ elem = elt
+
+ if self.inline:
+ pass
+ elif not self.inline and self.unique:
+ raise EvaluateException('Not inline, but unique makes no sense. No href/id.',
+ sw.Backtrace(elt))
+ elif n is not None:
+ self.set_attribute_id(elem, objid)
+
+ if self.pyclass and type(self.pyclass) is type:
+ f = lambda attr: getattr(pyobj, attr, None)
+ elif self.pyclass:
+ d = pyobj.__dict__
+ f = lambda attr: d.get(attr)
+ else:
+ d = pyobj
+ f = lambda attr: pyobj.get(attr)
+ if TypeCode.typechecks and type(d) != types.DictType:
+ raise TypeError("Classless struct didn't get dictionary")
+
+ indx, lenofwhat = 0, len(self.ofwhat)
+ if debug:
+ self.logger.debug('element declaration (%s,%s)', self.nspname,
+ self.pname)
+ if self.type:
+ self.logger.debug('xsi:type definition (%s,%s)', self.type[0],
+ self.type[1])
+ else:
+ self.logger.warning('NO xsi:type')
+
+ while indx < lenofwhat:
+ occurs = 0
+ what = self.ofwhat[indx]
+
+ # retrieve typecode if hidden
+ if callable(what): what = what()
+
+ if debug:
+ self.logger.debug('serialize what -- %s',
+ what.__class__.__name__)
+
+ # No way to order <any> instances, so just grab any unmatched
+ # anames and serialize them. Only support one <any> in all content.
+ # Must be self-describing instances
+
+ # Regular handling of declared elements
+ aname = what.aname
+ v = f(aname)
+ indx += 1
+ if what.minOccurs == 0 and v is None:
+ continue
+
+ # Default to typecode, if self-describing instance, and check
+ # to make sure it is derived from what.
+ whatTC = what
+ if whatTC.maxOccurs > 1 and v is not None:
+ if type(v) not in _seqtypes:
+ raise EvaluateException('pyobj (%s,%s), aname "%s": maxOccurs %s, expecting a %s' %(
+ self.nspname,self.pname,what.aname,whatTC.maxOccurs,_seqtypes),
+ sw.Backtrace(elt))
+
+ for v2 in v:
+ occurs += 1
+ if occurs > whatTC.maxOccurs:
+ raise EvaluateException('occurances (%d) exceeded maxOccurs(%d) for <%s>' %(
+ occurs, whatTC.maxOccurs, what.pname),
+ sw.Backtrace(elt))
+
+ what = _get_type_or_substitute(whatTC, v2, sw, elt)
+ if debug and what is not whatTC:
+ self.logger.debug('substitute derived type: %s' %
+ what.__class__)
+
+ what.serialize(elem, sw, v2, **kw)
+# try:
+# what.serialize(elem, sw, v2, **kw)
+# except Exception, e:
+# raise EvaluateException('Serializing %s.%s, %s %s' %
+# (n, whatTC.aname or '?', e.__class__.__name__, str(e)))
+
+ if occurs < whatTC.minOccurs:
+ raise EvaluateException(\
+ 'occurances(%d) less than minOccurs(%d) for <%s>' %
+ (occurs, whatTC.minOccurs, what.pname), sw.Backtrace(elt))
+
+ continue
+
+ if v is not None or what.nillable is True:
+ what = _get_type_or_substitute(whatTC, v, sw, elt)
+ if debug and what is not whatTC:
+ self.logger.debug('substitute derived type: %s' %
+ what.__class__)
+ what.serialize(elem, sw, v, **kw)
+# try:
+# what.serialize(elem, sw, v, **kw)
+# except (ParseException, EvaluateException), e:
+# raise
+# except Exception, e:
+# raise EvaluateException('Serializing %s.%s, %s %s' %
+# (n, whatTC.aname or '?', e.__class__.__name__, str(e)),
+# sw.Backtrace(elt))
+ continue
+
+ raise EvaluateException('Got None for nillable(%s), minOccurs(%d) element (%s,%s), %s' %
+ (what.nillable, what.minOccurs, what.nspname, what.pname, elem),
+ sw.Backtrace(elt))
+
+
+ def setDerivedTypeContents(self, extensions=None, restrictions=None):
+ """For derived types set appropriate parameter and
+ """
+ if extensions:
+ ofwhat = list(self.ofwhat)
+ if type(extensions) in _seqtypes:
+ ofwhat += list(extensions)
+ else:
+ ofwhat.append(extensions)
+ elif restrictions:
+ if type(restrictions) in _seqtypes:
+ ofwhat = restrictions
+ else:
+ ofwhat = (restrictions,)
+ else:
+ return
+ self.ofwhat = tuple(ofwhat)
+ self.lenofwhat = len(self.ofwhat)
+
+
+class Struct(ComplexType):
+ '''Struct is a complex type for accessors identified by name.
+ Constraint: No element may have the same name as any other,
+ nor may any element have a maxOccurs > 1.
+
+ <xs:group name="Struct" >
+ <xs:sequence>
+ <xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ </xs:sequence>
+ </xs:group>
+
+ <xs:complexType name="Struct" >
+ <xs:group ref="tns:Struct" minOccurs="0" />
+ <xs:attributeGroup ref="tns:commonAttributes"/>
+ </xs:complexType>
+ '''
+ logger = _GetLogger('ZSI.TCcompound.Struct')
+
+ def __init__(self, pyclass, ofwhat, pname=None, inorder=False, inline=False,
+ mutable=True, **kw):
+ '''pyclass -- the Python class to hold the fields
+ ofwhat -- a list of fields to be in the struct
+ inorder -- fields must be in exact order or not
+ inline -- don't href/id when serializing
+ mutable -- object could change between multiple serializations
+ '''
+ ComplexType.__init__(self, pyclass, ofwhat, pname=pname,
+ inorder=inorder, inline=inline, mutable=mutable,
+ **kw
+ )
+
+ # Check Constraints
+ whats = map(lambda what: (what.nspname,what.pname), self.ofwhat)
+ for idx in range(len(self.ofwhat)):
+ what = self.ofwhat[idx]
+ key = (what.nspname,what.pname)
+ if not isinstance(what, AnyElement) and what.maxOccurs > 1:
+ raise TypeError,\
+ 'Constraint: no element can have a maxOccurs>1'
+ if key in whats[idx+1:]:
+ raise TypeError,\
+ 'Constraint: No element may have the same name as any other'
+
+
+class Array(TypeCode):
+ '''An array.
+ atype -- arrayType, (namespace,ncname)
+ mutable -- object could change between multiple serializations
+ undeclared -- do not serialize/parse arrayType attribute.
+ '''
+ logger = _GetLogger('ZSI.TCcompound.Array')
+
+ def __init__(self, atype, ofwhat, pname=None, dimensions=1, fill=None,
+ sparse=False, mutable=False, size=None, nooffset=0, undeclared=False,
+ childnames=None, **kw):
+ TypeCode.__init__(self, pname, **kw)
+ self.dimensions = dimensions
+ self.atype = atype
+ if undeclared is False and self.atype[1].endswith(']') is False:
+ self.atype = (self.atype[0], '%s[]' %self.atype[1])
+ # Support multiple dimensions
+ if self.dimensions != 1:
+ raise TypeError("Only single-dimensioned arrays supported")
+ self.fill = fill
+ self.sparse = sparse
+ #if self.sparse: ofwhat.minOccurs = 0
+ self.mutable = mutable
+ self.size = size
+ self.nooffset = nooffset
+ self.undeclared = undeclared
+ self.childnames = childnames
+ if self.size:
+ t = type(self.size)
+ if t in _inttypes:
+ self.size = (self.size,)
+ elif t in _seqtypes:
+ self.size = tuple(self.size)
+ elif TypeCode.typechecks:
+ raise TypeError('Size must be integer or list, not ' + str(t))
+
+ if TypeCode.typechecks:
+ if self.undeclared is False and type(atype) not in _seqtypes and len(atype) == 2:
+ raise TypeError("Array type must be a sequence of len 2.")
+ t = type(ofwhat)
+ if not isinstance(ofwhat, TypeCode):
+ raise TypeError(
+ 'Array ofwhat outside the TypeCode hierarchy, ' +
+ str(ofwhat.__class__))
+ if self.size:
+ if len(self.size) != self.dimensions:
+ raise TypeError('Array dimension/size mismatch')
+ for s in self.size:
+ if type(s) not in _inttypes:
+ raise TypeError('Array size "' + str(s) +
+ '" is not an integer.')
+ self.ofwhat = ofwhat
+
+ def parse_offset(self, elt, ps):
+ o = _find_arrayoffset(elt)
+ if not o: return 0
+ if not _offset_pat.match(o):
+ raise EvaluateException('Bad offset "' + o + '"',
+ ps.Backtrace(elt))
+ return int(o[1:-1])
+
+ def parse_position(self, elt, ps):
+ o = _find_arrayposition(elt)
+ if not o: return None
+ if o.find(','):
+ raise EvaluateException('Sorry, no multi-dimensional arrays',
+ ps.Backtrace(elt))
+ if not _position_pat.match(o):
+ raise EvaluateException('Bad array position "' + o + '"',
+ ps.Backtrace(elt))
+ return int(o[-1:1])
+
+ def parse(self, elt, ps):
+ href = _find_href(elt)
+ if href:
+ if _children(elt):
+ raise EvaluateException('Array has content and HREF',
+ ps.Backtrace(elt))
+ elt = ps.FindLocalHREF(href, elt)
+ if self.nilled(elt, ps): return Nilled
+ if not _find_arraytype(elt) and self.undeclared is False:
+ raise EvaluateException('Array expected', ps.Backtrace(elt))
+ t = _find_type(elt)
+ if t:
+ pass # XXX should check the type, but parsing that is hairy.
+ offset = self.parse_offset(elt, ps)
+ v, vlen = [], 0
+ if offset and not self.sparse:
+ while vlen < offset:
+ vlen += 1
+ v.append(self.fill)
+ for c in _child_elements(elt):
+ item = self.ofwhat.parse(c, ps)
+ position = self.parse_position(c, ps) or offset
+ if self.sparse:
+ v.append((position, item))
+ else:
+ while offset < position:
+ offset += 1
+ v.append(self.fill)
+ v.append(item)
+ offset += 1
+ return v
+
+ def serialize(self, elt, sw, pyobj, name=None, childnames=None, **kw):
+ debug = self.logger.debugOn()
+ if debug:
+ self.logger.debug("serialize: %r" %pyobj)
+
+ if self.mutable is False and sw.Known(pyobj): return
+ objid = _get_idstr(pyobj)
+ ns,n = self.get_name(name, objid)
+ el = elt.createAppendElement(ns, n)
+
+ # nillable
+ if self.nillable is True and pyobj is None:
+ self.serialize_as_nil(el)
+ return None
+
+ # other attributes
+ self.set_attributes(el, pyobj)
+
+ # soap href attribute
+ unique = self.unique or kw.get('unique', False)
+ if unique is False and sw.Known(pyobj):
+ self.set_attribute_href(el, objid)
+ return None
+
+ # xsi:type attribute
+ if kw.get('typed', self.typed) is True:
+ self.set_attribute_xsi_type(el, **kw)
+
+ # soap id attribute
+ if self.unique is False:
+ self.set_attribute_id(el, objid)
+
+ offset = 0
+ if self.sparse is False and self.nooffset is False:
+ offset, end = 0, len(pyobj)
+ while offset < end and pyobj[offset] == self.fill:
+ offset += 1
+ if offset:
+ el.setAttributeNS(SOAP.ENC, 'offset', '[%d]' %offset)
+
+ if self.undeclared is False:
+ el.setAttributeNS(SOAP.ENC, 'arrayType',
+ '%s:%s' %(el.getPrefix(self.atype[0]), self.atype[1])
+ )
+
+ if debug:
+ self.logger.debug("ofwhat: %r" %self.ofwhat)
+
+ d = {}
+ kn = childnames or self.childnames
+ if kn:
+ d['name'] = kn
+ elif not self.ofwhat.aname:
+ d['name'] = 'element'
+
+ if self.sparse is False:
+ for e in pyobj[offset:]: self.ofwhat.serialize(el, sw, e, **d)
+ else:
+ position = 0
+ for pos, v in pyobj:
+ if pos != position:
+ el.setAttributeNS(SOAP.ENC, 'position', '[%d]' %pos)
+ position = pos
+
+ self.ofwhat.serialize(el, sw, v, **d)
+ position += 1
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/TCnumbers.py b/ZSI/zsi/ZSI/TCnumbers.py
new file mode 100644
index 0000000..67007b0
--- /dev/null
+++ b/ZSI/zsi/ZSI/TCnumbers.py
@@ -0,0 +1,184 @@
+#! /usr/bin/env python
+# $Header$
+'''Typecodes for numbers.
+'''
+import types
+from ZSI import _copyright, _inttypes, _floattypes, _seqtypes, \
+ EvaluateException
+from ZSI.TC import TypeCode, Integer, Decimal
+from ZSI.wstools.Namespaces import SCHEMA
+
+class IunsignedByte(Integer):
+ '''Unsigned 8bit value.
+ '''
+ type = (SCHEMA.XSD3, "unsignedByte")
+ parselist = [ (None, "unsignedByte") ]
+ seriallist = [ ]
+
+class IunsignedShort(Integer):
+ '''Unsigned 16bit value.
+ '''
+ type = (SCHEMA.XSD3, "unsignedShort")
+ parselist = [ (None, "unsignedShort") ]
+ seriallist = [ ]
+
+class IunsignedInt(Integer):
+ '''Unsigned 32bit value.
+ '''
+ type = (SCHEMA.XSD3, "unsignedInt")
+ parselist = [ (None, "unsignedInt") ]
+ seriallist = [ ]
+
+class IunsignedLong(Integer):
+ '''Unsigned 64bit value.
+ '''
+ type = (SCHEMA.XSD3, "unsignedLong")
+ parselist = [ (None, "unsignedLong") ]
+ seriallist = [ ]
+
+class Ibyte(Integer):
+ '''Signed 8bit value.
+ '''
+ type = (SCHEMA.XSD3, "byte")
+ parselist = [ (None, "byte") ]
+ seriallist = [ ]
+
+class Ishort(Integer):
+ '''Signed 16bit value.
+ '''
+ type = (SCHEMA.XSD3, "short")
+ parselist = [ (None, "short") ]
+ seriallist = [ ]
+
+class Iint(Integer):
+ '''Signed 32bit value.
+ '''
+ type = (SCHEMA.XSD3, "int")
+ parselist = [ (None, "int") ]
+ seriallist = [ types.IntType ]
+
+class Ilong(Integer):
+ '''Signed 64bit value.
+ '''
+ type = (SCHEMA.XSD3, "long")
+ parselist = [(None, "long")]
+ seriallist = [ types.LongType ]
+
+class InegativeInteger(Integer):
+ '''Value less than zero.
+ '''
+ type = (SCHEMA.XSD3, "negativeInteger")
+ parselist = [ (None, "negativeInteger") ]
+ seriallist = [ ]
+
+class InonPositiveInteger(Integer):
+ '''Value less than or equal to zero.
+ '''
+ type = (SCHEMA.XSD3, "nonPositiveInteger")
+ parselist = [ (None, "nonPositiveInteger") ]
+ seriallist = [ ]
+
+class InonNegativeInteger(Integer):
+ '''Value greater than or equal to zero.
+ '''
+ type = (SCHEMA.XSD3, "nonNegativeInteger")
+ parselist = [ (None, "nonNegativeInteger") ]
+ seriallist = [ ]
+
+class IpositiveInteger(Integer):
+ '''Value greater than zero.
+ '''
+ type = (SCHEMA.XSD3, "positiveInteger")
+ parselist = [ (None, "positiveInteger") ]
+ seriallist = [ ]
+
+class Iinteger(Integer):
+ '''Integer value.
+ '''
+ type = (SCHEMA.XSD3, "integer")
+ parselist = [ (None, "integer") ]
+ seriallist = [ ]
+
+class IEnumeration(Integer):
+ '''Integer value, limited to a specified set of values.
+ '''
+
+ def __init__(self, choices, pname=None, **kw):
+ Integer.__init__(self, pname, **kw)
+ self.choices = choices
+ t = type(choices)
+ if t in _seqtypes:
+ self.choices = tuple(choices)
+ elif TypeCode.typechecks:
+ raise TypeError(
+ 'Enumeration choices must be list or sequence, not ' + str(t))
+ if TypeCode.typechecks:
+ for c in self.choices:
+ if type(c) not in _inttypes:
+ raise TypeError('Enumeration choice "' +
+ str(c) + '" is not an integer')
+
+ def parse(self, elt, ps):
+ val = Integer.parse(self, elt, ps)
+ if val not in self.choices:
+ raise EvaluateException('Value "' + str(val) + \
+ '" not in enumeration list',
+ ps.Backtrace(elt))
+ return val
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ if pyobj not in self.choices:
+ raise EvaluateException('Value not in int enumeration list',
+ ps.Backtrace(elt))
+ Integer.serialize(self, elt, sw, pyobj, name=name, orig=orig, **kw)
+
+
+class FPfloat(Decimal):
+ '''IEEE 32bit floating point value.
+ '''
+ type = (SCHEMA.XSD3, "float")
+ parselist = [ (None, "float") ]
+ seriallist = [ types.FloatType ]
+
+class FPdouble(Decimal):
+ '''IEEE 64bit floating point value.
+ '''
+ type = (SCHEMA.XSD3, "double")
+ parselist = [ (None, "double") ]
+ seriallist = [ ]
+
+class FPEnumeration(FPfloat):
+ '''Floating point value, limited to a specified set of values.
+ '''
+
+ def __init__(self, choices, pname=None, **kw):
+ FPfloat.__init__(self, pname, **kw)
+ self.choices = choices
+ t = type(choices)
+ if t in _seqtypes:
+ self.choices = tuple(choices)
+ elif TypeCode.typechecks:
+ raise TypeError(
+ 'Enumeration choices must be list or sequence, not ' + str(t))
+ if TypeCode.typechecks:
+ for c in self.choices:
+ if type(c) not in _floattypes:
+ raise TypeError('Enumeration choice "' +
+ str(c) + '" is not floating point number')
+
+ def parse(self, elt, ps):
+ val = Decimal.parse(self, elt, ps)
+ if val not in self.choices:
+ raise EvaluateException('Value "' + str(val) + \
+ '" not in enumeration list',
+ ps.Backtrace(elt))
+ return val
+
+ def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
+ if pyobj not in self.choices:
+ raise EvaluateException('Value not in int enumeration list',
+ ps.Backtrace(elt))
+ Decimal.serialize(self, elt, sw, pyobj, name=name, orig=orig, **kw)
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/TCtimes.py b/ZSI/zsi/ZSI/TCtimes.py
new file mode 100644
index 0000000..4c317e9
--- /dev/null
+++ b/ZSI/zsi/ZSI/TCtimes.py
@@ -0,0 +1,314 @@
+#! /usr/bin/env python
+# $Header$
+'''Typecodes for dates and times.
+'''
+
+from ZSI import _copyright, _floattypes, _inttypes, _get_idstr, EvaluateException
+from ZSI.TC import TypeCode, SimpleType
+from ZSI.wstools.Namespaces import SCHEMA
+import operator, re, time as _time
+from time import mktime as _mktime, localtime as _localtime, gmtime as _gmtime
+from datetime import tzinfo as _tzinfo, timedelta as _timedelta,\
+ datetime as _datetime
+from math import modf as _modf
+
+_niltime = [
+ 0, 0, 0, # year month day
+ 0, 0, 0, # hour minute second
+ 0, 0, 0 # weekday, julian day, dst flag
+]
+
+#### Code added to check current timezone offset
+_zero = _timedelta(0)
+_dstoffset = _stdoffset = _timedelta(seconds=-_time.timezone)
+if _time.daylight: _dstoffset = _timedelta(seconds=-_time.altzone)
+_dstdiff = _dstoffset - _stdoffset
+
+
+class _localtimezone(_tzinfo):
+ """ """
+ def dst(self, dt):
+ """datetime -> DST offset in minutes east of UTC."""
+ tt = _localtime(_mktime((dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1)))
+ if tt.tm_isdst > 0: return _dstdiff
+ return _zero
+
+ #def fromutc(...)
+ #datetime in UTC -> datetime in local time.
+
+ def tzname(self, dt):
+ """datetime -> string name of time zone."""
+ tt = _localtime(_mktime((dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1)))
+ return _time.tzname[tt.tm_isdst > 0]
+
+ def utcoffset(self, dt):
+ """datetime -> minutes east of UTC (negative for west of UTC)."""
+ tt = _localtime(_mktime((dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1)))
+ if tt.tm_isdst > 0: return _dstoffset
+ return _stdoffset
+
+class _fixedoffset(_tzinfo):
+ """Fixed offset in minutes east from UTC.
+
+ A class building tzinfo objects for fixed-offset time zones.
+ Note that _fixedoffset(0, "UTC") is a different way to build a
+ UTC tzinfo object.
+ """
+ #def __init__(self, offset, name):
+ def __init__(self, offset):
+ self.__offset = _timedelta(minutes=offset)
+ #self.__name = name
+
+ def dst(self, dt):
+ """datetime -> DST offset in minutes east of UTC."""
+ return _zero
+
+ def tzname(self, dt):
+ """datetime -> string name of time zone."""
+ #return self.__name
+ return "server"
+
+ def utcoffset(self, dt):
+ """datetime -> minutes east of UTC (negative for west of UTC)."""
+ return self.__offset
+
+
+def _dict_to_tuple(d):
+ '''Convert a dictionary to a time tuple. Depends on key values in the
+ regexp pattern!
+ '''
+ retval = _niltime[:]
+ for k,i in ( ('Y', 0), ('M', 1), ('D', 2), ('h', 3), ('m', 4), ):
+ v = d.get(k)
+ if v: retval[i] = int(v)
+
+ v = d.get('s')
+ if v:
+ msec,sec = _modf(float(v))
+ retval[6],retval[5] = int(round(msec*1000)), int(sec)
+
+ v = d.get('tz')
+ if v and v != 'Z':
+ h,m = map(int, v.split(':'))
+ # check for time zone offset, if within the same timezone,
+ # ignore offset specific calculations
+ offset=_localtimezone().utcoffset(_datetime.now())
+ local_offset_hour = offset.seconds/3600
+ local_offset_min = (offset.seconds%3600)%60
+ if local_offset_hour > 12:
+ local_offset_hour -= 24
+
+ if local_offset_hour != h or local_offset_min != m:
+ if h<0:
+ #TODO: why is this set to server
+ #foff = _fixedoffset(-((abs(h)*60+m)),"server")
+ foff = _fixedoffset(-((abs(h)*60+m)))
+ else:
+ #TODO: why is this set to server
+ #foff = _fixedoffset((abs(h)*60+m),"server")
+ foff = _fixedoffset((abs(h)*60+m))
+
+ dt = _datetime(retval[0],retval[1],retval[2],retval[3],retval[4],
+ retval[5],0,foff)
+
+ # update dict with calculated timezone
+ localdt=dt.astimezone(_localtimezone())
+ retval[0] = localdt.year
+ retval[1] = localdt.month
+ retval[2] = localdt.day
+ retval[3] = localdt.hour
+ retval[4] = localdt.minute
+ retval[5] = localdt.second
+
+ if d.get('neg', 0):
+ retval[0:5] = map(operator.__neg__, retval[0:5])
+ return tuple(retval)
+
+
+class Duration(SimpleType):
+ '''Time duration.
+ '''
+ parselist = [ (None,'duration') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)P' \
+ r'((?P<Y>\d+)Y)?' r'((?P<M>\d+)M)?' r'((?P<D>\d+)D)?' \
+ r'(?P<T>T?)' r'((?P<h>\d+)H)?' r'((?P<m>\d+)M)?' \
+ r'((?P<s>\d*(\.\d+)?)S)?' '$')
+ type = (SCHEMA.XSD3, 'duration')
+
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ if text is None:
+ return None
+ m = Duration.lex_pattern.match(text)
+ if m is None:
+ raise EvaluateException('Illegal duration', ps.Backtrace(elt))
+ d = m.groupdict()
+ if d['T'] and (d['h'] is None and d['m'] is None and d['s'] is None):
+ raise EvaluateException('Duration has T without time')
+ try:
+ retval = _dict_to_tuple(d)
+ except ValueError, e:
+ raise EvaluateException(str(e))
+
+ if self.pyclass is not None:
+ return self.pyclass(retval)
+ return retval
+
+ def get_formatted_content(self, pyobj):
+ if type(pyobj) in _floattypes or type(pyobj) in _inttypes:
+ pyobj = _gmtime(pyobj)
+
+ d = {}
+ pyobj = tuple(pyobj)
+ if 1 in map(lambda x: x < 0, pyobj[0:6]):
+ pyobj = map(abs, pyobj)
+ neg = '-'
+ else:
+ neg = ''
+
+ val = '%sP%dY%dM%dDT%dH%dM%dS' % \
+ ( neg, pyobj[0], pyobj[1], pyobj[2], pyobj[3], pyobj[4], pyobj[5])
+
+ return val
+
+
+class Gregorian(SimpleType):
+ '''Gregorian times.
+ '''
+ lex_pattern = tag = format = None
+
+ def text_to_data(self, text, elt, ps):
+ '''convert text into typecode specific data.
+ '''
+ if text is None:
+ return None
+
+ m = self.lex_pattern.match(text)
+ if not m:
+ raise EvaluateException('Bad Gregorian: %s' %text, ps.Backtrace(elt))
+ try:
+ retval = _dict_to_tuple(m.groupdict())
+ except ValueError, e:
+ #raise EvaluateException(str(e))
+ raise
+
+ if self.pyclass is not None:
+ return self.pyclass(retval)
+ return retval
+
+ def get_formatted_content(self, pyobj):
+ if type(pyobj) in _floattypes or type(pyobj) in _inttypes:
+ pyobj = _gmtime(pyobj)
+
+ d = {}
+ pyobj = tuple(pyobj)
+ if 1 in map(lambda x: x < 0, pyobj[0:6]):
+ pyobj = map(abs, pyobj)
+ d['neg'] = '-'
+ else:
+ d['neg'] = ''
+
+ ms = pyobj[6]
+ if not ms:
+ d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2],
+ 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], }
+ return self.format % d
+
+ if ms > 999:
+ raise ValueError, 'milliseconds must be a integer between 0 and 999'
+
+ d = { 'Y': pyobj[0], 'M': pyobj[1], 'D': pyobj[2],
+ 'h': pyobj[3], 'm': pyobj[4], 's': pyobj[5], 'ms':ms, }
+ return self.format_ms % d
+
+
+class gDateTime(Gregorian):
+ '''A date and time.
+ '''
+ parselist = [ (None,'dateTime') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ '(?P<Y>\d{4,})-' r'(?P<M>\d\d)-' r'(?P<D>\d\d)' 'T' \
+ r'(?P<h>\d\d):' r'(?P<m>\d\d):' r'(?P<s>\d*(\.\d+)?)' \
+ r'(?P<tz>(Z|([-+]\d\d:\d\d))?)' '$')
+ tag, format = 'dateTime', '%(Y)04d-%(M)02d-%(D)02dT%(h)02d:%(m)02d:%(s)02dZ'
+ format_ms = format[:-1] + '.%(ms)03dZ'
+ type = (SCHEMA.XSD3, 'dateTime')
+
+class gDate(Gregorian):
+ '''A date.
+ '''
+ parselist = [ (None,'date') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ '(?P<Y>\d{4,})-' r'(?P<M>\d\d)-' r'(?P<D>\d\d)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'date', '%(Y)04d-%(M)02d-%(D)02dZ'
+ type = (SCHEMA.XSD3, 'date')
+
+class gYearMonth(Gregorian):
+ '''A date.
+ '''
+ parselist = [ (None,'gYearMonth') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ '(?P<Y>\d{4,})-' r'(?P<M>\d\d)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'gYearMonth', '%(Y)04d-%(M)02dZ'
+ type = (SCHEMA.XSD3, 'gYearMonth')
+
+class gYear(Gregorian):
+ '''A date.
+ '''
+ parselist = [ (None,'gYear') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ '(?P<Y>\d{4,})' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'gYear', '%(Y)04dZ'
+ type = (SCHEMA.XSD3, 'gYear')
+
+class gMonthDay(Gregorian):
+ '''A gMonthDay.
+ '''
+ parselist = [ (None,'gMonthDay') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ r'--(?P<M>\d\d)-' r'(?P<D>\d\d)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'gMonthDay', '---%(M)02d-%(D)02dZ'
+ type = (SCHEMA.XSD3, 'gMonthDay')
+
+
+class gDay(Gregorian):
+ '''A gDay.
+ '''
+ parselist = [ (None,'gDay') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ r'---(?P<D>\d\d)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'gDay', '---%(D)02dZ'
+ type = (SCHEMA.XSD3, 'gDay')
+
+class gMonth(Gregorian):
+ '''A gMonth.
+ '''
+ parselist = [ (None,'gMonth') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ r'---(?P<M>\d\d)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'gMonth', '---%(M)02dZ'
+ type = (SCHEMA.XSD3, 'gMonth')
+
+class gTime(Gregorian):
+ '''A time.
+ '''
+ parselist = [ (None,'time') ]
+ lex_pattern = re.compile('^' r'(?P<neg>-?)' \
+ r'(?P<h>\d\d):' r'(?P<m>\d\d):' r'(?P<s>\d*(\.\d+)?)' \
+ r'(?P<tz>Z|([-+]\d\d:\d\d))?' '$')
+ tag, format = 'time', '%(h)02d:%(m)02d:%(s)02dZ'
+ format_ms = format[:-1] + '.%(ms)03dZ'
+ type = (SCHEMA.XSD3, 'time')
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/__init__.py b/ZSI/zsi/ZSI/__init__.py
new file mode 100644
index 0000000..2e13da7
--- /dev/null
+++ b/ZSI/zsi/ZSI/__init__.py
@@ -0,0 +1,433 @@
+#! /usr/bin/env python
+# $Header$
+'''ZSI: Zolera Soap Infrastructure.
+
+Copyright 2001, Zolera Systems, Inc. All Rights Reserved.
+'''
+
+_copyright = """ZSI: Zolera Soap Infrastructure.
+
+Copyright 2001, Zolera Systems, Inc. All Rights Reserved.
+Copyright 2002-2003, Rich Salz. All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or
+sell copies of the Software, and to permit persons to whom the Software
+is furnished to do so, provided that the above copyright notice(s) and
+this permission notice appear in all copies of the Software and that
+both the above copyright notice(s) and this permission notice appear in
+supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+
+Portions are also:
+
+Copyright (c) 2003, The Regents of the University of California,
+through Lawrence Berkeley National Laboratory (subject to receipt of
+any required approvals from the U.S. Dept. of Energy). All rights
+reserved. Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+(1) Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+(2) Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+(3) Neither the name of the University of California, Lawrence Berkeley
+National Laboratory, U.S. Dept. of Energy nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+You are under no obligation whatsoever to provide any bug fixes,
+patches, or upgrades to the features, functionality or performance of
+the source code ("Enhancements") to anyone; however, if you choose to
+make your Enhancements available either publicly, or directly to
+Lawrence Berkeley National Laboratory, without imposing a separate
+written license agreement for such Enhancements, then you hereby grant
+the following license: a non-exclusive, royalty-free perpetual license
+to install, use, modify, prepare derivative works, incorporate into
+other computer software, distribute, and sublicense such Enhancements
+or derivative works thereof, in binary and source code form.
+
+
+For wstools also:
+
+Zope Public License (ZPL) Version 2.0
+-----------------------------------------------
+
+This software is Copyright (c) Zope Corporation (tm) and
+Contributors. All rights reserved.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the above
+ copyright notice, this list of conditions, and the following
+ disclaimer.
+
+2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+3. The name Zope Corporation (tm) must not be used to
+ endorse or promote products derived from this software
+ without prior written permission from Zope Corporation.
+
+4. The right to distribute this software or to use it for
+ any purpose does not give you the right to use Servicemarks
+ (sm) or Trademarks (tm) of Zope Corporation. Use of them is
+ covered in a separate agreement (see
+ http://www.zope.com/Marks).
+
+5. If any files are modified, you must cause the modified
+ files to carry prominent notices stating that you changed
+ the files and the date of any change.
+
+Disclaimer
+
+ THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
+ AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGE.
+
+
+This software consists of contributions made by Zope
+Corporation and many individuals on behalf of Zope
+Corporation. Specific attributions are listed in the
+accompanying credits file.
+"""
+
+##
+## Stuff imported from elsewhere.
+from xml.dom import Node as _Node
+import types as _types
+
+##
+## Public constants.
+from ZSI.wstools.Namespaces import ZSI_SCHEMA_URI
+
+
+##
+## Not public constants.
+_inttypes = [ _types.IntType, _types.LongType ]
+_floattypes = [ _types.FloatType ]
+_seqtypes = [ _types.TupleType, _types.ListType ]
+_stringtypes = [ _types.StringType, _types.UnicodeType ]
+
+##
+## Low-level DOM oriented utilities; useful for typecode implementors.
+_attrs = lambda E: (E.attributes and E.attributes.values()) or []
+_children = lambda E: E.childNodes or []
+_child_elements = lambda E: [ n for n in (E.childNodes or [])
+ if n.nodeType == _Node.ELEMENT_NODE ]
+
+##
+## Stuff imported from elsewhere.
+from ZSI.wstools.Namespaces import SOAP as _SOAP, SCHEMA as _SCHEMA, XMLNS as _XMLNS
+
+##
+## Low-level DOM oriented utilities; useful for typecode implementors.
+_find_arraytype = lambda E: E.getAttributeNS(_SOAP.ENC, "arrayType")
+_find_encstyle = lambda E: E.getAttributeNS(_SOAP.ENV, "encodingStyle")
+try:
+ from xml.dom import EMPTY_NAMESPACE
+ _empty_nsuri_list = [ EMPTY_NAMESPACE ]
+ #if '' not in _empty_nsuri_list: __empty_nsuri_list.append('')
+ #if None not in _empty_nsuri_list: __empty_nsuri_list.append(None)
+except:
+ _empty_nsuri_list = [ None, '' ]
+def _find_attr(E, attr):
+ for nsuri in _empty_nsuri_list:
+ try:
+ v = E.getAttributeNS(nsuri, attr)
+ if v: return v
+ except: pass
+ return None
+
+def _find_attrNS(E, namespaceURI, localName):
+ '''namespaceURI
+ localName
+ '''
+ try:
+ v = E.getAttributeNS(namespaceURI, localName)
+ if v: return v
+ except: pass
+ return None
+
+
+def _find_attrNodeNS(E, namespaceURI, localName):
+ '''Must grab the attribute Node to distinquish between
+ an unspecified attribute(None) and one set to empty string("").
+ namespaceURI
+ localName
+ '''
+ attr = E.getAttributeNodeNS(namespaceURI, localName)
+ if attr is None: return None
+ try:
+ return attr.value
+ except: pass
+ return E.getAttributeNS(namespaceURI, localName)
+
+
+
+
+_find_href = lambda E: _find_attr(E, "href")
+_find_xsi_attr = lambda E, attr: \
+ E.getAttributeNS(_SCHEMA.XSI3, attr) \
+ or E.getAttributeNS(_SCHEMA.XSI1, attr) \
+ or E.getAttributeNS(_SCHEMA.XSI2, attr)
+_find_type = lambda E: _find_xsi_attr(E, "type")
+
+_find_xmlns_prefix = lambda E, attr: E.getAttributeNS(_XMLNS.BASE, attr)
+_find_default_namespace = lambda E: E.getAttributeNS(_XMLNS.BASE, None)
+
+#_textprotect = lambda s: s.replace('&', '&amp;').replace('<', '&lt;')
+
+_get_element_nsuri_name = lambda E: (E.namespaceURI, E.localName)
+
+_is_element = lambda E: E.nodeType == _Node.ELEMENT_NODE
+
+def _resolve_prefix(celt, prefix):
+ '''resolve prefix to a namespaceURI. If None or
+ empty str, return default namespace or None.
+
+ Parameters:
+ celt -- element node
+ prefix -- xmlns:prefix, or empty str or None
+ '''
+ namespace = None
+ while _is_element(celt):
+ if prefix:
+ namespaceURI = _find_xmlns_prefix(celt, prefix)
+ else:
+ namespaceURI = _find_default_namespace(celt)
+ if namespaceURI: break
+ celt = celt.parentNode
+ else:
+ if prefix:
+ raise EvaluateException, 'cant resolve xmlns:%s' %prefix
+ return namespaceURI
+
+def _valid_encoding(elt):
+ '''Does this node have a valid encoding?
+ '''
+ enc = _find_encstyle(elt)
+ if not enc or enc == _SOAP.ENC: return 1
+ for e in enc.split():
+ if e.startswith(_SOAP.ENC):
+ # XXX Is this correct? Once we find a Sec5 compatible
+ # XXX encoding, should we check that all the rest are from
+ # XXX that same base? Perhaps. But since the if test above
+ # XXX will surely get 99% of the cases, leave it for now.
+ return 1
+ return 0
+
+def _backtrace(elt, dom):
+ '''Return a "backtrace" from the given element to the DOM root,
+ in XPath syntax.
+ '''
+ s = ''
+ while elt != dom:
+ name, parent = elt.nodeName, elt.parentNode
+ if parent is None: break
+ matches = [ c for c in _child_elements(parent)
+ if c.nodeName == name ]
+ if len(matches) == 1:
+ s = '/' + name + s
+ else:
+ i = matches.index(elt) + 1
+ s = ('/%s[%d]' % (name, i)) + s
+ elt = parent
+ return s
+
+def _get_idstr(pyobj):
+ '''Python 2.3.x generates a FutureWarning for negative IDs, so
+ we use a different prefix character to ensure uniqueness, and
+ call abs() to avoid the warning.'''
+ x = id(pyobj)
+ if x < 0:
+ return 'x%x' % abs(x)
+ return 'o%x' % x
+
+
+def _get_postvalue_from_absoluteURI(url):
+ """Bug [ 1513000 ] POST Request-URI not limited to "abs_path"
+ Request-URI = "*" | absoluteURI | abs_path | authority
+
+ Not a complete solution, but it seems to work with all known
+ implementations. ValueError thrown if bad uri.
+ """
+ cache = _get_postvalue_from_absoluteURI.cache
+ path = cache.get(url, '')
+ if not path:
+ scheme,authpath = url.split('://')
+ s = authpath.split('/', 1)
+ if len(s) == 2: path = '/%s' %s[1]
+ if len(cache) > _get_postvalue_from_absoluteURI.MAXLEN:cache.clear()
+ cache[url] = path
+ return path
+_get_postvalue_from_absoluteURI.cache = {}
+_get_postvalue_from_absoluteURI.MAXLEN = 20
+
+
+##
+## Exception classes.
+class ZSIException(Exception):
+ '''Base class for all ZSI exceptions.
+ '''
+ pass
+
+class ParseException(ZSIException):
+ '''Exception raised during parsing.
+ '''
+
+ def __init__(self, str, inheader, elt=None, dom=None):
+ Exception.__init__(self)
+ self.str, self.inheader, self.trace = str, inheader, None
+ if elt and dom:
+ self.trace = _backtrace(elt, dom)
+
+ def __str__(self):
+ if self.trace:
+ return self.str + '\n[Element trace: ' + self.trace + ']'
+ return self.str
+
+ def __repr__(self):
+ return "<%s.ParseException %s>" % (__name__, _get_idstr(self))
+
+
+class EvaluateException(ZSIException):
+ '''Exception raised during data evaluation (serialization).
+ '''
+
+ def __init__(self, str, trace=None):
+ Exception.__init__(self)
+ self.str, self.trace = str, trace
+
+ def __str__(self):
+ if self.trace:
+ return self.str + '\n[Element trace: ' + self.trace + ']'
+ return self.str
+
+ def __repr__(self):
+ return "<%s.EvaluateException %s>" % (__name__, _get_idstr(self))
+
+class FaultException(ZSIException):
+ '''Exception raised when a fault is received.
+ '''
+
+ def __init__(self, fault):
+ self.fault = fault
+
+ def __str__(self):
+ return str(self.fault)
+
+ def __repr__(self):
+ return "<%s.FaultException %s>" % (__name__, _get_idstr(self))
+
+class WSActionException(ZSIException):
+ '''Exception raised when WS-Address Action Header is incorrectly
+ specified when received by client or server.
+ '''
+ pass
+
+##
+## Importing the rest of ZSI.
+import version
+def Version():
+ return version.Version
+
+from writer import SoapWriter
+from parse import ParsedSoap
+from fault import Fault, \
+ FaultFromActor, FaultFromException, FaultFromFaultMessage, \
+ FaultFromNotUnderstood, FaultFromZSIException
+import TC
+TC.RegisterType(TC.String, minOccurs=0, nillable=False)
+TC.RegisterType(TC.URI, minOccurs=0, nillable=False)
+TC.RegisterType(TC.Base64String, minOccurs=0, nillable=False)
+TC.RegisterType(TC.HexBinaryString, minOccurs=0, nillable=False)
+
+#TC.RegisterType(TC.Integer)
+#TC.RegisterType(TC.Decimal)
+for pyclass in (TC.IunsignedByte, TC.IunsignedShort, TC.IunsignedInt, TC.IunsignedLong,
+ TC.Ibyte, TC.Ishort, TC.Iint, TC.Ilong, TC.InegativeInteger,
+ TC.InonPositiveInteger, TC.InonNegativeInteger, TC.IpositiveInteger,
+ TC.Iinteger, TC.FPfloat, TC.FPdouble, ):
+
+ TC.RegisterType(pyclass, minOccurs=0, nillable=False)
+
+TC.RegisterType(TC.Boolean, minOccurs=0, nillable=False)
+TC.RegisterType(TC.Duration, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gDateTime, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gDate, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gYearMonth, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gYear, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gMonthDay, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gDay, minOccurs=0, nillable=False)
+TC.RegisterType(TC.gTime, minOccurs=0, nillable=False)
+TC.RegisterType(TC.Apache.Map, minOccurs=0, nillable=False)
+
+##
+## Register Wrappers for builtin types.
+## TC.AnyElement wraps builtins so element name information can be saved
+##
+import schema
+for i in [int,float,str,tuple,list,unicode]:
+ schema._GetPyobjWrapper.RegisterBuiltin(i)
+
+## Load up Wrappers for builtin types
+schema.RegisterAnyElement()
+
+
+#try:
+# from ServiceProxy import *
+#except:
+# pass
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/address.py b/ZSI/zsi/ZSI/address.py
new file mode 100644
index 0000000..98f9884
--- /dev/null
+++ b/ZSI/zsi/ZSI/address.py
@@ -0,0 +1,331 @@
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+###########################################################################
+
+import time, urlparse, socket
+from ZSI import _seqtypes, EvaluateException, WSActionException
+from TC import AnyElement, AnyType, TypeCode
+from schema import GED, GTD, _has_type_definition
+from ZSI.TCcompound import ComplexType
+from ZSI.wstools.Namespaces import WSA_LIST
+
+
+class Address(object):
+ '''WS-Address
+
+ Implemented is dependent on the default "wsdl2py" convention of generating aname,
+ so the attributes representing element declaration names should be prefixed with
+ an underscore.
+ '''
+ def __init__(self, addressTo=None, wsAddressURI=None, action=None):
+ self.wsAddressURI = wsAddressURI
+ self.anonymousURI = None
+ self._addressTo = addressTo
+ self._messageID = None
+ self._action = action
+ self._endPointReference = None
+ self._replyTo = None
+ self._relatesTo = None
+ self.setUp()
+
+ def setUp(self):
+ '''Look for WS-Address
+ '''
+ toplist = filter(lambda wsa: wsa.ADDRESS==self.wsAddressURI, WSA_LIST)
+ epr = 'EndpointReferenceType'
+ for WSA in toplist+WSA_LIST:
+ if (self.wsAddressURI is not None and self.wsAddressURI != WSA.ADDRESS) or \
+ _has_type_definition(WSA.ADDRESS, epr) is True:
+ break
+ else:
+ raise EvaluateException,\
+ 'enabling wsAddressing requires the inclusion of that namespace'
+
+ self.wsAddressURI = WSA.ADDRESS
+ self.anonymousURI = WSA.ANONYMOUS
+ self._replyTo = WSA.ANONYMOUS
+
+ def _checkAction(self, action, value):
+ '''WS-Address Action
+ action -- Action value expecting.
+ value -- Action value server returned.
+ '''
+ if action is None:
+ raise WSActionException, 'User did not specify WSAddress Action value to expect'
+ if not value:
+ raise WSActionException, 'missing WSAddress Action, expecting %s' %action
+ if value != action:
+ raise WSActionException, 'wrong WSAddress Action(%s), expecting %s'%(value,action)
+
+ def _checkFrom(self, pyobj):
+ '''WS-Address From,
+ XXX currently not checking the hostname, not forwarding messages.
+ pyobj -- From server returned.
+ '''
+ if pyobj is None: return
+ value = pyobj._Address
+ if value != self._addressTo:
+ scheme,netloc,path,query,fragment = urlparse.urlsplit(value)
+ hostport = netloc.split(':')
+ schemeF,netlocF,pathF,queryF,fragmentF = urlparse.urlsplit(self._addressTo)
+ if scheme==schemeF and path==pathF and query==queryF and fragment==fragmentF:
+ netloc = netloc.split(':') + ['80']
+ netlocF = netlocF.split(':') + ['80']
+ if netloc[1]==netlocF[1] and \
+ socket.gethostbyname(netloc[0])==socket.gethostbyname(netlocF[0]):
+ return
+
+ raise WSActionException, 'wrong WS-Address From(%s), expecting %s'%(value,self._addressTo)
+
+ def _checkRelatesTo(self, value):
+ '''WS-Address From
+ value -- From server returned.
+ '''
+ if value != self._messageID:
+ raise WSActionException, 'wrong WS-Address RelatesTo(%s), expecting %s'%(value,self._messageID)
+
+ def _checkReplyTo(self, value):
+ '''WS-Address From
+ value -- From server returned in wsa:To
+ '''
+ if value != self._replyTo:
+ raise WSActionException, 'wrong WS-Address ReplyTo(%s), expecting %s'%(value,self._replyTo)
+
+ def setAction(self, action):
+ self._action = action
+
+ def getAction(self):
+ return self._action
+
+ def getRelatesTo(self):
+ return self._relatesTo
+
+ def getMessageID(self):
+ return self._messageID
+
+ def _getWSAddressTypeCodes(self, **kw):
+ '''kw -- namespaceURI keys with sequence of element names.
+ '''
+ typecodes = []
+ try:
+ for nsuri,elements in kw.items():
+ for el in elements:
+ typecode = GED(nsuri, el)
+ if typecode is None:
+ raise WSActionException, 'Missing namespace, import "%s"' %nsuri
+
+ typecodes.append(typecode)
+ else:
+ pass
+ except EvaluateException, ex:
+ raise EvaluateException, \
+ 'To use ws-addressing register typecodes for namespace(%s)' %self.wsAddressURI
+ return typecodes
+
+ def checkResponse(self, ps, action):
+ '''
+ ps -- ParsedSoap
+ action -- ws-action for response
+ '''
+ namespaceURI = self.wsAddressURI
+ d = {namespaceURI:("MessageID","Action","To","From","RelatesTo")}
+ typecodes = self._getWSAddressTypeCodes(**d)
+ pyobjs = ps.ParseHeaderElements(typecodes)
+
+ got_action = pyobjs.get((namespaceURI,"Action"))
+ self._checkAction(got_action, action)
+
+ From = pyobjs.get((namespaceURI,"From"))
+ self._checkFrom(From)
+
+ RelatesTo = pyobjs.get((namespaceURI,"RelatesTo"))
+ self._checkRelatesTo(RelatesTo)
+
+ To = pyobjs.get((namespaceURI,"To"))
+ if To: self._checkReplyTo(To)
+
+ def setRequest(self, endPointReference, action):
+ '''Call For Request
+ '''
+ self._action = action
+ self.header_pyobjs = None
+ pyobjs = []
+ namespaceURI = self.wsAddressURI
+ addressTo = self._addressTo
+ messageID = self._messageID = "uuid:%s" %time.time()
+
+ # Set Message Information Headers
+ # MessageID
+ typecode = GED(namespaceURI, "MessageID")
+ pyobjs.append(typecode.pyclass(messageID))
+
+ # Action
+ typecode = GED(namespaceURI, "Action")
+ pyobjs.append(typecode.pyclass(action))
+
+ # To
+ typecode = GED(namespaceURI, "To")
+ pyobjs.append(typecode.pyclass(addressTo))
+
+ # From
+ typecode = GED(namespaceURI, "From")
+ mihFrom = typecode.pyclass()
+ mihFrom._Address = self.anonymousURI
+ pyobjs.append(mihFrom)
+
+ if endPointReference:
+ if hasattr(endPointReference, 'typecode') is False:
+ raise EvaluateException, 'endPointReference must have a typecode attribute'
+
+ if isinstance(endPointReference.typecode, \
+ GTD(namespaceURI ,'EndpointReferenceType')) is False:
+ raise EvaluateException, 'endPointReference must be of type %s' \
+ %GTD(namespaceURI ,'EndpointReferenceType')
+
+ ReferenceProperties = endPointReference._ReferenceProperties
+ any = ReferenceProperties._any or []
+ #if not (what.maxOccurs=='unbounded' and type(any) in _seqtypes):
+ # raise EvaluateException, 'ReferenceProperties <any> assumed maxOccurs unbounded'
+
+ for v in any:
+ if not hasattr(v,'typecode'):
+ raise EvaluateException, '<any> element, instance missing typecode attribute'
+
+ pyobjs.append(v)
+
+ #pyobjs.append(v)
+
+ self.header_pyobjs = tuple(pyobjs)
+
+ def setResponseFromWSAddress(self, address, localURL):
+ '''Server-side has to set these fields in response.
+ address -- Address instance, representing a WS-Address
+ '''
+ self.From = localURL
+ self.header_pyobjs = None
+ pyobjs = []
+ namespaceURI = self.wsAddressURI
+
+ for nsuri,name,value in (\
+ (namespaceURI, "Action", self._action),
+ (namespaceURI, "MessageID","uuid:%s" %time.time()),
+ (namespaceURI, "RelatesTo", address.getMessageID()),
+ (namespaceURI, "To", self.anonymousURI),):
+
+ typecode = GED(nsuri, name)
+ pyobjs.append(typecode.pyclass(value))
+
+ typecode = GED(nsuri, "From")
+ pyobj = typecode.pyclass()
+ pyobj._Address = self.From
+ pyobjs.append(pyobj)
+ self.header_pyobjs = tuple(pyobjs)
+
+
+ def serialize(self, sw, **kw):
+ '''
+ sw -- SoapWriter instance, add WS-Address header.
+ '''
+ for pyobj in self.header_pyobjs:
+ if hasattr(pyobj, 'typecode') is False:
+ raise RuntimeError, 'all header pyobjs must have a typecode attribute'
+
+ sw.serialize_header(pyobj, **kw)
+
+
+ def parse(self, ps, **kw):
+ '''
+ ps -- ParsedSoap instance
+ '''
+ namespaceURI = self.wsAddressURI
+ elements = ("MessageID","Action","To","From","RelatesTo")
+ d = {namespaceURI:elements}
+ typecodes = self._getWSAddressTypeCodes(**d)
+ pyobjs = ps.ParseHeaderElements(typecodes)
+ self._messageID = pyobjs[(namespaceURI,elements[0])]
+ self._action = pyobjs[(namespaceURI,elements[1])]
+ self._addressTo = pyobjs[(namespaceURI,elements[2])]
+ self._from = pyobjs[(namespaceURI,elements[3])]
+ self._relatesTo = pyobjs[(namespaceURI,elements[4])]
+
+# TODO: Remove MessageContainer. Hopefully the new <any> functionality
+# makes this irrelevant. But could create an Interop problem.
+"""
+class MessageContainer:
+ '''Automatically wraps all primitive types so attributes
+ can be specified.
+ '''
+ class IntHolder(int): pass
+ class StrHolder(str): pass
+ class UnicodeHolder(unicode): pass
+ class LongHolder(long): pass
+ class FloatHolder(float): pass
+ class BoolHolder(int): pass
+ #class TupleHolder(tuple): pass
+ #class ListHolder(list): pass
+ typecode = None
+ pyclass_list = [IntHolder,StrHolder,UnicodeHolder,LongHolder,
+ FloatHolder,BoolHolder,]
+
+ def __setattr__(self, key, value):
+ '''wrap all primitives with Holders if present.
+ '''
+ if type(value) in _seqtypes:
+ value = list(value)
+ for indx in range(len(value)):
+ try:
+ item = self._wrap(value[indx])
+ except TypeError, ex:
+ pass
+ else:
+ value[indx] = item
+ elif type(value) is bool:
+ value = BoolHolder(value)
+ else:
+ try:
+ value = self._wrap(value)
+ except TypeError, ex:
+ pass
+ self.__dict__[key] = value
+
+ def _wrap(self, value):
+ '''wrap primitive, return None
+ '''
+ if value is None:
+ return value
+ for pyclass in self.pyclass_list:
+ if issubclass(value.__class__, pyclass): break
+ else:
+ raise TypeError, 'MessageContainer does not know about type %s' %(type(value))
+ return pyclass(value)
+
+ def setUp(self, typecode=None, pyclass=None):
+ '''set up all attribute names (aname) in this python instance.
+ If what is a ComplexType or a simpleType w/attributes instantiate
+ a new MessageContainer, else set attribute aname to None.
+ '''
+ if typecode is None:
+ typecode = self.typecode
+ else:
+ self.typecode = typecode
+
+ if not isinstance(typecode, TypeCode):
+ raise TypeError, 'typecode must be a TypeCode class instance'
+
+ if isinstance(typecode, ComplexType):
+ if typecode.has_attributes() is True:
+ setattr(self, typecode.attrs_aname, {})
+ if typecode.mixed is True:
+ setattr(self, typecode.mixed_aname, None)
+ for what in typecode.ofwhat:
+ setattr(self, what.aname, None)
+ if isinstance(what, ComplexType):
+ setattr(self, what.aname, MessageContainer())
+ getattr(self, what.aname).setUp(typecode=what)
+ else:
+ raise TypeError, 'Primitive type'
+"""
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/auth.py b/ZSI/zsi/ZSI/auth.py
new file mode 100644
index 0000000..966b6b0
--- /dev/null
+++ b/ZSI/zsi/ZSI/auth.py
@@ -0,0 +1,65 @@
+#! /usr/bin/env python
+# $Header$
+'''Simple CGI dispatching.
+'''
+
+from ZSI import *
+from ZSI import _copyright
+import base64, os
+
+_b64_decode = base64.decodestring
+
+# Typecode to parse a ZSI BasicAuth header.
+_auth_tc = TC.Struct(None,
+ [ TC.String('Name'), TC.String('Password') ],
+ extras=1)
+
+class AUTH:
+ '''Constants for authentication mechanisms.
+ '''
+ none = 0
+ httpbasic = 1
+ zsibasic = 2
+ httpdigest = 4
+
+class ClientBinding:
+ '''Information about the client that is connected to us.
+ '''
+
+ def __init__(self, ps):
+ self.ps, self.auth = \
+ ps, None
+ self.environ = os.environ.copy()
+ self.environ['CONTENT_LENGTH'] = str(0)
+
+ def GetAuth(self):
+ '''Return a tuple containing client authentication data.
+ '''
+ if self.auth: return self.auth
+ for elt in self.ps.GetMyHeaderElements():
+ if elt.localName == 'BasicAuth' \
+ and elt.namespaceURI == ZSI_SCHEMA_URI:
+ d = _auth_tc.parse(elt, self.ps)
+ self.auth = (AUTH.zsibasic, d['Name'], d['Password'])
+ return self.auth
+ ba = self.environ.get('HTTP_AUTHENTICATION')
+ if ba:
+ ba = ba.split(' ')
+ if len(ba) == 2 and ba[0].lower() == 'basic':
+ ba = _b64_decode(ba[1])
+ self.auth = (AUTH.httpbasic,) + tuple(ba.split(':'))
+ return self.auth
+ self.auth = (AUTH.none,)
+ return self.auth
+
+ def GetNS(self):
+ '''Return namespace for the top main request element.
+ '''
+ return self.ps.body_root.namespaceURI or ''
+
+ def GetRequest(self):
+ '''Return the ParsedSoap request.
+ '''
+ return self.ps
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/client.py b/ZSI/zsi/ZSI/client.py
new file mode 100755
index 0000000..7b3a615
--- /dev/null
+++ b/ZSI/zsi/ZSI/client.py
@@ -0,0 +1,541 @@
+#! /usr/bin/env python
+# $Header$
+#
+# Copyright (c) 2001 Zolera Systems. All rights reserved.
+
+from ZSI import _copyright, _seqtypes, ParsedSoap, SoapWriter, TC, ZSI_SCHEMA_URI,\
+ EvaluateException, FaultFromFaultMessage, _child_elements, _attrs, _find_arraytype,\
+ _find_type, _get_idstr, _get_postvalue_from_absoluteURI, FaultException, WSActionException
+from ZSI.auth import AUTH
+from ZSI.TC import AnyElement, AnyType, String, TypeCode, _get_global_element_declaration,\
+ _get_type_definition
+from ZSI.TCcompound import Struct
+import base64, httplib, Cookie, types, time, urlparse
+from ZSI.address import Address
+from ZSI.wstools.logging import getLogger as _GetLogger
+_b64_encode = base64.encodestring
+
+class _AuthHeader:
+ """<BasicAuth xmlns="ZSI_SCHEMA_URI">
+ <Name>%s</Name><Password>%s</Password>
+ </BasicAuth>
+ """
+ def __init__(self, name=None, password=None):
+ self.Name = name
+ self.Password = password
+_AuthHeader.typecode = Struct(_AuthHeader, ofwhat=(String((ZSI_SCHEMA_URI,'Name'), typed=False),
+ String((ZSI_SCHEMA_URI,'Password'), typed=False)), pname=(ZSI_SCHEMA_URI,'BasicAuth'),
+ typed=False)
+
+
+class _Caller:
+ '''Internal class used to give the user a callable object
+ that calls back to the Binding object to make an RPC call.
+ '''
+
+ def __init__(self, binding, name):
+ self.binding, self.name = binding, name
+
+ def __call__(self, *args):
+ return self.binding.RPC(None, self.name, args,
+ encodingStyle="http://schemas.xmlsoap.org/soap/encoding/",
+ replytype=TC.Any(self.name+"Response"))
+
+
+class _NamedParamCaller:
+ '''Similar to _Caller, expect that there are named parameters
+ not positional.
+ '''
+
+ def __init__(self, binding, name):
+ self.binding, self.name = binding, name
+
+ def __call__(self, **params):
+ # Pull out arguments that Send() uses
+ kw = { }
+ for key in [ 'auth_header', 'nsdict', 'requesttypecode' 'soapaction' ]:
+ if params.has_key(key):
+ kw[key] = params[key]
+ del params[key]
+ return self.binding.RPC(None, self.name, None,
+ encodingStyle="http://schemas.xmlsoap.org/soap/encoding/",
+ _args=params,
+ replytype=TC.Any(self.name+"Response", aslist=False),
+ **kw)
+
+
+class _Binding:
+ '''Object that represents a binding (connection) to a SOAP server.
+ Once the binding is created, various ways of sending and
+ receiving SOAP messages are available.
+ '''
+ defaultHttpTransport = httplib.HTTPConnection
+ defaultHttpsTransport = httplib.HTTPSConnection
+ logger = _GetLogger('ZSI.client.Binding')
+
+ def __init__(self, nsdict=None, transport=None, url=None, tracefile=None,
+ readerclass=None, writerclass=None, soapaction='',
+ wsAddressURI=None, sig_handler=None, transdict=None, **kw):
+ '''Initialize.
+ Keyword arguments include:
+ transport -- default use HTTPConnection.
+ transdict -- dict of values to pass to transport.
+ url -- URL of resource, POST is path
+ soapaction -- value of SOAPAction header
+ auth -- (type, name, password) triplet; default is unauth
+ nsdict -- namespace entries to add
+ tracefile -- file to dump packet traces
+ cert_file, key_file -- SSL data (q.v.)
+ readerclass -- DOM reader class
+ writerclass -- DOM writer class, implements MessageInterface
+ wsAddressURI -- namespaceURI of WS-Address to use. By default
+ it's not used.
+ sig_handler -- XML Signature handler, must sign and verify.
+ endPointReference -- optional Endpoint Reference.
+ '''
+ self.data = None
+ self.ps = None
+ self.user_headers = []
+ self.nsdict = nsdict or {}
+ self.transport = transport
+ self.transdict = transdict or {}
+ self.url = url
+ self.trace = tracefile
+ self.readerclass = readerclass
+ self.writerclass = writerclass
+ self.soapaction = soapaction
+ self.wsAddressURI = wsAddressURI
+ self.sig_handler = sig_handler
+ self.address = None
+ self.endPointReference = kw.get('endPointReference', None)
+ self.cookies = Cookie.SimpleCookie()
+ self.http_callbacks = {}
+
+ if kw.has_key('auth'):
+ self.SetAuth(*kw['auth'])
+ else:
+ self.SetAuth(AUTH.none)
+
+ def SetAuth(self, style, user=None, password=None):
+ '''Change auth style, return object to user.
+ '''
+ self.auth_style, self.auth_user, self.auth_pass = \
+ style, user, password
+ return self
+
+ def SetURL(self, url):
+ '''Set the URL we post to.
+ '''
+ self.url = url
+ return self
+
+ def ResetHeaders(self):
+ '''Empty the list of additional headers.
+ '''
+ self.user_headers = []
+ return self
+
+ def ResetCookies(self):
+ '''Empty the list of cookies.
+ '''
+ self.cookies = Cookie.SimpleCookie()
+
+ def AddHeader(self, header, value):
+ '''Add a header to send.
+ '''
+ self.user_headers.append((header, value))
+ return self
+
+ def __addcookies(self):
+ '''Add cookies from self.cookies to request in self.h
+ '''
+ for cname, morsel in self.cookies.items():
+ attrs = []
+ value = morsel.get('version', '')
+ if value != '' and value != '0':
+ attrs.append('$Version=%s' % value)
+ attrs.append('%s=%s' % (cname, morsel.coded_value))
+ value = morsel.get('path')
+ if value:
+ attrs.append('$Path=%s' % value)
+ value = morsel.get('domain')
+ if value:
+ attrs.append('$Domain=%s' % value)
+ self.h.putheader('Cookie', "; ".join(attrs))
+
+ def RPC(self, url, opname, obj, replytype=None, **kw):
+ '''Send a request, return the reply. See Send() and Recieve()
+ docstrings for details.
+ '''
+ self.Send(url, opname, obj, **kw)
+ return self.Receive(replytype, **kw)
+
+ def Send(self, url, opname, obj, nsdict={}, soapaction=None, wsaction=None,
+ endPointReference=None, **kw):
+ '''Send a message. If url is None, use the value from the
+ constructor (else error). obj is the object (data) to send.
+ Data may be described with a requesttypecode keyword, the default
+ is the class's typecode (if there is one), else Any.
+
+ Try to serialize as a Struct, if this is not possible serialize an Array. If
+ data is a sequence of built-in python data types, it will be serialized as an
+ Array, unless requesttypecode is specified.
+
+ arguments:
+ url --
+ opname -- struct wrapper
+ obj -- python instance
+
+ key word arguments:
+ nsdict --
+ soapaction --
+ wsaction -- WS-Address Action, goes in SOAP Header.
+ endPointReference -- set by calling party, must be an
+ EndPointReference type instance.
+ requesttypecode --
+
+ '''
+ url = url or self.url
+ endPointReference = endPointReference or self.endPointReference
+
+ # Serialize the object.
+ d = {}
+ d.update(self.nsdict)
+ d.update(nsdict)
+
+ sw = SoapWriter(nsdict=d, header=True, outputclass=self.writerclass,
+ encodingStyle=kw.get('encodingStyle'),)
+
+ requesttypecode = kw.get('requesttypecode')
+ if kw.has_key('_args'): #NamedParamBinding
+ tc = requesttypecode or TC.Any(pname=opname, aslist=False)
+ sw.serialize(kw['_args'], tc)
+ elif not requesttypecode:
+ tc = getattr(obj, 'typecode', None) or TC.Any(pname=opname, aslist=False)
+ try:
+ if type(obj) in _seqtypes:
+ obj = dict(map(lambda i: (i.typecode.pname,i), obj))
+ except AttributeError:
+ # can't do anything but serialize this in a SOAP:Array
+ tc = TC.Any(pname=opname, aslist=True)
+ else:
+ tc = TC.Any(pname=opname, aslist=False)
+
+ sw.serialize(obj, tc)
+ else:
+ sw.serialize(obj, requesttypecode)
+
+ #
+ # Determine the SOAP auth element. SOAP:Header element
+ if self.auth_style & AUTH.zsibasic:
+ sw.serialize_header(_AuthHeader(self.auth_user, self.auth_pass),
+ _AuthHeader.typecode)
+
+ #
+ # Serialize WS-Address
+ if self.wsAddressURI is not None:
+ if self.soapaction and wsaction.strip('\'"') != self.soapaction:
+ raise WSActionException, 'soapAction(%s) and WS-Action(%s) must match'\
+ %(self.soapaction,wsaction)
+
+ self.address = Address(url, self.wsAddressURI)
+ self.address.setRequest(endPointReference, wsaction)
+ self.address.serialize(sw)
+
+ #
+ # WS-Security Signature Handler
+ if self.sig_handler is not None:
+ self.sig_handler.sign(sw)
+
+ scheme,netloc,path,nil,nil,nil = urlparse.urlparse(url)
+ transport = self.transport
+ if transport is None and url is not None:
+ if scheme == 'https':
+ transport = self.defaultHttpsTransport
+ elif scheme == 'http':
+ transport = self.defaultHttpTransport
+ else:
+ raise RuntimeError, 'must specify transport or url startswith https/http'
+
+ # Send the request.
+ if issubclass(transport, httplib.HTTPConnection) is False:
+ raise TypeError, 'transport must be a HTTPConnection'
+
+ soapdata = str(sw)
+ self.h = transport(netloc, None, **self.transdict)
+ self.h.connect()
+ self.SendSOAPData(soapdata, url, soapaction, **kw)
+
+ def SendSOAPData(self, soapdata, url, soapaction, headers={}, **kw):
+ # Tracing?
+ if self.trace:
+ print >>self.trace, "_" * 33, time.ctime(time.time()), "REQUEST:"
+ print >>self.trace, soapdata
+
+ #scheme,netloc,path,nil,nil,nil = urlparse.urlparse(url)
+ path = _get_postvalue_from_absoluteURI(url)
+ self.h.putrequest("POST", path)
+ self.h.putheader("Content-length", "%d" % len(soapdata))
+ self.h.putheader("Content-type", 'text/xml; charset=utf-8')
+ self.__addcookies()
+
+ for header,value in headers.items():
+ self.h.putheader(header, value)
+
+ SOAPActionValue = '"%s"' % (soapaction or self.soapaction)
+ self.h.putheader("SOAPAction", SOAPActionValue)
+ if self.auth_style & AUTH.httpbasic:
+ val = _b64_encode(self.auth_user + ':' + self.auth_pass) \
+ .replace("\012", "")
+ self.h.putheader('Authorization', 'Basic ' + val)
+ elif self.auth_style == AUTH.httpdigest and not headers.has_key('Authorization') \
+ and not headers.has_key('Expect'):
+ def digest_auth_cb(response):
+ self.SendSOAPDataHTTPDigestAuth(response, soapdata, url, soapaction, **kw)
+ self.http_callbacks[401] = None
+ self.http_callbacks[401] = digest_auth_cb
+
+ for header,value in self.user_headers:
+ self.h.putheader(header, value)
+ self.h.endheaders()
+ self.h.send(soapdata)
+
+ # Clear prior receive state.
+ self.data, self.ps = None, None
+
+ def SendSOAPDataHTTPDigestAuth(self, response, soapdata, url, soapaction, **kw):
+ '''Resend the initial request w/http digest authorization headers.
+ The SOAP server has requested authorization. Fetch the challenge,
+ generate the authdict for building a response.
+ '''
+ if self.trace:
+ print >>self.trace, "------ Digest Auth Header"
+ url = url or self.url
+ if response.status != 401:
+ raise RuntimeError, 'Expecting HTTP 401 response.'
+ if self.auth_style != AUTH.httpdigest:
+ raise RuntimeError,\
+ 'Auth style(%d) does not support requested digest authorization.' %self.auth_style
+
+ from ZSI.digest_auth import fetch_challenge,\
+ generate_response,\
+ build_authorization_arg,\
+ dict_fetch
+
+ chaldict = fetch_challenge( response.getheader('www-authenticate') )
+ if dict_fetch(chaldict,'challenge','').lower() == 'digest' and \
+ dict_fetch(chaldict,'nonce',None) and \
+ dict_fetch(chaldict,'realm',None) and \
+ dict_fetch(chaldict,'qop',None):
+ authdict = generate_response(chaldict,
+ url, self.auth_user, self.auth_pass, method='POST')
+ headers = {\
+ 'Authorization':build_authorization_arg(authdict),
+ 'Expect':'100-continue',
+ }
+ self.SendSOAPData(soapdata, url, soapaction, headers, **kw)
+ return
+
+ raise RuntimeError,\
+ 'Client expecting digest authorization challenge.'
+
+ def ReceiveRaw(self, **kw):
+ '''Read a server reply, unconverted to any format and return it.
+ '''
+ if self.data: return self.data
+ trace = self.trace
+ while 1:
+ response = self.h.getresponse()
+ self.reply_code, self.reply_msg, self.reply_headers, self.data = \
+ response.status, response.reason, response.msg, response.read()
+ if trace:
+ print >>trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
+ for i in (self.reply_code, self.reply_msg,):
+ print >>trace, str(i)
+ print >>trace, "-------"
+ print >>trace, str(self.reply_headers)
+ print >>trace, self.data
+ saved = None
+ for d in response.msg.getallmatchingheaders('set-cookie'):
+ if d[0] in [ ' ', '\t' ]:
+ saved += d.strip()
+ else:
+ if saved: self.cookies.load(saved)
+ saved = d.strip()
+ if saved: self.cookies.load(saved)
+ if response.status == 401:
+ if not callable(self.http_callbacks.get(response.status,None)):
+ raise RuntimeError, 'HTTP Digest Authorization Failed'
+ self.http_callbacks[response.status](response)
+ continue
+ if response.status != 100: break
+
+ # The httplib doesn't understand the HTTP continuation header.
+ # Horrible internals hack to patch things up.
+ self.h._HTTPConnection__state = httplib._CS_REQ_SENT
+ self.h._HTTPConnection__response = None
+ return self.data
+
+ def IsSOAP(self):
+ if self.ps: return 1
+ self.ReceiveRaw()
+ mimetype = self.reply_headers.type
+ return mimetype == 'text/xml'
+
+ def ReceiveSOAP(self, readerclass=None, **kw):
+ '''Get back a SOAP message.
+ '''
+ if self.ps: return self.ps
+ if not self.IsSOAP():
+ raise TypeError(
+ 'Response is "%s", not "text/xml"' % self.reply_headers.type)
+ if len(self.data) == 0:
+ raise TypeError('Received empty response')
+
+ self.ps = ParsedSoap(self.data,
+ readerclass=readerclass or self.readerclass,
+ encodingStyle=kw.get('encodingStyle'))
+
+ if self.sig_handler is not None:
+ self.sig_handler.verify(self.ps)
+
+ return self.ps
+
+ def IsAFault(self):
+ '''Get a SOAP message, see if it has a fault.
+ '''
+ self.ReceiveSOAP()
+ return self.ps.IsAFault()
+
+ def ReceiveFault(self, **kw):
+ '''Parse incoming message as a fault. Raise TypeError if no
+ fault found.
+ '''
+ self.ReceiveSOAP(**kw)
+ if not self.ps.IsAFault():
+ raise TypeError("Expected SOAP Fault not found")
+ return FaultFromFaultMessage(self.ps)
+
+ def Receive(self, replytype, **kw):
+ '''Parse message, create Python object.
+
+ KeyWord data:
+ faults -- list of WSDL operation.fault typecodes
+ wsaction -- If using WS-Address, must specify Action value we expect to
+ receive.
+ '''
+ self.ReceiveSOAP(**kw)
+ if self.ps.IsAFault():
+ msg = FaultFromFaultMessage(self.ps)
+ raise FaultException(msg)
+
+ tc = replytype
+ if hasattr(replytype, 'typecode'):
+ tc = replytype.typecode
+
+ reply = self.ps.Parse(tc)
+ if self.address is not None:
+ self.address.checkResponse(self.ps, kw.get('wsaction'))
+ return reply
+
+ def __repr__(self):
+ return "<%s instance %s>" % (self.__class__.__name__, _get_idstr(self))
+
+
+class Binding(_Binding):
+ '''Object that represents a binding (connection) to a SOAP server.
+ Can be used in the "name overloading" style.
+
+ class attr:
+ gettypecode -- funcion that returns typecode from typesmodule,
+ can be set so can use whatever mapping you desire.
+ '''
+ gettypecode = staticmethod(lambda mod,e: getattr(mod, str(e.localName)).typecode)
+ logger = _GetLogger('ZSI.client.Binding')
+
+ def __init__(self, typesmodule=None, **kw):
+ self.typesmodule = typesmodule
+ _Binding.__init__(self, **kw)
+
+ def __getattr__(self, name):
+ '''Return a callable object that will invoke the RPC method
+ named by the attribute.
+ '''
+ if name[:2] == '__' and len(name) > 5 and name[-2:] == '__':
+ if hasattr(self, name): return getattr(self, name)
+ return getattr(self.__class__, name)
+ return _Caller(self, name)
+
+ def __parse_child(self, node):
+ '''for rpc-style map each message part to a class in typesmodule
+ '''
+ try:
+ tc = self.gettypecode(self.typesmodule, node)
+ except:
+ self.logger.debug('didnt find typecode for "%s" in typesmodule: %s',
+ node.localName, self.typesmodule)
+ tc = TC.Any(aslist=1)
+ return tc.parse(node, self.ps)
+
+ self.logger.debug('parse child with typecode : %s', tc)
+ try:
+ return tc.parse(node, self.ps)
+ except Exception:
+ self.logger.debug('parse failed try Any : %s', tc)
+
+ tc = TC.Any(aslist=1)
+ return tc.parse(node, self.ps)
+
+ def Receive(self, replytype, **kw):
+ '''Parse message, create Python object.
+
+ KeyWord data:
+ faults -- list of WSDL operation.fault typecodes
+ wsaction -- If using WS-Address, must specify Action value we expect to
+ receive.
+ '''
+ self.ReceiveSOAP(**kw)
+ ps = self.ps
+ tp = _find_type(ps.body_root)
+ isarray = ((type(tp) in (tuple,list) and tp[1] == 'Array') or _find_arraytype(ps.body_root))
+ if self.typesmodule is None or isarray:
+ return _Binding.Receive(self, replytype, **kw)
+
+ if ps.IsAFault():
+ msg = FaultFromFaultMessage(ps)
+ raise FaultException(msg)
+
+ tc = replytype
+ if hasattr(replytype, 'typecode'):
+ tc = replytype.typecode
+
+ #Ignore response wrapper
+ reply = {}
+ for elt in _child_elements(ps.body_root):
+ name = str(elt.localName)
+ reply[name] = self.__parse_child(elt)
+
+ if self.address is not None:
+ self.address.checkResponse(ps, kw.get('wsaction'))
+
+ return reply
+
+
+
+class NamedParamBinding(Binding):
+ '''Like Binding, except the argument list for invocation is
+ named parameters.
+ '''
+ logger = _GetLogger('ZSI.client.Binding')
+
+ def __getattr__(self, name):
+ '''Return a callable object that will invoke the RPC method
+ named by the attribute.
+ '''
+ if name[:2] == '__' and len(name) > 5 and name[-2:] == '__':
+ if hasattr(self, name): return getattr(self, name)
+ return getattr(self.__class__, name)
+ return _NamedParamCaller(self, name)
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/digest_auth.py b/ZSI/zsi/ZSI/digest_auth.py
new file mode 100644
index 0000000..9c59e81
--- /dev/null
+++ b/ZSI/zsi/ZSI/digest_auth.py
@@ -0,0 +1,105 @@
+#! /usr/bin/env python
+# $Header$
+'''Utilities for HTTP Digest Authentication
+'''
+
+from md5 import md5
+import random
+import time
+import httplib
+
+random.seed(int(time.time()*10))
+
+def H(val):
+ return md5(val).hexdigest()
+
+def KD(secret,data):
+ return H('%s:%s' % (secret,data))
+
+def A1(username,realm,passwd,nonce=None,cnonce=None):
+ if nonce and cnonce:
+ return '%s:%s:%s:%s:%s' % (username,realm,passwd,nonce,cnonce)
+ else:
+ return '%s:%s:%s' % (username,realm,passwd)
+
+def A2(method,uri):
+ return '%s:%s' % (method,uri)
+
+def dict_fetch(d,k,defval=None):
+ if d.has_key(k):
+ return d[k]
+ return defval
+
+def generate_response(chaldict,uri,username,passwd,method='GET',cnonce=None):
+ """
+ Generate an authorization response dictionary. chaldict should contain the digest
+ challenge in dict form. Use fetch_challenge to create a chaldict from a HTTPResponse
+ object like this: fetch_challenge(res.getheaders()).
+
+ returns dict (the authdict)
+
+ Note. Use build_authorization_arg() to turn an authdict into the final Authorization
+ header value.
+ """
+ authdict = {}
+ qop = dict_fetch(chaldict,'qop')
+ domain = dict_fetch(chaldict,'domain')
+ nonce = dict_fetch(chaldict,'nonce')
+ stale = dict_fetch(chaldict,'stale')
+ algorithm = dict_fetch(chaldict,'algorithm','MD5')
+ realm = dict_fetch(chaldict,'realm','MD5')
+ opaque = dict_fetch(chaldict,'opaque')
+ nc = "00000001"
+ if not cnonce:
+ cnonce = H(str(random.randint(0,10000000)))[:16]
+
+ if algorithm.lower()=='md5-sess':
+ a1 = A1(username,realm,passwd,nonce,cnonce)
+ else:
+ a1 = A1(username,realm,passwd)
+
+ a2 = A2(method,uri)
+
+ secret = H(a1)
+ data = '%s:%s:%s:%s:%s' % (nonce,nc,cnonce,qop,H(a2))
+ authdict['username'] = '"%s"' % username
+ authdict['realm'] = '"%s"' % realm
+ authdict['nonce'] = '"%s"' % nonce
+ authdict['uri'] = '"%s"' % uri
+ authdict['response'] = '"%s"' % KD(secret,data)
+ authdict['qop'] = '"%s"' % qop
+ authdict['nc'] = nc
+ authdict['cnonce'] = '"%s"' % cnonce
+
+ return authdict
+
+
+def fetch_challenge(http_header):
+ """
+ Create a challenge dictionary from a HTTPResponse objects getheaders() method.
+ """
+ chaldict = {}
+ vals = http_header.split(' ')
+ chaldict['challenge'] = vals[0]
+ for val in vals[1:]:
+ try:
+ a,b = val.split('=')
+ b=b.replace('"','')
+ b=b.replace("'",'')
+ b=b.replace(",",'')
+ chaldict[a.lower()] = b
+ except:
+ pass
+ return chaldict
+
+
+def build_authorization_arg(authdict):
+ """
+ Create an "Authorization" header value from an authdict (created by generate_response()).
+ """
+ vallist = []
+ for k in authdict.keys():
+ vallist += ['%s=%s' % (k,authdict[k])]
+ return 'Digest '+', '.join(vallist)
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/dispatch.py b/ZSI/zsi/ZSI/dispatch.py
new file mode 100644
index 0000000..c97c3c3
--- /dev/null
+++ b/ZSI/zsi/ZSI/dispatch.py
@@ -0,0 +1,290 @@
+#! /usr/bin/env python
+# $Header$
+'''Simple CGI dispatching.
+'''
+
+import types, os, sys
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+from ZSI import *
+from ZSI import _child_elements, _copyright, _seqtypes, _find_arraytype, _find_type, resolvers
+from ZSI.auth import _auth_tc, AUTH, ClientBinding
+
+
+# Client binding information is stored in a global. We provide an accessor
+# in case later on it's not.
+_client_binding = None
+
+def GetClientBinding():
+ '''Return the client binding object.
+ '''
+ return _client_binding
+
+gettypecode = lambda mod,e: getattr(mod, str(e.localName)).typecode
+def _Dispatch(ps, modules, SendResponse, SendFault, nsdict={}, typesmodule=None,
+ gettypecode=gettypecode, rpc=False, docstyle=False, **kw):
+ '''Find a handler for the SOAP request in ps; search modules.
+ Call SendResponse or SendFault to send the reply back, appropriately.
+
+ Behaviors:
+ default -- Call "handler" method with pyobj representation of body root, and return
+ a self-describing request (w/typecode). Parsing done via a typecode from
+ typesmodule, or Any.
+
+ docstyle -- Call "handler" method with ParsedSoap instance and parse result with an
+ XML typecode (DOM). Behavior, wrap result in a body_root "Response" appended message.
+
+ rpc -- Specify RPC wrapper of result. Behavior, ignore body root (RPC Wrapper)
+ of request, parse all "parts" of message via individual typecodes. Expect
+ the handler to return the parts of the message, whether it is a dict, single instance,
+ or a list try to serialize it as a Struct but if this is not possible put it in an Array.
+ Parsing done via a typecode from typesmodule, or Any.
+
+ '''
+ global _client_binding
+ try:
+ what = str(ps.body_root.localName)
+
+ # See what modules have the element name.
+ if modules is None:
+ modules = ( sys.modules['__main__'], )
+
+ handlers = [ getattr(m, what) for m in modules if hasattr(m, what) ]
+ if len(handlers) == 0:
+ raise TypeError("Unknown method " + what)
+
+ # Of those modules, see who's callable.
+ handlers = [ h for h in handlers if callable(h) ]
+ if len(handlers) == 0:
+ raise TypeError("Unimplemented method " + what)
+ if len(handlers) > 1:
+ raise TypeError("Multiple implementations found: " + `handlers`)
+ handler = handlers[0]
+
+ _client_binding = ClientBinding(ps)
+ if docstyle:
+ result = handler(ps.body_root)
+ tc = TC.XML(aslist=1, pname=what+'Response')
+ elif not rpc:
+ try:
+ tc = gettypecode(typesmodule, ps.body_root)
+ except Exception:
+ tc = TC.Any()
+
+ try:
+ arg = tc.parse(ps.body_root, ps)
+ except EvaluateException, ex:
+ SendFault(FaultFromZSIException(ex), **kw)
+ return
+
+ try:
+ result = handler(*arg)
+ except Exception,ex:
+ SendFault(FaultFromZSIException(ex), **kw)
+
+ try:
+ tc = result.typecode
+ except AttributeError,ex:
+ SendFault(FaultFromZSIException(ex), **kw)
+
+ elif typesmodule is not None:
+ kwargs = {}
+ for e in _child_elements(ps.body_root):
+ try:
+ tc = gettypecode(typesmodule, e)
+ except Exception:
+ tc = TC.Any()
+
+ try:
+ kwargs[str(e.localName)] = tc.parse(e, ps)
+ except EvaluateException, ex:
+ SendFault(FaultFromZSIException(ex), **kw)
+ return
+
+ result = handler(**kwargs)
+ aslist = False
+ # make sure data is wrapped, try to make this a Struct
+ if type(result) in _seqtypes:
+ for o in result:
+ aslist = hasattr(result, 'typecode')
+ if aslist: break
+ elif type(result) is not dict:
+ aslist = not hasattr(result, 'typecode')
+ result = (result,)
+
+ tc = TC.Any(pname=what+'Response', aslist=aslist)
+ else:
+ # if this is an Array, call handler with list
+ # if this is an Struct, call handler with dict
+ tp = _find_type(ps.body_root)
+ isarray = ((type(tp) in (tuple,list) and tp[1] == 'Array') or _find_arraytype(ps.body_root))
+ data = _child_elements(ps.body_root)
+ tc = TC.Any()
+ if isarray and len(data) == 0:
+ result = handler()
+ elif isarray:
+ try: arg = [ tc.parse(e, ps) for e in data ]
+ except EvaluateException, e:
+ #SendFault(FaultFromZSIException(e), **kw)
+ SendFault(RuntimeError("THIS IS AN ARRAY: %s" %isarray))
+ return
+
+ result = handler(*arg)
+ else:
+ try: kwarg = dict([ (str(e.localName),tc.parse(e, ps)) for e in data ])
+ except EvaluateException, e:
+ SendFault(FaultFromZSIException(e), **kw)
+ return
+
+ result = handler(**kwarg)
+
+ # reponse typecode
+ #tc = getattr(result, 'typecode', TC.Any(pname=what+'Response'))
+ tc = TC.Any(pname=what+'Response')
+
+ sw = SoapWriter(nsdict=nsdict)
+ sw.serialize(result, tc)
+ return SendResponse(str(sw), **kw)
+ except Fault, e:
+ return SendFault(e, **kw)
+ except Exception, e:
+ # Something went wrong, send a fault.
+ return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
+
+
+def _ModPythonSendXML(text, code=200, **kw):
+ req = kw['request']
+ req.content_type = 'text/xml'
+ req.content_length = len(text)
+ req.send_http_header()
+ req.write(text)
+
+
+def _ModPythonSendFault(f, **kw):
+ _ModPythonSendXML(f.AsSOAP(), 500, **kw)
+
+def _JonPySendFault(f, **kw):
+ _JonPySendXML(f.AsSOAP(), 500, **kw)
+
+def _JonPySendXML(text, code=200, **kw):
+ req = kw['request']
+ req.set_header("Content-Type", 'text/xml; charset="utf-8"')
+ req.set_header("Content-Length", str(len(text)))
+ req.write(text)
+
+def _CGISendXML(text, code=200, **kw):
+ print 'Status: %d' % code
+ print 'Content-Type: text/xml; charset="utf-8"'
+ print 'Content-Length: %d' % len(text)
+ print ''
+ print text
+
+def _CGISendFault(f, **kw):
+ _CGISendXML(f.AsSOAP(), 500, **kw)
+
+
+class SOAPRequestHandler(BaseHTTPRequestHandler):
+ '''SOAP handler.
+ '''
+ server_version = 'ZSI/1.1 ' + BaseHTTPRequestHandler.server_version
+
+ def send_xml(self, text, code=200):
+ '''Send some XML.
+ '''
+ self.send_response(code)
+ self.send_header('Content-type', 'text/xml; charset="utf-8"')
+ self.send_header('Content-Length', str(len(text)))
+ self.end_headers()
+ self.wfile.write(text)
+ self.wfile.flush()
+
+ def send_fault(self, f, code=500):
+ '''Send a fault.
+ '''
+ self.send_xml(f.AsSOAP(), code)
+
+ def do_POST(self):
+ '''The POST command.
+ '''
+ try:
+ ct = self.headers['content-type']
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, self.rfile)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ length = int(self.headers['content-length'])
+ ps = ParsedSoap(self.rfile.read(length))
+ except ParseException, e:
+ self.send_fault(FaultFromZSIException(e))
+ return
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ self.send_fault(FaultFromException(e, 1, sys.exc_info()[2]))
+ return
+
+ _Dispatch(ps, self.server.modules, self.send_xml, self.send_fault,
+ docstyle=self.server.docstyle, nsdict=self.server.nsdict,
+ typesmodule=self.server.typesmodule, rpc=self.server.rpc)
+
+def AsServer(port=80, modules=None, docstyle=False, nsdict={}, typesmodule=None,
+ rpc=False, addr=''):
+ address = (addr, port)
+ httpd = HTTPServer(address, SOAPRequestHandler)
+ httpd.modules = modules
+ httpd.docstyle = docstyle
+ httpd.nsdict = nsdict
+ httpd.typesmodule = typesmodule
+ httpd.rpc = rpc
+ httpd.serve_forever()
+
+def AsCGI(nsdict={}, typesmodule=None, rpc=False, modules=None):
+ '''Dispatch within a CGI script.
+ '''
+ if os.environ.get('REQUEST_METHOD') != 'POST':
+ _CGISendFault(Fault(Fault.Client, 'Must use POST'))
+ return
+ ct = os.environ['CONTENT_TYPE']
+ try:
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, sys.stdin)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ length = int(os.environ['CONTENT_LENGTH'])
+ ps = ParsedSoap(sys.stdin.read(length))
+ except ParseException, e:
+ _CGISendFault(FaultFromZSIException(e))
+ return
+ _Dispatch(ps, modules, _CGISendXML, _CGISendFault, nsdict=nsdict,
+ typesmodule=typesmodule, rpc=rpc)
+
+def AsHandler(request=None, modules=None, **kw):
+ '''Dispatch from within ModPython.'''
+ ps = ParsedSoap(request)
+ kw['request'] = request
+ _Dispatch(ps, modules, _ModPythonSendXML, _ModPythonSendFault, **kw)
+
+def AsJonPy(request=None, modules=None, **kw):
+ '''Dispatch within a jonpy CGI/FastCGI script.
+ '''
+
+ kw['request'] = request
+ if request.environ.get('REQUEST_METHOD') != 'POST':
+ _JonPySendFault(Fault(Fault.Client, 'Must use POST'), **kw)
+ return
+ ct = request.environ['CONTENT_TYPE']
+ try:
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, request.stdin)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ length = int(request.environ['CONTENT_LENGTH'])
+ ps = ParsedSoap(request.stdin.read(length))
+ except ParseException, e:
+ _JonPySendFault(FaultFromZSIException(e), **kw)
+ return
+ _Dispatch(ps, modules, _JonPySendXML, _JonPySendFault, **kw)
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/fault.py b/ZSI/zsi/ZSI/fault.py
new file mode 100644
index 0000000..96f507f
--- /dev/null
+++ b/ZSI/zsi/ZSI/fault.py
@@ -0,0 +1,262 @@
+#! /usr/bin/env python
+# $Header$
+'''Faults.
+'''
+
+from ZSI import _copyright, _children, _child_elements, \
+ _get_idstr, _stringtypes, _seqtypes, _Node, SoapWriter, ZSIException
+
+from ZSI.TCcompound import Struct
+from ZSI.TC import QName, URI, String, XMLString, AnyElement, UNBOUNDED
+
+from ZSI.wstools.Namespaces import SOAP, ZSI_SCHEMA_URI
+from ZSI.wstools.c14n import Canonicalize
+from ZSI.TC import ElementDeclaration
+
+import traceback, cStringIO as StringIO
+
+
+class Detail:
+ def __init__(self, any=None):
+ self.any = any
+
+Detail.typecode = Struct(Detail, [AnyElement(aname='any',minOccurs=0, maxOccurs="unbounded")], pname='detail', minOccurs=0)
+
+class FaultType:
+ def __init__(self, faultcode=None, faultstring=None, faultactor=None, detail=None):
+ self.faultcode = faultcode
+ self.faultstring= faultstring
+ self.faultactor = faultactor
+ self.detail = detail
+
+FaultType.typecode = \
+ Struct(FaultType,
+ [QName(pname='faultcode'),
+ String(pname='faultstring'),
+ URI(pname=(SOAP.ENV,'faultactor'), minOccurs=0),
+ Detail.typecode,
+ AnyElement(aname='any',minOccurs=0, maxOccurs=UNBOUNDED),
+ ],
+ pname=(SOAP.ENV,'Fault'),
+ inline=True,
+ hasextras=0,
+ )
+
+class ZSIHeaderDetail:
+ def __init__(self, detail):
+ self.any = detail
+
+ZSIHeaderDetail.typecode =\
+ Struct(ZSIHeaderDetail,
+ [AnyElement(aname='any', minOccurs=0, maxOccurs=UNBOUNDED)],
+ pname=(ZSI_SCHEMA_URI, 'detail'))
+
+
+class ZSIFaultDetailTypeCode(ElementDeclaration, Struct):
+ '''<ZSI:FaultDetail>
+ <ZSI:string>%s</ZSI:string>
+ <ZSI:trace>%s</ZSI:trace>
+ </ZSI:FaultDetail>
+ '''
+ schema = ZSI_SCHEMA_URI
+ literal = 'FaultDetail'
+ def __init__(self, **kw):
+ Struct.__init__(self, ZSIFaultDetail, [String(pname=(ZSI_SCHEMA_URI, 'string')),
+ String(pname=(ZSI_SCHEMA_URI, 'trace'),minOccurs=0),],
+ pname=(ZSI_SCHEMA_URI, 'FaultDetail'), **kw
+ )
+
+class ZSIFaultDetail:
+ def __init__(self, string=None, trace=None):
+ self.string = string
+ self.trace = trace
+
+ def __str__(self):
+ if self.trace:
+ return self.string + '\n[trace: ' + self.trace + ']'
+ return self.string
+
+ def __repr__(self):
+ return "<%s.ZSIFaultDetail %s>" % (__name__, _get_idstr(self))
+ZSIFaultDetail.typecode = ZSIFaultDetailTypeCode()
+
+
+class URIFaultDetailTypeCode(ElementDeclaration, Struct):
+ '''
+ <ZSI:URIFaultDetail>
+ <ZSI:URI>uri</ZSI:URI>
+ <ZSI:localname>localname</ZSI:localname>
+ </ZSI:URIFaultDetail>
+ '''
+ schema = ZSI_SCHEMA_URI
+ literal = 'URIFaultDetail'
+ def __init__(self, **kw):
+ Struct.__init__(self, URIFaultDetail,
+ [String(pname=(ZSI_SCHEMA_URI, 'URI')), String(pname=(ZSI_SCHEMA_URI, 'localname')),],
+ pname=(ZSI_SCHEMA_URI, 'URIFaultDetail'), **kw
+ )
+
+class URIFaultDetail:
+ def __init__(self, uri=None, localname=None):
+ self.URI = uri
+ self.localname = localname
+URIFaultDetail.typecode = URIFaultDetailTypeCode()
+
+
+class ActorFaultDetailTypeCode(ElementDeclaration, Struct):
+ '''
+ <ZSI:ActorFaultDetail>
+ <ZSI:URI>%s</ZSI:URI>
+ </ZSI:ActorFaultDetail>
+ '''
+ schema = ZSI_SCHEMA_URI
+ literal = 'ActorFaultDetail'
+ def __init__(self, **kw):
+ Struct.__init__(self, ActorFaultDetail, [String(pname=(ZSI_SCHEMA_URI, 'URI')),],
+ pname=(ZSI_SCHEMA_URI, 'ActorFaultDetail'), **kw
+ )
+
+class ActorFaultDetail:
+ def __init__(self, uri=None):
+ self.URI = uri
+ActorFaultDetail.typecode = ActorFaultDetailTypeCode()
+
+
+class Fault(ZSIException):
+ '''SOAP Faults.
+ '''
+
+ Client = "SOAP-ENV:Client"
+ Server = "SOAP-ENV:Server"
+ MU = "SOAP-ENV:MustUnderstand"
+
+ def __init__(self, code, string,
+ actor=None, detail=None, headerdetail=None):
+ if detail is not None and type(detail) not in _seqtypes:
+ detail = (detail,)
+ if headerdetail is not None and type(headerdetail) not in _seqtypes:
+ headerdetail = (headerdetail,)
+ self.code, self.string, self.actor, self.detail, self.headerdetail = \
+ code, string, actor, detail, headerdetail
+ ZSIException.__init__(self, code, string, actor, detail, headerdetail)
+
+ def DataForSOAPHeader(self):
+ if not self.headerdetail: return None
+ # SOAP spec doesn't say how to encode header fault data.
+ return ZSIHeaderDetail(self.headerdetail)
+
+ def serialize(self, sw):
+ '''Serialize the object.'''
+ detail = None
+ if self.detail is not None:
+ detail = Detail()
+ detail.any = self.detail
+
+ pyobj = FaultType(self.code, self.string, self.actor, detail)
+ sw.serialize(pyobj, typed=False)
+
+ def AsSOAP(self, **kw):
+
+ header = self.DataForSOAPHeader()
+ sw = SoapWriter(**kw)
+ self.serialize(sw)
+ if header is not None:
+ sw.serialize_header(header, header.typecode, typed=False)
+ return str(sw)
+
+ def __str__(self):
+ strng = str(self.string) + "\n"
+ if hasattr(self, 'detail'):
+ if hasattr(self.detail, '__len__'):
+ for d in self.detail:
+ strng += str(d)
+ else:
+ strng += str(self.detail)
+ return strng
+
+ def __repr__(self):
+ return "<%s.Fault at %s>" % (__name__, _get_idstr(self))
+
+ AsSoap = AsSOAP
+
+
+def FaultFromNotUnderstood(uri, localname, actor=None):
+ detail, headerdetail = None, URIFaultDetail(uri, localname)
+ return Fault(Fault.MU, 'SOAP mustUnderstand not understood',
+ actor, detail, headerdetail)
+
+
+def FaultFromActor(uri, actor=None):
+ detail, headerdetail = None, ActorFaultDetail(uri)
+ return Fault(Fault.Client, 'Cannot process specified actor',
+ actor, detail, headerdetail)
+
+
+def FaultFromZSIException(ex, actor=None):
+ '''Return a Fault object created from a ZSI exception object.
+ '''
+ mystr = getattr(ex, 'str', None) or str(ex)
+ mytrace = getattr(ex, 'trace', '')
+ elt = '''<ZSI:ParseFaultDetail>
+<ZSI:string>%s</ZSI:string>
+<ZSI:trace>%s</ZSI:trace>
+</ZSI:ParseFaultDetail>
+''' % (mystr, mytrace)
+ if getattr(ex, 'inheader', 0):
+ detail, headerdetail = None, elt
+ else:
+ detail, headerdetail = elt, None
+ return Fault(Fault.Client, 'Unparseable message',
+ actor, detail, headerdetail)
+
+
+def FaultFromException(ex, inheader, tb=None, actor=None):
+ '''Return a Fault object created from a Python exception.
+
+ <SOAP-ENV:Fault>
+ <faultcode>SOAP-ENV:Server</faultcode>
+ <faultstring>Processing Failure</faultstring>
+ <detail>
+ <ZSI:FaultDetail>
+ <ZSI:string></ZSI:string>
+ <ZSI:trace></ZSI:trace>
+ </ZSI:FaultDetail>
+ </detail>
+ </SOAP-ENV:Fault>
+ '''
+ tracetext = None
+ if tb:
+ try:
+ lines = '\n'.join(['%s:%d:%s' % (name, line, func)
+ for name, line, func, text in traceback.extract_tb(tb)])
+ except:
+ pass
+ else:
+ tracetext = lines
+
+ exceptionName = ""
+ try:
+ exceptionName = ":".join([ex.__module__, ex.__class__.__name__])
+ except: pass
+ elt = ZSIFaultDetail(string=exceptionName + "\n" + str(ex), trace=tracetext)
+ if inheader:
+ detail, headerdetail = None, elt
+ else:
+ detail, headerdetail = elt, None
+ return Fault(Fault.Server, 'Processing Failure',
+ actor, detail, headerdetail)
+
+
+def FaultFromFaultMessage(ps):
+ '''Parse the message as a fault.
+ '''
+ pyobj = ps.Parse(FaultType.typecode)
+
+ if pyobj.detail == None: detailany = None
+ else: detailany = pyobj.detail.any
+
+ return Fault(pyobj.faultcode, pyobj.faultstring,
+ pyobj.faultactor, detailany)
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/generate/__init__.py b/ZSI/zsi/ZSI/generate/__init__.py
new file mode 100644
index 0000000..07f9493
--- /dev/null
+++ b/ZSI/zsi/ZSI/generate/__init__.py
@@ -0,0 +1,24 @@
+# $Id: __init__.py 1261 2006-10-10 23:45:43Z boverhof $
+
+__all__ = ['wsdl2python', 'utility', 'containers', 'commands']
+
+class WSDLFormatError(Exception):
+ '''Detected errors in the WSDL document.
+ '''
+
+class WsdlGeneratorError(Exception):
+ pass
+
+class Wsdl2PythonError(Exception):
+ pass
+
+class WSInteropError(Exception):
+ '''Conformance to WS-I Basic-Profile 1.0 specification
+ '''
+
+class WSISpec:
+ R2203 = 'An rpc-literal binding in a DESCRIPTION MUST refer, in its soapbind:body element(s), only to wsdl:part element(s) that have been defined using the type attribute.'
+ R2710 = 'The operations in a wsdl:binding in a DESCRIPTION MUST result in wire signatures that are different from one another.'
+ R2717 = 'An rpc-literal binding in a DESCRIPTION MUST have the namespace attribute specified, the value of which MUST be an absolute URI, on contained soapbind:body elements.'
+ R2729 = 'A MESSAGE described with an rpc-literal binding that is a response message MUST have a wrapper element whose name is the corresponding wsdl:operation name suffixed with the string "Response"'
+
diff --git a/ZSI/zsi/ZSI/generate/commands.py b/ZSI/zsi/ZSI/generate/commands.py
new file mode 100644
index 0000000..9b9603e
--- /dev/null
+++ b/ZSI/zsi/ZSI/generate/commands.py
@@ -0,0 +1,304 @@
+############################################################################
+# Joshua Boverhof<JRBoverhof@lbl.gov>, LBNL
+# Monte Goode <MMGoode@lbl.gov>, LBNL
+# See Copyright for copyright notice!
+############################################################################
+
+import exceptions, sys, optparse, os, warnings
+from operator import xor
+import ZSI
+from ConfigParser import ConfigParser
+from ZSI.generate.wsdl2python import WriteServiceModule, ServiceDescription
+from ZSI.wstools import WSDLTools, XMLSchema
+from ZSI.wstools.logging import setBasicLoggerDEBUG
+from ZSI.generate import containers, utility
+from ZSI.generate.utility import NCName_to_ClassName as NC_to_CN, TextProtect
+from ZSI.generate.wsdl2dispatch import ServiceModuleWriter as ServiceDescription
+from ZSI.generate.wsdl2dispatch import DelAuthServiceModuleWriter as DelAuthServiceDescription
+from ZSI.generate.wsdl2dispatch import WSAServiceModuleWriter as ServiceDescriptionWSA
+from ZSI.generate.wsdl2dispatch import DelAuthWSAServiceModuleWriter as DelAuthServiceDescriptionWSA
+
+warnings.filterwarnings('ignore', '', exceptions.UserWarning)
+def SetDebugCallback(option, opt, value, parser, *args, **kwargs):
+ setBasicLoggerDEBUG()
+ warnings.resetwarnings()
+
+def SetPyclassMetaclass(option, opt, value, parser, *args, **kwargs):
+ """set up pyclass metaclass for complexTypes"""
+ from ZSI.generate.containers import ServiceHeaderContainer, TypecodeContainerBase, TypesHeaderContainer
+ TypecodeContainerBase.metaclass = kwargs['metaclass']
+ TypesHeaderContainer.imports.append(\
+ 'from %(module)s import %(metaclass)s' %kwargs
+ )
+ ServiceHeaderContainer.imports.append(\
+ 'from %(module)s import %(metaclass)s' %kwargs
+ )
+
+def SetUpTwistedClient(option, opt, value, parser, *args, **kwargs):
+ from ZSI.generate.containers import ServiceHeaderContainer
+ ServiceHeaderContainer.imports.remove('from ZSI import client')
+ ServiceHeaderContainer.imports.append('from ZSI.twisted import client')
+
+
+def SetUpLazyEvaluation(option, opt, value, parser, *args, **kwargs):
+ from ZSI.generate.containers import TypecodeContainerBase
+ TypecodeContainerBase.lazy = True
+
+
+def formatSchemaObject(fname, schemaObj):
+ """ In the case of a 'schema only' generation (-s) this creates
+ a fake wsdl object that will function w/in the adapters
+ and allow the generator to do what it needs to do.
+ """
+
+ class fake:
+ pass
+
+ f = fake()
+
+ if fname.rfind('/'):
+ tmp = fname[fname.rfind('/') + 1 :].split('.')
+ else:
+ tmp = fname.split('.')
+
+ f.name = tmp[0] + '_' + tmp[1]
+ f.types = { schemaObj.targetNamespace : schemaObj }
+
+ return f
+
+def wsdl2py(args=None):
+ """
+ A utility for automatically generating client interface code from a wsdl
+ definition, and a set of classes representing element declarations and
+ type definitions. This will produce two files in the current working
+ directory named after the wsdl definition name.
+
+ eg. <definition name='SampleService'>
+ SampleService.py
+ SampleService_types.py
+ """
+
+ op = optparse.OptionParser(usage="usage: %prog [options]",
+ description=wsdl2py.__doc__)
+
+ # Basic options
+ op.add_option("-f", "--file",
+ action="store", dest="file", default=None, type="string",
+ help="FILE to load wsdl from")
+ op.add_option("-u", "--url",
+ action="store", dest="url", default=None, type="string",
+ help="URL to load wsdl from")
+ op.add_option("-x", "--schema",
+ action="store_true", dest="schema", default=False,
+ help="process just the schema from an xsd file [no services]")
+ op.add_option("-d", "--debug",
+ action="callback", callback=SetDebugCallback,
+ help="debug output")
+
+ # WS Options
+ op.add_option("-a", "--address",
+ action="store_true", dest="address", default=False,
+ help="ws-addressing support, must include WS-Addressing schema.")
+
+ # pyclass Metaclass
+ op.add_option("-b", "--complexType",
+ action="callback", callback=SetPyclassMetaclass,
+ callback_kwargs={'module':'ZSI.generate.pyclass',
+ 'metaclass':'pyclass_type'},
+ help="add convenience functions for complexTypes, including Getters, Setters, factory methods, and properties (via metaclass). *** DONT USE WITH --simple-naming ***")
+
+ # Lazy Evaluation of Typecodes (done at serialization/parsing when needed).
+ op.add_option("-l", "--lazy",
+ action="callback", callback=SetUpLazyEvaluation,
+ callback_kwargs={},
+ help="EXPERIMENTAL: recursion error solution, lazy evalution of typecodes")
+
+ # Use Twisted
+ op.add_option("-w", "--twisted",
+ action="callback", callback=SetUpTwistedClient,
+ callback_kwargs={'module':'ZSI.generate.pyclass',
+ 'metaclass':'pyclass_type'},
+ help="generate a twisted.web client, dependencies python>=2.4, Twisted>=2.0.0, TwistedWeb>=0.5.0")
+
+ # Extended generation options
+ op.add_option("-e", "--extended",
+ action="store_true", dest="extended", default=False,
+ help="Do Extended code generation.")
+ op.add_option("-z", "--aname",
+ action="store", dest="aname", default=None, type="string",
+ help="pass in a function for attribute name creation")
+ op.add_option("-t", "--types",
+ action="store", dest="types", default=None, type="string",
+ help="file to load types from")
+ op.add_option("-o", "--output-dir",
+ action="store", dest="output_dir", default=".", type="string",
+ help="Write generated files to OUTPUT_DIR")
+ op.add_option("-s", "--simple-naming",
+ action="store_true", dest="simple_naming", default=False,
+ help="Simplify generated naming.")
+ op.add_option("-c", "--clientClassSuffix",
+ action="store", dest="clientClassSuffix", default=None, type="string",
+ help="Suffix to use for service client class (default \"SOAP\")")
+ op.add_option("-m", "--pyclassMapModule",
+ action="store", dest="pyclassMapModule", default=None, type="string",
+ help="Python file that maps external python classes to a schema type. The classes are used as the \"pyclass\" for that type. The module should contain a dict() called mapping in the format: mapping = {schemaTypeName:(moduleName.py,className) }")
+
+ if args is None:
+ (options, args) = op.parse_args()
+ else:
+ (options, args) = op.parse_args(args)
+
+ if not xor(options.file is None, options.url is None):
+ print 'Must specify either --file or --url option'
+ sys.exit(os.EX_USAGE)
+
+ location = options.file
+ if options.url is not None:
+ location = options.url
+
+ if options.schema is True:
+ reader = XMLSchema.SchemaReader(base_url=location)
+ else:
+ reader = WSDLTools.WSDLReader()
+
+ load = reader.loadFromFile
+ if options.url is not None:
+ load = reader.loadFromURL
+
+ wsdl = None
+ try:
+ wsdl = load(location)
+ except Exception, e:
+ print "Error loading %s: \n\t%s" % (location, e)
+ # exit code UNIX specific, Windows?
+ sys.exit(os.EX_NOINPUT)
+
+ if options.simple_naming:
+ # Use a different client suffix
+ WriteServiceModule.client_module_suffix = "_client"
+ # Write messages definitions to a separate file.
+ ServiceDescription.separate_messages = True
+ # Use more simple type and element class names
+ containers.SetTypeNameFunc( lambda n: '%s_' %(NC_to_CN(n)) )
+ containers.SetElementNameFunc( lambda n: '%s' %(NC_to_CN(n)) )
+ # Don't add "_" to the attribute name (remove when --aname works well)
+ containers.ContainerBase.func_aname = lambda instnc,n: TextProtect(str(n))
+ # write out the modules with their names rather than their number.
+ utility.namespace_name = lambda cls, ns: utility.Namespace2ModuleName(ns)
+
+ if options.clientClassSuffix:
+ from ZSI.generate.containers import ServiceContainerBase
+ ServiceContainerBase.clientClassSuffix = options.clientClassSuffix
+
+ if options.schema is True:
+ wsdl = formatSchemaObject(location, wsdl)
+
+ if options.aname is not None:
+ args = options.aname.rsplit('.',1)
+ assert len(args) == 2, 'expecting module.function'
+ # The following exec causes a syntax error.
+ #exec('from %s import %s as FUNC' %(args[0],args[1]))
+ assert callable(FUNC),\
+ '%s must be a callable method with one string parameter' %options.aname
+ from ZSI.generate.containers import TypecodeContainerBase
+ TypecodeContainerBase.func_aname = staticmethod(FUNC)
+
+ if options.pyclassMapModule != None:
+ mod = __import__(options.pyclassMapModule)
+ components = options.pyclassMapModule.split('.')
+ for comp in components[1:]:
+ mod = getattr(mod, comp)
+ extPyClasses = mod.mapping
+ else:
+ extPyClasses = None
+
+ wsm = WriteServiceModule(wsdl, addressing=options.address, do_extended=options.extended, extPyClasses=extPyClasses)
+ if options.types != None:
+ wsm.setTypesModuleName(options.types)
+ if options.schema is False:
+ fd = open(os.path.join(options.output_dir, '%s.py' %wsm.getClientModuleName()), 'w+')
+ # simple naming writes the messages to a separate file
+ if not options.simple_naming:
+ wsm.writeClient(fd)
+ else: # provide a separate file to store messages to.
+ msg_fd = open(os.path.join(options.output_dir, '%s.py' %wsm.getMessagesModuleName()), 'w+')
+ wsm.writeClient(fd, msg_fd=msg_fd)
+ msg_fd.close()
+ fd.close()
+
+ fd = open( os.path.join(options.output_dir, '%s.py' %wsm.getTypesModuleName()), 'w+')
+ wsm.writeTypes(fd)
+ fd.close()
+
+
+def wsdl2dispatch(args=None):
+ """
+ wsdl2dispatch
+
+ A utility for automatically generating service skeleton code from a wsdl
+ definition.
+ """
+
+ op = optparse.OptionParser()
+ op.add_option("-f", "--file",
+ action="store", dest="file", default=None, type="string",
+ help="file to load wsdl from")
+ op.add_option("-u", "--url",
+ action="store", dest="url", default=None, type="string",
+ help="URL to load wsdl from")
+ op.add_option("-a", "--address",
+ action="store_true", dest="address", default=False,
+ help="ws-addressing support, must include WS-Addressing schema.")
+ op.add_option("-e", "--extended",
+ action="store_true", dest="extended", default=False,
+ help="Extended code generation.")
+ op.add_option("-d", "--debug",
+ action="callback", callback=SetDebugCallback,
+ help="debug output")
+ op.add_option("-t", "--types",
+ action="store", dest="types", default=None, type="string",
+ help="Write generated files to OUTPUT_DIR")
+ op.add_option("-o", "--output-dir",
+ action="store", dest="output_dir", default=".", type="string",
+ help="file to load types from")
+ op.add_option("-s", "--simple-naming",
+ action="store_true", dest="simple_naming", default=False,
+ help="Simplify generated naming.")
+
+ if args is None:
+ (options, args) = op.parse_args()
+ else:
+ (options, args) = op.parse_args(args)
+
+ if options.simple_naming:
+ ServiceDescription.server_module_suffix = '_interface'
+ ServiceDescription.func_aname = lambda instnc,n: TextProtect(n)
+ ServiceDescription.separate_messages = True
+ # use module names rather than their number.
+ utility.namespace_name = lambda cls, ns: utility.Namespace2ModuleName(ns)
+
+ reader = WSDLTools.WSDLReader()
+ wsdl = None
+ if options.file is not None:
+ wsdl = reader.loadFromFile(options.file)
+ elif options.url is not None:
+ wsdl = reader.loadFromURL(options.url)
+
+ assert wsdl is not None, 'Must specify WSDL either with --file or --url'
+
+ ss = None
+ if options.address is True:
+ if options.extended:
+ ss = DelAuthServiceDescriptionWSA(do_extended=options.extended)
+ else:
+ ss = ServiceDescriptionWSA(do_extended=options.extended)
+ else:
+ if options.extended:
+ ss = DelAuthServiceDescription(do_extended=options.extended)
+ else:
+ ss = ServiceDescription(do_extended=options.extended)
+
+ ss.fromWSDL(wsdl)
+ fd = open( os.path.join(options.output_dir, ss.getServiceModuleName()+'.py'), 'w+')
+ ss.write(fd)
+ fd.close()
diff --git a/ZSI/zsi/ZSI/generate/containers.py b/ZSI/zsi/ZSI/generate/containers.py
new file mode 100644
index 0000000..3a59fda
--- /dev/null
+++ b/ZSI/zsi/ZSI/generate/containers.py
@@ -0,0 +1,2869 @@
+############################################################################
+# Monte M. Goode, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+# contains text container classes for new generation generator
+
+# $Id: containers.py 1276 2006-10-23 23:18:07Z boverhof $
+import types
+from utility import StringWriter, TextProtect, TextProtectAttributeName,\
+ GetPartsSubNames
+from utility import NamespaceAliasDict as NAD, NCName_to_ClassName as NC_to_CN
+
+import ZSI
+from ZSI.TC import _is_xsd_or_soap_ns
+from ZSI.wstools import XMLSchema, WSDLTools
+from ZSI.wstools.Namespaces import SCHEMA, SOAP, WSDL
+from ZSI.wstools.logging import getLogger as _GetLogger
+from ZSI.typeinterpreter import BaseTypeInterpreter
+from ZSI.generate import WSISpec, WSInteropError, Wsdl2PythonError,\
+ WsdlGeneratorError, WSDLFormatError
+
+ID1 = ' '
+ID2 = 2*ID1
+ID3 = 3*ID1
+ID4 = 4*ID1
+ID5 = 5*ID1
+ID6 = 6*ID1
+
+KW = {'ID1':ID1, 'ID2':ID2, 'ID3':ID3,'ID4':ID4, 'ID5':ID5, 'ID6':ID6,}
+
+DEC = '_Dec'
+DEF = '_Def'
+
+"""
+type_class_name -- function to return the name formatted as a type class.
+element_class_name -- function to return the name formatted as an element class.
+"""
+type_class_name = lambda n: '%s%s' %(NC_to_CN(n), DEF)
+element_class_name = lambda n: '%s%s' %(NC_to_CN(n), DEC)
+
+
+def IsRPC(item):
+ """item -- OperationBinding instance.
+ """
+ if not isinstance(item, WSDLTools.OperationBinding):
+ raise TypeError, 'IsRPC takes 1 argument of type WSDLTools.OperationBinding'
+ soapbinding = item.getBinding().findBinding(WSDLTools.SoapBinding)
+ sob = item.findBinding(WSDLTools.SoapOperationBinding)
+ style = soapbinding.style
+ if sob is not None:
+ style = sob.style or soapbinding.style
+ return style == 'rpc'
+
+
+def IsLiteral(item):
+ """item -- MessageRoleBinding instance.
+ """
+ if not isinstance(item, WSDLTools.MessageRoleBinding):
+ raise TypeError, 'IsLiteral takes 1 argument of type WSDLTools.MessageRoleBinding'
+ sbb = None
+ if item.type == 'input' or item.type == 'output':
+ sbb = item.findBinding(WSDLTools.SoapBodyBinding)
+ if sbb is None:
+ raise ValueError, 'Missing soap:body binding.'
+ return sbb.use == 'literal'
+
+
+def SetTypeNameFunc(func):
+ global type_class_name
+ type_class_name = func
+
+def SetElementNameFunc(func):
+ global element_class_name
+ element_class_name = func
+
+def GetClassNameFromSchemaItem(item,do_extended=False):
+ '''
+ '''
+ assert isinstance(item, XMLSchema.XMLSchemaComponent), 'must be a schema item.'
+ alias = NAD.getAlias(item.getTargetNamespace())
+ if item.isDefinition() is True:
+ return '%s.%s' %(alias, NC_to_CN('%s' %type_class_name(item.getAttributeName())))
+ return None
+
+def FromMessageGetSimpleElementDeclaration(message):
+ '''If message consists of one part with an element attribute,
+ and this element is a simpleType return a string representing
+ the python type, else return None.
+
+ '''
+ assert isinstance(message, WSDLTools.Message), 'expecting WSDLTools.Message'
+
+ if len(message.parts) == 1 and message.parts[0].element is not None:
+ part = message.parts[0]
+ nsuri,name = part.element
+ wsdl = message.getWSDL()
+ types = wsdl.types
+ if types.has_key(nsuri) and types[nsuri].elements.has_key(name):
+ e = types[nsuri].elements[name]
+ if isinstance(e, XMLSchema.ElementDeclaration) is True and e.getAttribute('type'):
+ typ = e.getAttribute('type')
+ bt = BaseTypeInterpreter()
+ ptype = bt.get_pythontype(typ[1], typ[0])
+ return ptype
+
+ return None
+
+
+class AttributeMixIn:
+ '''for containers that can declare attributes.
+ Class Attributes:
+ attribute_typecode -- typecode attribute name typecode dict
+ built_in_refs -- attribute references that point to built-in
+ types. Skip resolving them into attribute declarations.
+ '''
+ attribute_typecode = 'self.attribute_typecode_dict'
+ built_in_refs = [(SOAP.ENC, 'arrayType'),]
+
+ def _setAttributes(self, attributes):
+ '''parameters
+ attributes -- a flat list of all attributes,
+ from this list all items in attribute_typecode_dict will
+ be generated into attrComponents.
+
+ returns a list of strings representing the attribute_typecode_dict.
+ '''
+ atd = self.attribute_typecode
+ atd_list = formatted_attribute_list = []
+ if not attributes:
+ return formatted_attribute_list
+
+ atd_list.append('# attribute handling code')
+ for a in attributes:
+
+ if a.isWildCard() and a.isDeclaration():
+ atd_list.append(\
+ '%s[("%s","anyAttribute")] = ZSI.TC.AnyElement()'\
+ % (atd, SCHEMA.XSD3)
+ )
+ elif a.isDeclaration():
+ tdef = a.getTypeDefinition('type')
+ if tdef is not None:
+ tc = '%s.%s(None)' %(NAD.getAlias(tdef.getTargetNamespace()),
+ self.mangle(type_class_name(tdef.getAttributeName()))
+ )
+ else:
+ # built-in
+ t = a.getAttribute('type')
+ try:
+ tc = BTI.get_typeclass(t[1], t[0])
+ except:
+ # hand back a string by default.
+ tc = ZSI.TC.String
+
+ if tc is not None:
+ tc = '%s()' %tc
+
+ key = None
+ if a.getAttribute('form') == 'qualified':
+ key = '("%s","%s")' % ( a.getTargetNamespace(),
+ a.getAttribute('name') )
+ elif a.getAttribute('form') == 'unqualified':
+ key = '"%s"' % a.getAttribute('name')
+ else:
+ raise ContainerError, \
+ 'attribute form must be un/qualified %s' \
+ % a.getAttribute('form')
+
+ atd_list.append(\
+ '%s[%s] = %s' % (atd, key, tc)
+ )
+ elif a.isReference() and a.isAttributeGroup():
+ # flatten 'em out....
+ for ga in a.getAttributeGroup().getAttributeContent():
+ if not ga.isAttributeGroup():
+ attributes += (ga,)
+ continue
+ elif a.isReference():
+ try:
+ ga = a.getAttributeDeclaration()
+ except XMLSchema.SchemaError:
+ key = a.getAttribute('ref')
+ self.logger.debug('No schema item for attribute ref (%s, %s)' %key)
+ if key in self.built_in_refs: continue
+ raise
+
+ tp = None
+ if ga is not None:
+ tp = ga.getTypeDefinition('type')
+ key = '("%s","%s")' %(ga.getTargetNamespace(),
+ ga.getAttribute('name'))
+
+ if ga is None:
+ # TODO: probably SOAPENC:arrayType
+ key = '("%s","%s")' %(
+ a.getAttribute('ref').getTargetNamespace(),
+ a.getAttribute('ref').getName())
+ atd_list.append(\
+ '%s[%s] = ZSI.TC.String()' %(atd, key)
+ )
+ elif tp is None:
+ # built in simple type
+ try:
+ namespace,typeName = ga.getAttribute('type')
+ except TypeError, ex:
+ # TODO: attribute declaration could be anonymous type
+ # hack in something to work
+ atd_list.append(\
+ '%s[%s] = ZSI.TC.String()' %(atd, key)
+ )
+ else:
+ atd_list.append(\
+ '%s[%s] = %s()' %(atd, key,
+ BTI.get_typeclass(typeName, namespace))
+ )
+ else:
+ typeName = tp.getAttribute('name')
+ namespace = tp.getTargetNamespace()
+ alias = NAD.getAlias(namespace)
+ key = '("%s","%s")' \
+ % (ga.getTargetNamespace(),ga.getAttribute('name'))
+ atd_list.append(\
+ '%s[%s] = %s.%s(None)' \
+ % (atd, key, alias, type_class_name(typeName))
+ )
+ else:
+ raise TypeError, 'expecting an attribute: %s' %a.getItemTrace()
+
+ return formatted_attribute_list
+
+
+class ContainerError(Exception):
+ pass
+
+
+class ContainerBase:
+ '''Base class for all Containers.
+ func_aname -- function that takes name, and returns aname.
+ '''
+ func_aname = TextProtectAttributeName
+ func_aname = staticmethod(func_aname)
+ logger = _GetLogger("ContainerBase")
+
+ def __init__(self):
+ self.content = StringWriter('\n')
+ self.__setup = False
+ self.ns = None
+
+ def __str__(self):
+ return self.getvalue()
+
+ # - string content methods
+ def mangle(self, s):
+ '''class/variable name illegalities
+ '''
+ return TextProtect(s)
+
+ def write(self, s):
+ self.content.write(s)
+
+ def writeArray(self, a):
+ self.content.write('\n'.join(a))
+
+ def _setContent(self):
+ '''override in subclasses. formats the content in the desired way.
+ '''
+ raise NotImplementedError, 'abstract method not implemented'
+
+ def getvalue(self):
+ if not self.__setup:
+ self._setContent()
+ self.__setup = True
+
+ return self.content.getvalue()
+
+ # - namespace utility methods
+ def getNSAlias(self):
+ if self.ns:
+ return NAD.getAlias(self.ns)
+ raise ContainerError, 'no self.ns attr defined in %s' % self.__class__
+
+ def getNSModuleName(self):
+ if self.ns:
+ return NAD.getModuleName(self.ns)
+ raise ContainerError, 'no self.ns attr defined in %s' % self.__class__
+
+ def getAttributeName(self, name):
+ '''represents the aname
+ '''
+ if self.func_aname is None:
+ return name
+ assert callable(self.func_aname), \
+ 'expecting callable method for attribute func_aname, not %s' %type(self.func_aname)
+ f = self.func_aname
+ return f(name)
+
+
+# -- containers for services file components
+
+class ServiceContainerBase(ContainerBase):
+ clientClassSuffix = "SOAP"
+ logger = _GetLogger("ServiceContainerBase")
+
+
+class ServiceHeaderContainer(ServiceContainerBase):
+ imports = ['\nimport urlparse, types',
+ 'from ZSI.TCcompound import ComplexType, Struct',
+ 'from ZSI import client',
+ 'import ZSI'
+ ]
+ logger = _GetLogger("ServiceHeaderContainer")
+
+ def __init__(self, do_extended=False):
+ ServiceContainerBase.__init__(self)
+
+ self.basic = self.imports[:]
+ self.types = None
+ self.messages = None
+ self.extras = []
+ self.do_extended = do_extended
+
+ def setTypesModuleName(self, module):
+ self.types = module
+
+ def setMessagesModuleName(self, module):
+ self.messages = module
+
+ def appendImport(self, statement):
+ '''append additional import statement(s).
+ import_stament -- tuple or list or str
+ '''
+ if type(statement) in (list,tuple):
+ self.extras += statement
+ else:
+ self.extras.append(statement)
+
+ def _setContent(self):
+ if self.messages:
+ self.write('from %s import *' % self.messages)
+ if self.types:
+ self.write('from %s import *' % self.types)
+
+ imports = self.basic[:]
+ imports += self.extras
+ self.writeArray(imports)
+
+
+class ServiceLocatorContainer(ServiceContainerBase):
+ logger = _GetLogger("ServiceLocatorContainer")
+
+ def __init__(self):
+ ServiceContainerBase.__init__(self)
+ self.serviceName = None
+ self.portInfo = []
+ self.locatorName = None
+ self.portMethods = []
+
+ def setUp(self, service):
+ assert isinstance(service, WSDLTools.Service), \
+ 'expecting WDSLTools.Service instance.'
+
+ self.serviceName = service.name
+ for p in service.ports:
+ try:
+ ab = p.getAddressBinding()
+ except WSDLTools.WSDLError, ex:
+ self.logger.warning('Skip port(%s), missing address binding' %p.name)
+ continue
+ if isinstance(ab, WSDLTools.SoapAddressBinding) is False:
+ self.logger.warning('Skip port(%s), not a SOAP-1.1 address binding' %p.name)
+ continue
+
+ info = (p.getBinding().getPortType().name, p.getBinding().name, ab.location)
+ self.portInfo.append(info)
+
+ def getLocatorName(self):
+ '''return class name of generated locator.
+ '''
+ return self.locatorName
+
+ def getPortMethods(self):
+ '''list of get port accessor methods of generated locator class.
+ '''
+ return self.portMethods
+
+ def _setContent(self):
+ if not self.serviceName:
+ raise ContainerError, 'no service name defined!'
+
+ self.serviceName = self.mangle(self.serviceName)
+ self.locatorName = '%sLocator' %self.serviceName
+ locator = ['# Locator', 'class %s:' %self.locatorName, ]
+ self.portMethods = []
+ for p in self.portInfo:
+ ptName = NC_to_CN(p[0])
+ bName = NC_to_CN(p[1])
+ sAdd = p[2]
+ method = 'get%s' %ptName
+
+ pI = [
+ '%s%s_address = "%s"' % (ID1, ptName, sAdd),
+ '%sdef get%sAddress(self):' % (ID1, ptName),
+ '%sreturn %sLocator.%s_address' % (ID2,
+ self.serviceName,ptName),
+ '%sdef %s(self, url=None, **kw):' %(ID1, method),
+ '%sreturn %s%s(url or %sLocator.%s_address, **kw)' \
+ % (ID2, bName, self.clientClassSuffix, self.serviceName, ptName),
+ ]
+ self.portMethods.append(method)
+ locator += pI
+
+ self.writeArray(locator)
+
+
+class ServiceOperationContainer(ServiceContainerBase):
+ logger = _GetLogger("ServiceOperationContainer")
+
+ def __init__(self, useWSA=False, do_extended=False):
+ '''Parameters:
+ useWSA -- boolean, enable ws-addressing
+ do_extended -- boolean
+ '''
+ ServiceContainerBase.__init__(self)
+ self.useWSA = useWSA
+ self.do_extended = do_extended
+
+ def hasInput(self):
+ return self.inputName is not None
+
+ def hasOutput(self):
+ return self.outputName is not None
+
+ def isRPC(self):
+ return IsRPC(self.binding_operation)
+
+ def isLiteral(self, input=True):
+ msgrole = self.binding_operation.input
+ if input is False:
+ msgrole = self.binding_operation.output
+ return IsLiteral(msgrole)
+
+ def isSimpleType(self, input=True):
+ if input is False:
+ return self.outputSimpleType
+ return self.inputSimpleType
+
+ def getOperation(self):
+ return self.port.operations.get(self.name)
+
+ def getBOperation(self):
+ return self.port.get(self.name)
+
+ def getOperationName(self):
+ return self.name
+
+ def setUp(self, item):
+ '''
+ Parameters:
+ item -- WSDLTools BindingOperation instance.
+ '''
+ if not isinstance(item, WSDLTools.OperationBinding):
+ raise TypeError, 'Expecting WSDLTools Operation instance'
+
+ if not item.input:
+ raise WSDLFormatError('No <input/> in <binding name="%s"><operation name="%s">' %(
+ item.getBinding().name, item.name))
+
+ self.name = None
+ self.port = None
+ self.soapaction = None
+ self.inputName = None
+ self.outputName = None
+ self.inputSimpleType = None
+ self.outputSimpleType = None
+ self.inputAction = None
+ self.outputAction = None
+ self.port = port = item.getBinding().getPortType()
+ self._wsdl = item.getWSDL()
+ self.name = name = item.name
+ self.binding_operation = bop = item
+
+ op = port.operations.get(name)
+ if op is None:
+ raise WSDLFormatError(
+ '<portType name="%s"/> no match for <binding name="%s"><operation name="%s">' %(
+ port.name, item.getBinding().name, item.name))
+
+ soap_bop = bop.findBinding(WSDLTools.SoapOperationBinding)
+ if soap_bop is None:
+ raise SOAPBindingError, 'expecting SOAP Bindings'
+
+ self.soapaction = soap_bop.soapAction
+ sbody = bop.input.findBinding(WSDLTools.SoapBodyBinding)
+ if not sbody:
+ raise SOAPBindingError('Missing <binding name="%s"><operation name="%s"><input><soap:body>' %(
+ port.binding.name, bop.name))
+
+ self.encodingStyle = None
+ if sbody.use == 'encoded':
+ assert sbody.encodingStyle == SOAP.ENC,\
+ 'Supporting encodingStyle=%s, not %s'%(SOAP.ENC, sbody.encodingStyle)
+ self.encodingStyle = sbody.encodingStyle
+
+ self.inputName = op.getInputMessage().name
+ self.inputSimpleType = \
+ FromMessageGetSimpleElementDeclaration(op.getInputMessage())
+ self.inputAction = op.getInputAction()
+
+ if bop.output is not None:
+ sbody = bop.output.findBinding(WSDLTools.SoapBodyBinding)
+ if not item.output:
+ raise WSDLFormatError, "Operation %s, no match for output binding" %name
+
+ self.outputName = op.getOutputMessage().name
+ self.outputSimpleType = \
+ FromMessageGetSimpleElementDeclaration(op.getOutputMessage())
+ self.outputAction = op.getOutputAction()
+
+ def _setContent(self):
+ '''create string representation of operation.
+ '''
+ kwstring = 'kw = {}'
+ tCheck = 'if isinstance(request, %s) is False:' % self.inputName
+ bindArgs = ''
+ if self.encodingStyle is not None:
+ bindArgs = 'encodingStyle="%s", ' %self.encodingStyle
+
+ if self.useWSA:
+ wsactionIn = 'wsaction = "%s"' % self.inputAction
+ wsactionOut = 'wsaction = "%s"' % self.outputAction
+ bindArgs += 'wsaction=wsaction, endPointReference=self.endPointReference, '
+ responseArgs = ', wsaction=wsaction'
+ else:
+ wsactionIn = '# no input wsaction'
+ wsactionOut = '# no output wsaction'
+ responseArgs = ''
+
+ bindArgs += '**kw)'
+
+ if self.do_extended:
+ inputName = self.getOperation().getInputMessage().name
+ wrap_str = ""
+ partsList = self.getOperation().getInputMessage().parts.values()
+ try:
+ subNames = GetPartsSubNames(partsList, self._wsdl)
+ except TypeError, ex:
+ raise Wsdl2PythonError,\
+ "Extended generation failure: only supports doc/lit, "\
+ +"and all element attributes (<message><part element="\
+ +"\"my:GED\"></message>) must refer to single global "\
+ +"element declaration with complexType content. "\
+ +"\n\n**** TRY WITHOUT EXTENDED ****\n"
+
+ args = []
+ for pa in subNames:
+ args += pa
+
+ for arg in args:
+ wrap_str += "%srequest.%s = %s\n" % (ID2,
+ self.getAttributeName(arg),
+ self.mangle(arg))
+
+ #args = [pa.name for pa in self.getOperation().getInputMessage().parts.values()]
+ argsStr = ",".join(args)
+ if len(argsStr) > 1: # add inital comma if args exist
+ argsStr = ", " + argsStr
+
+ method = [
+ '%s# op: %s' % (ID1, self.getOperation().getInputMessage()),
+ '%sdef %s(self%s):' % (ID1, self.name, argsStr),
+ '\n%srequest = %s()' % (ID2, self.inputName),
+ '%s' % (wrap_str),
+ '%s%s' % (ID2, kwstring),
+ '%s%s' % (ID2, wsactionIn),
+ '%sself.binding.Send(None, None, request, soapaction="%s", %s'\
+ %(ID2, self.soapaction, bindArgs),
+ ]
+ else:
+ method = [
+ '%s# op: %s' % (ID1, self.name),
+ '%sdef %s(self, request):' % (ID1, self.name),
+ '%s%s' % (ID2, tCheck),
+ '%sraise TypeError, "%%s incorrect request type" %% (%s)' %(ID3, 'request.__class__'),
+ '%s%s' % (ID2, kwstring),
+ '%s%s' % (ID2, wsactionIn),
+ '%sself.binding.Send(None, None, request, soapaction="%s", %s'\
+ %(ID2, self.soapaction, bindArgs),
+ ]
+
+ #
+ # BP 1.0: rpc/literal
+ # WSDL 1.1 Section 3.5 could be interpreted to mean the RPC response
+ # wrapper element must be named identical to the name of the
+ # wsdl:operation.
+ # R2729
+
+ #
+ # SOAP-1.1 Note: rpc/encoded
+ # Each parameter accessor has a name corresponding to the name of the
+ # parameter and type corresponding to the type of the parameter. The name of
+ # the return value accessor is not significant. Likewise, the name of the struct is
+ # not significant. However, a convention is to name it after the method name
+ # with the string "Response" appended.
+ #
+ if self.outputName:
+ response = ['%s%s' % (ID2, wsactionOut),]
+ if self.isRPC() and not self.isLiteral():
+ # rpc/encoded Replace wrapper name with None
+ response.append(\
+ '%stypecode = Struct(pname=None, ofwhat=%s.typecode.ofwhat, pyclass=%s.typecode.pyclass)' %(
+ ID2, self.outputName, self.outputName)
+ )
+ response.append(\
+ '%sresponse = self.binding.Receive(typecode%s)' %(
+ ID2, responseArgs)
+ )
+ else:
+ response.append(\
+ '%sresponse = self.binding.Receive(%s.typecode%s)' %(
+ ID2, self.outputName, responseArgs)
+ )
+
+ if self.outputSimpleType:
+ response.append('%sreturn %s(response)' %(ID2, self.outputName))
+ else:
+ if self.do_extended:
+ partsList = self.getOperation().getOutputMessage().parts.values()
+ subNames = GetPartsSubNames(partsList, self._wsdl)
+ args = []
+ for pa in subNames:
+ args += pa
+
+ for arg in args:
+ response.append('%s%s = response.%s' % (ID2, self.mangle(arg), self.getAttributeName(arg)) )
+ margs = ",".join(args)
+ response.append("%sreturn %s" % (ID2, margs) )
+ else:
+ response.append('%sreturn response' %ID2)
+ method += response
+
+ self.writeArray(method)
+
+
+class ServiceOperationsClassContainer(ServiceContainerBase):
+ '''
+ class variables:
+ readerclass --
+ writerclass --
+ operationclass -- representation of each operation.
+ '''
+ readerclass = None
+ writerclass = None
+ operationclass = ServiceOperationContainer
+ logger = _GetLogger("ServiceOperationsClassContainer")
+
+ def __init__(self, useWSA=False, do_extended=False, wsdl=None):
+ '''Parameters:
+ name -- binding name
+ property -- resource properties
+ useWSA -- boolean, enable ws-addressing
+ name -- binding name
+ '''
+ ServiceContainerBase.__init__(self)
+ self.useWSA = useWSA
+ self.rProp = None
+ self.bName = None
+ self.operations = None
+ self.do_extended = do_extended
+ self._wsdl = wsdl # None unless do_extended == True
+
+ def setReaderClass(cls, className):
+ '''specify a reader class name, this must be imported
+ in service module.
+ '''
+ cls.readerclass = className
+ setReaderClass = classmethod(setReaderClass)
+
+ def setWriterClass(cls, className):
+ '''specify a writer class name, this must be imported
+ in service module.
+ '''
+ cls.writerclass = className
+ setWriterClass = classmethod(setWriterClass)
+
+ def setOperationClass(cls, className):
+ '''specify an operation container class name.
+ '''
+ cls.operationclass = className
+ setOperationClass = classmethod(setOperationClass)
+
+ def setUp(self, port):
+ '''This method finds all SOAP Binding Operations, it will skip
+ all bindings that are not SOAP.
+ port -- WSDL.Port instance
+ '''
+ assert isinstance(port, WSDLTools.Port), 'expecting WSDLTools Port instance'
+
+ self.operations = []
+ self.bName = port.getBinding().name
+ self.rProp = port.getBinding().getPortType().getResourceProperties()
+ soap_binding = port.getBinding().findBinding(WSDLTools.SoapBinding)
+ if soap_binding is None:
+ raise Wsdl2PythonError,\
+ 'port(%s) missing WSDLTools.SoapBinding' %port.name
+
+ for bop in port.getBinding().operations:
+ soap_bop = bop.findBinding(WSDLTools.SoapOperationBinding)
+ if soap_bop is None:
+ self.logger.warning(\
+ 'Skip port(%s) operation(%s) no SOAP Binding Operation'\
+ %(port.name, bop.name),
+ )
+ continue
+
+ #soapAction = soap_bop.soapAction
+ if bop.input is not None:
+ soapBodyBind = bop.input.findBinding(WSDLTools.SoapBodyBinding)
+ if soapBodyBind is None:
+ self.logger.warning(\
+ 'Skip port(%s) operation(%s) Bindings(%s) not supported'\
+ %(port.name, bop.name, bop.extensions)
+ )
+ continue
+
+ op = port.getBinding().getPortType().operations.get(bop.name)
+ if op is None:
+ raise Wsdl2PythonError,\
+ 'no matching portType/Binding operation(%s)' % bop.name
+
+ c = self.operationclass(useWSA=self.useWSA,
+ do_extended=self.do_extended)
+ c.setUp(bop)
+ self.operations.append(c)
+
+ def _setContent(self):
+ if self.useWSA is True:
+ ctorArgs = 'endPointReference=None, **kw'
+ epr = 'self.endPointReference = endPointReference'
+ else:
+ ctorArgs = '**kw'
+ epr = '# no ws-addressing'
+
+ if self.rProp:
+ rprop = 'kw.setdefault("ResourceProperties", ("%s","%s"))'\
+ %(self.rProp[0], self.rProp[1])
+ else:
+ rprop = '# no resource properties'
+
+ methods = [
+ '# Methods',
+ 'class %s%s:' % (NC_to_CN(self.bName), self.clientClassSuffix),
+ '%sdef __init__(self, url, %s):' % (ID1, ctorArgs),
+ '%skw.setdefault("readerclass", %s)' % (ID2, self.readerclass),
+ '%skw.setdefault("writerclass", %s)' % (ID2, self.writerclass),
+ '%s%s' % (ID2, rprop),
+ '%sself.binding = client.Binding(url=url, **kw)' %ID2,
+ '%s%s' % (ID2,epr),
+ ]
+
+ for op in self.operations:
+ methods += [ op.getvalue() ]
+
+ self.writeArray(methods)
+
+
+
+class MessageContainerInterface:
+ logger = _GetLogger("MessageContainerInterface")
+
+ def setUp(self, port, soc, input):
+ '''sets the attribute _simple which represents a
+ primitive type message represents, or None if not primitive.
+
+ soc -- WSDLTools.ServiceOperationContainer instance
+ port -- WSDLTools.Port instance
+ input-- boolean, input messasge or output message of operation.
+ '''
+ raise NotImplementedError, 'Message container must implemented setUp.'
+
+
+class ServiceDocumentLiteralMessageContainer(ServiceContainerBase, MessageContainerInterface):
+ logger = _GetLogger("ServiceDocumentLiteralMessageContainer")
+
+ def __init__(self, do_extended=False):
+
+ ServiceContainerBase.__init__(self)
+ self.do_extended=do_extended
+
+ def setUp(self, port, soc, input):
+ content = self.content
+ # TODO: check soapbody for part name
+ simple = self._simple = soc.isSimpleType(soc.getOperationName())
+ name = soc.getOperationName()
+
+ # Document/literal
+ operation = port.getBinding().getPortType().operations.get(name)
+ bop = port.getBinding().operations.get(name)
+ soapBodyBind = None
+ if input is True:
+ soapBodyBind = bop.input.findBinding(WSDLTools.SoapBodyBinding)
+ message = operation.getInputMessage()
+ else:
+ soapBodyBind = bop.output.findBinding(WSDLTools.SoapBodyBinding)
+ message = operation.getOutputMessage()
+
+ # using underlying data structure to avoid phantom problem.
+# parts = message.parts.data.values()
+# if len(parts) > 1:
+# raise Wsdl2PythonError, 'not suporting multi part doc/lit msgs'
+ if len(message.parts) == 0:
+ raise Wsdl2PythonError, 'must specify part for doc/lit msg'
+
+ p = None
+ if soapBodyBind.parts is not None:
+ if len(soapBodyBind.parts) > 1:
+ raise Wsdl2PythonError,\
+ 'not supporting multiple parts in soap body'
+ if len(soapBodyBind.parts) == 0:
+ return
+
+ p = message.parts.get(soapBodyBind.parts[0])
+
+ # XXX: Allow for some slop
+ p = p or message.parts[0]
+
+ if p.type:
+ raise Wsdl2PythonError, 'no doc/lit suport for <part type>'
+
+ if not p.element:
+ return
+
+ content.ns = p.element[0]
+ content.pName = p.element[1]
+ content.mName = message.name
+
+ def _setContent(self):
+ '''create string representation of doc/lit message container. If
+ message element is simple(primitive), use python type as base class.
+ '''
+ try:
+ simple = self._simple
+ except AttributeError:
+ raise RuntimeError, 'call setUp first'
+
+ # TODO: Hidden contract. Must set self.ns before getNSAlias...
+ # File "/usr/local/python/lib/python2.4/site-packages/ZSI/generate/containers.py", line 625, in _setContent
+ # kw['message'],kw['prefix'],kw['typecode'] = \
+ # File "/usr/local/python/lib/python2.4/site-packages/ZSI/generate/containers.py", line 128, in getNSAlias
+ # raise ContainerError, 'no self.ns attr defined in %s' % self.__class__
+ # ZSI.generate.containers.ContainerError: no self.ns attr defined in ZSI.generate.containers.ServiceDocumentLiteralMessageContainer
+ #
+ self.ns = self.content.ns
+
+
+ kw = KW.copy()
+ kw['message'],kw['prefix'],kw['typecode'] = \
+ self.content.mName, self.getNSAlias(), element_class_name(self.content.pName)
+
+ # These messsages are just global element declarations
+ self.writeArray(['%(message)s = %(prefix)s.%(typecode)s().pyclass' %kw])
+
+
+class ServiceRPCEncodedMessageContainer(ServiceContainerBase, MessageContainerInterface):
+ logger = _GetLogger("ServiceRPCEncodedMessageContainer")
+
+ def setUp(self, port, soc, input):
+ '''
+ Instance Data:
+ op -- WSDLTools Operation instance
+ bop -- WSDLTools BindingOperation instance
+ input -- boolean input/output
+ '''
+ name = soc.getOperationName()
+ bop = port.getBinding().operations.get(name)
+ op = port.getBinding().getPortType().operations.get(name)
+
+ assert op is not None, 'port has no operation %s' %name
+ assert bop is not None, 'port has no binding operation %s' %name
+
+ self.input = input
+ self.op = op
+ self.bop = bop
+
+ def _setContent(self):
+ try:
+ self.op
+ except AttributeError:
+ raise RuntimeError, 'call setUp first'
+
+ pname = self.op.name
+ msgRole = self.op.input
+ msgRoleB = self.bop.input
+ if self.input is False:
+ pname = '%sResponse' %self.op.name
+ msgRole = self.op.output
+ msgRoleB = self.bop.output
+
+ sbody = msgRoleB.findBinding(WSDLTools.SoapBodyBinding)
+ if not sbody or not sbody.namespace:
+ raise WSInteropError, WSISpec.R2717
+
+ assert sbody.use == 'encoded', 'Expecting use=="encoded"'
+ encodingStyle = sbody.encodingStyle
+
+ assert encodingStyle == SOAP.ENC,\
+ 'Supporting encodingStyle=%s, not %s' %(SOAP.ENC, encodingStyle)
+
+ namespace = sbody.namespace
+ tcb = MessageTypecodeContainer(\
+ tuple(msgRole.getMessage().parts.list),
+ )
+ ofwhat = '[%s]' %tcb.getTypecodeList()
+ pyclass = msgRole.getMessage().name
+
+ fdict = KW.copy()
+ fdict['nspname'] = sbody.namespace
+ fdict['pname'] = pname
+ fdict['pyclass'] = None
+ fdict['ofwhat'] = ofwhat
+ fdict['encoded'] = namespace
+
+ #if self.input is False:
+ # fdict['typecode'] = \
+ # 'Struct(pname=None, ofwhat=%(ofwhat)s, pyclass=%(pyclass)s, encoded="%(encoded)s")'
+ #else:
+ fdict['typecode'] = \
+ 'Struct(pname=("%(nspname)s","%(pname)s"), ofwhat=%(ofwhat)s, pyclass=%(pyclass)s, encoded="%(encoded)s")'
+
+ message = ['class %(pyclass)s:',
+ '%(ID1)sdef __init__(self):']
+
+ for aname in tcb.getAttributeNames():
+ message.append('%(ID2)sself.' + aname +' = None')
+ message.append('%(ID2)sreturn')
+
+ # TODO: This isn't a TypecodeContainerBase instance but it
+ # certaintly generates a pyclass and typecode.
+ #if self.metaclass is None:
+ if TypecodeContainerBase.metaclass is None:
+ fdict['pyclass'] = pyclass
+ fdict['typecode'] = fdict['typecode'] %fdict
+ message.append('%(pyclass)s.typecode = %(typecode)s')
+ else:
+ # Need typecode to be available when class is constructed.
+ fdict['typecode'] = fdict['typecode'] %fdict
+ fdict['pyclass'] = pyclass
+ fdict['metaclass'] = TypecodeContainerBase.metaclass
+ message.insert(0, '_%(pyclass)sTypecode = %(typecode)s')
+ message.insert(2, '%(ID1)stypecode = _%(pyclass)sTypecode')
+ message.insert(3, '%(ID1)s__metaclass__ = %(metaclass)s')
+ message.append('%(pyclass)s.typecode.pyclass = %(pyclass)s')
+
+ self.writeArray(map(lambda l: l %fdict, message))
+
+
+class ServiceRPCLiteralMessageContainer(ServiceContainerBase, MessageContainerInterface):
+ logger = _GetLogger("ServiceRPCLiteralMessageContainer")
+
+ def setUp(self, port, soc, input):
+ '''
+ Instance Data:
+ op -- WSDLTools Operation instance
+ bop -- WSDLTools BindingOperation instance
+ input -- boolean input/output
+ '''
+ name = soc.getOperationName()
+ bop = port.getBinding().operations.get(name)
+ op = port.getBinding().getPortType().operations.get(name)
+
+ assert op is not None, 'port has no operation %s' %name
+ assert bop is not None, 'port has no binding operation %s' %name
+
+ self.op = op
+ self.bop = bop
+ self.input = input
+
+ def _setContent(self):
+ try:
+ self.op
+ except AttributeError:
+ raise RuntimeError, 'call setUp first'
+
+ operation = self.op
+ input = self.input
+ pname = operation.name
+ msgRole = operation.input
+ msgRoleB = self.bop.input
+ if input is False:
+ pname = '%sResponse' %operation.name
+ msgRole = operation.output
+ msgRoleB = self.bop.output
+
+ sbody = msgRoleB.findBinding(WSDLTools.SoapBodyBinding)
+ if not sbody or not sbody.namespace:
+ raise WSInteropError, WSISpec.R2717
+
+ namespace = sbody.namespace
+ tcb = MessageTypecodeContainer(\
+ tuple(msgRole.getMessage().parts.list),
+ )
+ ofwhat = '[%s]' %tcb.getTypecodeList()
+ pyclass = msgRole.getMessage().name
+
+ fdict = KW.copy()
+ fdict['nspname'] = sbody.namespace
+ fdict['pname'] = pname
+ fdict['pyclass'] = None
+ fdict['ofwhat'] = ofwhat
+ fdict['encoded'] = namespace
+ fdict['typecode'] = \
+ 'Struct(pname=("%(nspname)s","%(pname)s"), ofwhat=%(ofwhat)s, pyclass=%(pyclass)s, encoded="%(encoded)s")'
+
+ message = ['class %(pyclass)s:',
+ '%(ID1)sdef __init__(self):']
+
+ for aname in tcb.getAttributeNames():
+ message.append('%(ID2)sself.' + aname +' = None')
+ message.append('%(ID2)sreturn')
+
+ # TODO: This isn't a TypecodeContainerBase instance but it
+ # certaintly generates a pyclass and typecode.
+ #if self.metaclass is None:
+ if TypecodeContainerBase.metaclass is None:
+ fdict['pyclass'] = pyclass
+ fdict['typecode'] = fdict['typecode'] %fdict
+ message.append('%(pyclass)s.typecode = %(typecode)s')
+ else:
+ # Need typecode to be available when class is constructed.
+ fdict['typecode'] = fdict['typecode'] %fdict
+ fdict['pyclass'] = pyclass
+ fdict['metaclass'] = TypecodeContainerBase.metaclass
+ message.insert(0, '_%(pyclass)sTypecode = %(typecode)s')
+ message.insert(2, '%(ID1)stypecode = _%(pyclass)sTypecode')
+ message.insert(3, '%(ID1)s__metaclass__ = %(metaclass)s')
+ message.append('%(pyclass)s.typecode.pyclass = %(pyclass)s')
+
+ self.writeArray(map(lambda l: l %fdict, message))
+
+
+TypesContainerBase = ContainerBase
+
+
+class TypesHeaderContainer(TypesContainerBase):
+ '''imports for all generated types modules.
+ '''
+ imports = [
+ 'import ZSI',
+ 'import ZSI.TCcompound',
+ 'from ZSI.schema import LocalElementDeclaration, ElementDeclaration, TypeDefinition, GTD, GED',
+ ]
+ logger = _GetLogger("TypesHeaderContainer")
+
+ def _setContent(self):
+ self.writeArray(TypesHeaderContainer.imports)
+
+
+NamespaceClassContainerBase = TypesContainerBase
+
+
+class NamespaceClassHeaderContainer(NamespaceClassContainerBase):
+ logger = _GetLogger("NamespaceClassHeaderContainer")
+
+ def _setContent(self):
+
+ head = [
+ '#' * 30,
+ '# targetNamespace',
+ '# %s' % self.ns,
+ '#' * 30 + '\n',
+ 'class %s:' % self.getNSAlias(),
+ '%stargetNamespace = "%s"' % (ID1, self.ns)
+ ]
+
+ self.writeArray(head)
+
+class NamespaceClassFooterContainer(NamespaceClassContainerBase):
+ logger = _GetLogger("NamespaceClassFooterContainer")
+
+ def _setContent(self):
+
+ foot = [
+ '# end class %s (tns: %s)' % (self.getNSAlias(), self.ns),
+ ]
+
+ self.writeArray(foot)
+
+
+BTI = BaseTypeInterpreter()
+class TypecodeContainerBase(TypesContainerBase):
+ '''Base class for all classes representing anything
+ with element content.
+
+ class variables:
+ mixed_content_aname -- text content will be placed in this attribute.
+ attributes_aname -- attributes will be placed in this attribute.
+ metaclass -- set this attribute to specify a pyclass __metaclass__
+ '''
+ mixed_content_aname = 'text'
+ attributes_aname = 'attrs'
+ metaclass = None
+ lazy = False
+ logger = _GetLogger("TypecodeContainerBase")
+
+ def __init__(self, do_extended=False, extPyClasses=None):
+ TypesContainerBase.__init__(self)
+ self.name = None
+
+ # attrs for model groups and others with elements, tclists, etc...
+ self.allOptional = False
+ self.mgContent = None
+ self.contentFlattened = False
+ self.elementAttrs = []
+ self.tcListElements = []
+ self.tcListSet = False
+
+ self.localTypes = []
+
+ # used when processing nested anonymous types
+ self.parentClass = None
+
+ # used when processing attribute content
+ self.mixed = False
+ self.extraFlags = ''
+ self.attrComponents = None
+
+ # --> EXTENDED
+ # Used if an external pyclass was specified for this type.
+ self.do_extended = do_extended
+ if extPyClasses is None:
+ self.extPyClasses = {}
+ else:
+ self.extPyClasses = extPyClasses
+ # <--
+
+ def getvalue(self):
+ out = ContainerBase.getvalue(self)
+ for item in self.localTypes:
+ content = None
+ assert True is item.isElement() is item.isLocal(), 'expecting local elements only'
+
+ etp = item.content
+ qName = item.getAttribute('type')
+ if not qName:
+ etp = item.content
+ local = True
+ else:
+ etp = item.getTypeDefinition('type')
+
+ if etp is None:
+ if local is True:
+ content = ElementLocalComplexTypeContainer(do_extended=self.do_extended)
+ else:
+ content = ElementSimpleTypeContainer()
+ elif etp.isLocal() is False:
+ content = ElementGlobalDefContainer()
+ elif etp.isSimple() is True:
+ content = ElementLocalSimpleTypeContainer()
+ elif etp.isComplex():
+ content = ElementLocalComplexTypeContainer(do_extended=self.do_extended)
+ else:
+ raise Wsdl2PythonError, "Unknown element declaration: %s" %item.getItemTrace()
+
+ content.setUp(item)
+
+ out += '\n\n'
+ if self.parentClass:
+ content.parentClass = \
+ '%s.%s' %(self.parentClass, self.getClassName())
+ else:
+ content.parentClass = '%s.%s' %(self.getNSAlias(), self.getClassName())
+
+ for l in content.getvalue().split('\n'):
+ if l: out += '%s%s\n' % (ID1, l)
+ else: out += '\n'
+
+ out += '\n\n'
+
+ return out
+
+ def getAttributeName(self, name):
+ '''represents the aname
+ '''
+ if self.func_aname is None:
+ return name
+ assert callable(self.func_aname), \
+ 'expecting callable method for attribute func_aname, not %s' %type(self.func_aname)
+ f = self.func_aname
+ return f(name)
+
+ def getMixedTextAName(self):
+ '''returns an aname representing mixed text content.
+ '''
+ return self.getAttributeName(self.mixed_content_aname)
+
+ def getClassName(self):
+
+ if not self.name:
+ raise ContainerError, 'self.name not defined!'
+ if not hasattr(self.__class__, 'type'):
+ raise ContainerError, 'container type not defined!'
+
+ #suffix = self.__class__.type
+ if self.__class__.type == DEF:
+ classname = type_class_name(self.name)
+ elif self.__class__.type == DEC:
+ classname = element_class_name(self.name)
+
+ return self.mangle( classname )
+
+ # --> EXTENDED
+ def hasExtPyClass(self):
+ if self.name in self.extPyClasses:
+ return True
+ else:
+ return False
+ # <--
+
+ def getPyClass(self):
+ '''Name of generated inner class that will be specified as pyclass.
+ '''
+ # --> EXTENDED
+ if self.hasExtPyClass():
+ classInfo = self.extPyClasses[self.name]
+ return ".".join(classInfo)
+ # <--
+
+ return 'Holder'
+
+ def getPyClassDefinition(self):
+ '''Return a list containing pyclass definition.
+ '''
+ kw = KW.copy()
+
+ # --> EXTENDED
+ if self.hasExtPyClass():
+ classInfo = self.extPyClasses[self.name]
+ kw['classInfo'] = classInfo[0]
+ return ["%(ID3)simport %(classInfo)s" %kw ]
+ # <--
+
+ kw['pyclass'] = self.getPyClass()
+ definition = []
+ definition.append('%(ID3)sclass %(pyclass)s:' %kw)
+ if self.metaclass is not None:
+ kw['type'] = self.metaclass
+ definition.append('%(ID4)s__metaclass__ = %(type)s' %kw)
+ definition.append('%(ID4)stypecode = self' %kw)
+
+ #TODO: Remove pyclass holder __init__ -->
+ definition.append('%(ID4)sdef __init__(self):' %kw)
+ definition.append('%(ID5)s# pyclass' %kw)
+
+ # JRB HACK need to call _setElements via getElements
+ self._setUpElements()
+
+ # JRB HACK need to indent additional one level
+ for el in self.elementAttrs:
+ kw['element'] = el
+ definition.append('%(ID2)s%(element)s' %kw)
+ definition.append('%(ID5)sreturn' %kw)
+ # <--
+
+ # pyclass descriptive name
+ if self.name is not None:
+ kw['name'] = self.name
+ definition.append(\
+ '%(ID3)s%(pyclass)s.__name__ = "%(name)s_Holder"' %kw
+ )
+
+ return definition
+
+ def nsuriLogic(self):
+ '''set a variable "ns" that represents the targetNamespace in
+ which this item is defined. Used for namespacing local elements.
+ '''
+ if self.parentClass:
+ return 'ns = %s.%s.schema' %(self.parentClass, self.getClassName())
+ return 'ns = %s.%s.schema' %(self.getNSAlias(), self.getClassName())
+
+ def schemaTag(self):
+ if self.ns is not None:
+ return 'schema = "%s"' % self.ns
+ raise ContainerError, 'failed to set schema targetNamespace(%s)' %(self.__class__)
+
+ def typeTag(self):
+ if self.name is not None:
+ return 'type = (schema, "%s")' % self.name
+ raise ContainerError, 'failed to set type name(%s)' %(self.__class__)
+
+ def literalTag(self):
+ if self.name is not None:
+ return 'literal = "%s"' % self.name
+ raise ContainerError, 'failed to set element name(%s)' %(self.__class__)
+
+ def getExtraFlags(self):
+ if self.mixed:
+ self.extraFlags += 'mixed=True, mixed_aname="%s", ' %self.getMixedTextAName()
+
+ return self.extraFlags
+
+ def simpleConstructor(self, superclass=None):
+
+ if superclass:
+ return '%s.__init__(self, **kw)' % superclass
+ else:
+ return 'def __init__(self, **kw):'
+
+ def pnameConstructor(self, superclass=None):
+
+ if superclass:
+ return '%s.__init__(self, pname, **kw)' % superclass
+ else:
+ return 'def __init__(self, pname, **kw):'
+
+
+ def _setUpElements(self):
+ """TODO: Remove this method
+
+ This method ONLY sets up the instance attributes.
+ Dependency instance attribute:
+ mgContent -- expected to be either a complex definition
+ with model group content, a model group, or model group
+ content. TODO: should only support the first two.
+ """
+ self.logger.debug("_setUpElements: %s" %self._item.getItemTrace())
+ if hasattr(self, '_done'):
+ #return '\n'.join(self.elementAttrs)
+ return
+
+ self._done = True
+ flat = []
+ content = self.mgContent
+ if type(self.mgContent) is not tuple:
+ mg = self.mgContent
+ if not mg.isModelGroup():
+ mg = mg.content
+
+ content = mg.content
+ if mg.isAll():
+ flat = content
+ content = []
+ elif mg.isModelGroup() and mg.isDefinition():
+ mg = mg.content
+ content = mg.content
+
+ idx = 0
+ content = list(content)
+ while idx < len(content):
+ c = orig = content[idx]
+ if c.isElement():
+ flat.append(c)
+ idx += 1
+ continue
+
+ if c.isReference() and c.isModelGroup():
+ c = c.getModelGroupReference()
+
+ if c.isDefinition() and c.isModelGroup():
+ c = c.content
+
+ if c.isSequence() or c.isChoice():
+ begIdx = idx
+ endIdx = begIdx + len(c.content)
+ for i in range(begIdx, endIdx):
+ content.insert(i, c.content[i-begIdx])
+
+ content.remove(orig)
+ continue
+
+ raise ContainerError, 'unexpected schema item: %s' %c.getItemTrace()
+
+ for c in flat:
+ if c.isDeclaration() and c.isElement():
+ defaultValue = "None"
+ parent = c
+ defs = []
+ # stop recursion via global ModelGroupDefinition
+ while defs.count(parent) <= 1:
+ maxOccurs = parent.getAttribute('maxOccurs')
+ if maxOccurs == 'unbounded' or int(maxOccurs) > 1:
+ defaultValue = "[]"
+ break
+
+ parent = parent._parent()
+ if not parent.isModelGroup():
+ break
+
+ if parent.isReference():
+ parent = parent.getModelGroupReference()
+
+ if parent.isDefinition():
+ parent = parent.content
+ defs.append(parent)
+
+ if None == c.getAttribute('name') and c.isWildCard():
+ e = '%sself.%s = %s' %(ID3,
+ self.getAttributeName('any'), defaultValue)
+ else:
+ e = '%sself.%s = %s' %(ID3,
+ self.getAttributeName(c.getAttribute('name')), defaultValue)
+ self.elementAttrs.append(e)
+ continue
+
+ # TODO: This seems wrong
+ if c.isReference():
+ e = '%sself._%s = None' %(ID3,
+ self.mangle(c.getAttribute('ref')[1]))
+ self.elementAttrs.append(e)
+ continue
+
+ raise ContainerError, 'unexpected item: %s' % c.getItemTrace()
+
+ #return '\n'.join(self.elementAttrs)
+ return
+
+ def _setTypecodeList(self):
+ """generates ofwhat content, minOccurs/maxOccurs facet generation.
+ Dependency instance attribute:
+ mgContent -- expected to be either a complex definition
+ with model group content, a model group, or model group
+ content. TODO: should only support the first two.
+ localTypes -- produce local class definitions later
+ tcListElements -- elements, local/global
+ """
+ self.logger.debug("_setTypecodeList(%r): %s" %
+ (self.mgContent, self._item.getItemTrace()))
+
+ flat = []
+ content = self.mgContent
+
+ #TODO: too much slop permitted here, impossible
+ # to tell what is going on.
+
+ if type(content) is not tuple:
+ mg = content
+ if not mg.isModelGroup():
+ raise Wsdl2PythonErr("Expecting ModelGroup: %s" %
+ mg.getItemTrace())
+
+ self.logger.debug("ModelGroup(%r) contents(%r): %s" %
+ (mg, mg.content, mg.getItemTrace()))
+
+ #<group ref>
+ if mg.isReference():
+ raise RuntimeError("Unexpected modelGroup reference: %s" %
+ mg.getItemTrace())
+
+ #<group name>
+ if mg.isDefinition():
+ mg = mg.content
+
+ if mg.isAll():
+ flat = mg.content
+ content = []
+ elif mg.isSequence():
+ content = mg.content
+ elif mg.isChoice():
+ content = mg.content
+ else:
+ raise RuntimeError("Unknown schema item")
+
+ idx = 0
+ content = list(content)
+ self.logger.debug("content: %r" %content)
+ while idx < len(content):
+ c = orig = content[idx]
+ if c.isElement():
+ flat.append(c)
+ idx += 1
+ continue
+
+ if c.isReference() and c.isModelGroup():
+ c = c.getModelGroupReference()
+
+ if c.isDefinition() and c.isModelGroup():
+ c = c.content
+
+ if c.isSequence() or c.isChoice():
+ begIdx = idx
+ endIdx = begIdx + len(c.content)
+ for i in range(begIdx, endIdx):
+ content.insert(i, c.content[i-begIdx])
+
+ content.remove(orig)
+ continue
+
+ raise ContainerError, 'unexpected schema item: %s' %c.getItemTrace()
+
+ # TODO: Need to store "parents" in a dict[id] = list(),
+ # because cannot follow references, but not currently
+ # a big concern.
+
+ self.logger.debug("flat: %r" %list(flat))
+ for c in flat:
+ tc = TcListComponentContainer()
+ # TODO: Remove _getOccurs
+ min,max,nil = self._getOccurs(c)
+ min = max = None
+ maxOccurs = 1
+
+ parent = c
+ defs = []
+ # stop recursion via global ModelGroupDefinition
+ while defs.count(parent) <= 1:
+ max = parent.getAttribute('maxOccurs')
+ if max == 'unbounded':
+ maxOccurs = '"%s"' %max
+ break
+
+ maxOccurs = int(max) * maxOccurs
+ parent = parent._parent()
+ if not parent.isModelGroup():
+ break
+
+ if parent.isReference():
+ parent = parent.getModelGroupReference()
+
+ if parent.isDefinition():
+ parent = parent.content
+ defs.append(parent)
+
+ del defs
+ parent = c
+ while 1:
+ minOccurs = int(parent.getAttribute('minOccurs'))
+ if minOccurs == 0 or parent.isChoice():
+ minOccurs = 0
+ break
+
+ parent = parent._parent()
+ if not parent.isModelGroup():
+ minOccurs = int(c.getAttribute('minOccurs'))
+ break
+
+ if parent.isReference():
+ parent = parent.getModelGroupReference()
+
+ if parent.isDefinition():
+ parent = parent.content
+
+ tc.setOccurs(minOccurs, maxOccurs, nil)
+ processContents = self._getProcessContents(c)
+ tc.setProcessContents(processContents)
+ if c.isDeclaration() and c.isElement():
+ global_type = c.getAttribute('type')
+ content = getattr(c, 'content', None)
+ if c.isLocal() and c.isQualified() is False:
+ tc.unQualified()
+
+ if c.isWildCard():
+ tc.setStyleAnyElement()
+ elif global_type is not None:
+ tc.name = c.getAttribute('name')
+ ns = global_type[0]
+ if ns in SCHEMA.XSD_LIST:
+ tpc = BTI.get_typeclass(global_type[1],global_type[0])
+ tc.klass = tpc
+# elif (self.ns,self.name) == global_type:
+# # elif self._isRecursiveElement(c)
+# # TODO: Remove this, it only works for 1 level.
+# tc.setStyleRecursion()
+ else:
+ tc.setGlobalType(*global_type)
+# tc.klass = '%s.%s' % (NAD.getAlias(ns),
+# type_class_name(global_type[1]))
+ del ns
+ elif content is not None and content.isLocal() and content.isComplex():
+ tc.name = c.getAttribute('name')
+ tc.klass = 'self.__class__.%s' % (element_class_name(tc.name))
+ #TODO: Not an element reference, confusing nomenclature
+ tc.setStyleElementReference()
+ self.localTypes.append(c)
+ elif content is not None and content.isLocal() and content.isSimple():
+ # Local Simple Type
+ tc.name = c.getAttribute('name')
+ tc.klass = 'self.__class__.%s' % (element_class_name(tc.name))
+ #TODO: Not an element reference, confusing nomenclature
+ tc.setStyleElementReference()
+ self.localTypes.append(c)
+ else:
+ raise ContainerError, 'unexpected item: %s' % c.getItemTrace()
+
+ elif c.isReference():
+ # element references
+ ref = c.getAttribute('ref')
+# tc.klass = '%s.%s' % (NAD.getAlias(ref[0]),
+# element_class_name(ref[1]) )
+ tc.setStyleElementReference()
+ tc.setGlobalType(*ref)
+ else:
+ raise ContainerError, 'unexpected item: %s' % c.getItemTrace()
+
+ self.tcListElements.append(tc)
+
+ def getTypecodeList(self):
+ if not self.tcListSet:
+# self._flattenContent()
+ self._setTypecodeList()
+ self.tcListSet = True
+
+ list = []
+ for e in self.tcListElements:
+ list.append(str(e))
+
+ return ', '.join(list)
+
+ # the following _methods() are utility methods used during
+ # TCList generation, et al.
+
+ def _getOccurs(self, e):
+
+ nillable = e.getAttribute('nillable')
+ if nillable == 'true':
+ nillable = True
+ else:
+ nillable = False
+
+ maxOccurs = e.getAttribute('maxOccurs')
+ if maxOccurs == 'unbounded':
+ maxOccurs = '"%s"' %maxOccurs
+
+ minOccurs = e.getAttribute('minOccurs')
+
+ if self.allOptional is True:
+ #JRB Hack
+ minOccurs = '0'
+ maxOccurs = '"unbounded"'
+
+ return minOccurs,maxOccurs,nillable
+
+ def _getProcessContents(self, e):
+ processContents = e.getAttribute('processContents')
+ return processContents
+
+ def getBasesLogic(self, indent):
+ try:
+ prefix = NAD.getAlias(self.sKlassNS)
+ except WsdlGeneratorError, ex:
+ # XSD or SOAP
+ raise
+
+ bases = []
+ bases.append(\
+ 'if %s.%s not in %s.%s.__bases__:'\
+ %(NAD.getAlias(self.sKlassNS), type_class_name(self.sKlass), self.getNSAlias(), self.getClassName()),
+ )
+ bases.append(\
+ '%sbases = list(%s.%s.__bases__)'\
+ %(ID1,self.getNSAlias(),self.getClassName()),
+ )
+ bases.append(\
+ '%sbases.insert(0, %s.%s)'\
+ %(ID1,NAD.getAlias(self.sKlassNS), type_class_name(self.sKlass) ),
+ )
+ bases.append(\
+ '%s%s.%s.__bases__ = tuple(bases)'\
+ %(ID1, self.getNSAlias(), self.getClassName())
+ )
+
+ s = ''
+ for b in bases:
+ s += '%s%s\n' % (indent, b)
+
+ return s
+
+
+class MessageTypecodeContainer(TypecodeContainerBase):
+ '''Used for RPC style messages, where we have
+ serveral parts serialized within a rpc wrapper name.
+ '''
+ logger = _GetLogger("MessageTypecodeContainer")
+
+ def __init__(self, parts=None):
+ TypecodeContainerBase.__init__(self)
+ self.mgContent = parts
+
+ def _getOccurs(self, e):
+ '''return a 3 item tuple
+ '''
+ minOccurs = maxOccurs = '1'
+ nillable = True
+ return minOccurs,maxOccurs,nillable
+
+ def _setTypecodeList(self):
+ self.logger.debug("_setTypecodeList: %s" %
+ str(self.mgContent))
+
+ assert type(self.mgContent) is tuple,\
+ 'expecting tuple for mgContent not: %s' %type(self.mgContent)
+
+ for p in self.mgContent:
+ # JRB
+ # not sure what needs to be set for tc, this should be
+ # the job of the constructor or a setUp method.
+ min,max,nil = self._getOccurs(p)
+ if p.element:
+ raise WSInteropError, WSISpec.R2203
+ elif p.type:
+ nsuri,name = p.type
+ tc = RPCMessageTcListComponentContainer(qualified=False)
+ tc.setOccurs(min, max, nil)
+ tc.name = p.name
+ if nsuri in SCHEMA.XSD_LIST:
+ tpc = BTI.get_typeclass(name, nsuri)
+ tc.klass = tpc
+ else:
+ tc.klass = '%s.%s' % (NAD.getAlias(nsuri), type_class_name(name) )
+ else:
+ raise ContainerError, 'part must define an element or type attribute'
+
+ self.tcListElements.append(tc)
+
+ def getTypecodeList(self):
+ if not self.tcListSet:
+ self._setTypecodeList()
+ self.tcListSet = True
+
+ list = []
+ for e in self.tcListElements:
+ list.append(str(e))
+ return ', '.join(list)
+
+ def getAttributeNames(self):
+ '''returns a list of anames representing the parts
+ of the message.
+ '''
+ return map(lambda e: self.getAttributeName(e.name), self.tcListElements)
+
+ def setParts(self, parts):
+ self.mgContent = parts
+
+
+class TcListComponentContainer(ContainerBase):
+ '''Encapsulates a single value in the TClist list.
+ it inherits TypecodeContainerBase only to get the mangle() method,
+ it does not call the baseclass ctor.
+
+ TODO: Change this inheritance scheme.
+ '''
+ logger = _GetLogger("TcListComponentContainer")
+
+ def __init__(self, qualified=True):
+ '''
+ qualified -- qualify element. All GEDs should be qualified,
+ but local element declarations qualified if form attribute
+ is qualified, else they are unqualified. Only relevant for
+ standard style.
+ '''
+ #TypecodeContainerBase.__init__(self)
+ ContainerBase.__init__(self)
+
+ self.qualified = qualified
+ self.name = None
+ self.klass = None
+ self.global_type = None
+
+ self.min = None
+ self.max = None
+ self.nil = None
+ self.style = None
+ self.setStyleElementDeclaration()
+
+ def setOccurs(self, min, max, nil):
+ self.min = min
+ self.max = max
+ self.nil = nil
+
+ def setProcessContents(self, processContents):
+ self.processContents = processContents
+
+ def setGlobalType(self, namespace, name):
+ self.global_type = (namespace, name)
+
+ def setStyleElementDeclaration(self):
+ '''set the element style.
+ standard -- GED or local element
+ '''
+ self.style = 'standard'
+
+ def setStyleElementReference(self):
+ '''set the element style.
+ ref -- element reference
+ '''
+ self.style = 'ref'
+
+ def setStyleAnyElement(self):
+ '''set the element style.
+ anyElement -- <any> element wildcard
+ '''
+ self.name = 'any'
+ self.style = 'anyElement'
+
+# def setStyleRecursion(self):
+# '''TODO: Remove. good for 1 level
+# '''
+# self.style = 'recursion'
+
+ def unQualified(self):
+ '''Do not qualify element.
+ '''
+ self.qualified = False
+
+ def _getOccurs(self):
+ return 'minOccurs=%s, maxOccurs=%s, nillable=%s' \
+ % (self.min, self.max, self.nil)
+
+ def _getProcessContents(self):
+ return 'processContents="%s"' \
+ % (self.processContents)
+
+ def _getvalue(self):
+ kw = {'occurs':self._getOccurs(),
+ 'aname':self.getAttributeName(self.name),
+ 'klass':self.klass,
+ 'lazy':TypecodeContainerBase.lazy,
+ 'typed':'typed=False',
+ 'encoded':'encoded=kw.get("encoded")'}
+
+ gt = self.global_type
+ if gt is not None:
+ kw['nsuri'],kw['type'] = gt
+
+ if self.style == 'standard':
+ kw['pname'] = '"%s"' %self.name
+ if self.qualified is True:
+ kw['pname'] = '(ns,"%s")' %self.name
+ if gt is None:
+ return '%(klass)s(pname=%(pname)s, aname="%(aname)s", %(occurs)s, %(typed)s, %(encoded)s)' %kw
+ return 'GTD("%(nsuri)s","%(type)s",lazy=%(lazy)s)(pname=%(pname)s, aname="%(aname)s", %(occurs)s, %(typed)s, %(encoded)s)' %kw
+
+ if self.style == 'ref':
+ if gt is None:
+ return '%(klass)s(%(occurs)s, %(encoded)s)' %kw
+ return 'GED("%(nsuri)s","%(type)s",lazy=%(lazy)s, isref=True)(%(occurs)s, %(encoded)s)' %kw
+
+ kw['process'] = self._getProcessContents()
+ if self.style == 'anyElement':
+ return 'ZSI.TC.AnyElement(aname="%(aname)s", %(occurs)s, %(process)s)' %kw
+
+# if self.style == 'recursion':
+# return 'ZSI.TC.AnyElement(aname="%(aname)s", %(occurs)s, %(process)s)' %kw
+
+ raise RuntimeError, 'Must set style for typecode list generation'
+
+ def __str__(self):
+ return self._getvalue()
+
+
+class RPCMessageTcListComponentContainer(TcListComponentContainer):
+ '''Container for rpc/literal rpc/encoded message typecode.
+ '''
+ logger = _GetLogger("RPCMessageTcListComponentContainer")
+
+ def __init__(self, qualified=True, encoded=None):
+ '''
+ encoded -- encoded namespaceURI, if None treat as rpc/literal.
+ '''
+ TcListComponentContainer.__init__(self, qualified=qualified)
+ self._encoded = encoded
+
+ def _getvalue(self):
+ encoded = self._encoded
+ if encoded is not None:
+ encoded = '"%s"' %self._encoded
+
+ if self.style == 'standard':
+ pname = '"%s"' %self.name
+ if self.qualified is True:
+ pname = '(ns,"%s")' %self.name
+ return '%s(pname=%s, aname="%s", typed=False, encoded=%s, %s)' \
+ %(self.klass, pname, self.getAttributeName(self.name),
+ encoded, self._getOccurs())
+ elif self.style == 'ref':
+ return '%s(encoded=%s, %s)' % (self.klass, encoded, self._getOccurs())
+ elif self.style == 'anyElement':
+ return 'ZSI.TC.AnyElement(aname="%s", %s, %s)' \
+ %(self.getAttributeName(self.name), self._getOccurs(), self._getProcessContents())
+# elif self.style == 'recursion':
+# return 'ZSI.TC.AnyElement(aname="%s", %s, %s)' \
+# % (self.getAttributeName(self.name), self._getOccurs(), self._getProcessContents())
+
+ raise RuntimeError('Must set style(%s) for typecode list generation' %
+ self.style)
+
+
+class ElementSimpleTypeContainer(TypecodeContainerBase):
+ type = DEC
+ logger = _GetLogger("ElementSimpleTypeContainer")
+
+ def _setContent(self):
+ aname = self.getAttributeName(self.name)
+ pyclass = self.pyclass
+
+ # bool cannot be subclassed
+ if pyclass == 'bool': pyclass = 'int'
+ kw = KW.copy()
+ kw.update(dict(aname=aname, ns=self.ns, name=self.name,
+ subclass=self.sKlass,literal=self.literalTag(),
+ schema=self.schemaTag(), init=self.simpleConstructor(),
+ klass=self.getClassName(), element="ElementDeclaration"))
+
+ if self.local:
+ kw['element'] = 'LocalElementDeclaration'
+
+ element = map(lambda i: i %kw, [
+ '%(ID1)sclass %(klass)s(%(subclass)s, %(element)s):',
+ '%(ID2)s%(literal)s',
+ '%(ID2)s%(schema)s',
+ '%(ID2)s%(init)s',
+ '%(ID3)skw["pname"] = ("%(ns)s","%(name)s")',
+ '%(ID3)skw["aname"] = "%(aname)s"',
+ ]
+ )
+
+ # TODO: What about getPyClass and getPyClassDefinition?
+ # I want to add pyclass metaclass here but this needs to be
+ # corrected first.
+ #
+ # anyType (?others) has no pyclass.
+ app = element.append
+ if pyclass is not None:
+ app('%sclass IHolder(%s): typecode=self' % (ID3, pyclass),)
+ app('%skw["pyclass"] = IHolder' %(ID3),)
+ app('%sIHolder.__name__ = "%s_immutable_holder"' %(ID3, aname),)
+
+ app('%s%s' % (ID3, self.simpleConstructor(self.sKlass)),)
+
+ self.writeArray(element)
+
+ def setUp(self, tp):
+ self._item = tp
+ self.local = tp.isLocal()
+ try:
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ qName = tp.getAttribute('type')
+ except Exception, ex:
+ raise Wsdl2PythonError('Error occured processing element: %s' %(
+ tp.getItemTrace()), *ex.args)
+
+ if qName is None:
+ raise Wsdl2PythonError('Missing QName for element type attribute: %s' %tp.getItemTrace())
+
+ tns,local = qName.getTargetNamespace(),qName.getName()
+ self.sKlass = BTI.get_typeclass(local, tns)
+ if self.sKlass is None:
+ raise Wsdl2PythonError('No built-in typecode for type definition("%s","%s"): %s' %(tns,local,tp.getItemTrace()))
+
+ try:
+ self.pyclass = BTI.get_pythontype(None, None, typeclass=self.sKlass)
+ except Exception, ex:
+ raise Wsdl2PythonError('Error occured processing element: %s' %(
+ tp.getItemTrace()), *ex.args)
+
+
+class ElementLocalSimpleTypeContainer(TypecodeContainerBase):
+ '''local simpleType container
+ '''
+ type = DEC
+ logger = _GetLogger("ElementLocalSimpleTypeContainer")
+
+ def _setContent(self):
+ kw = KW.copy()
+ kw.update(dict(aname=self.getAttributeName(self.name), ns=self.ns, name=self.name,
+ subclass=self.sKlass,literal=self.literalTag(),
+ schema=self.schemaTag(), init=self.simpleConstructor(),
+ klass=self.getClassName(), element="ElementDeclaration",
+ baseinit=self.simpleConstructor(self.sKlass)))
+
+ if self.local:
+ kw['element'] = 'LocalElementDeclaration'
+
+ element = map(lambda i: i %kw, [
+ '%(ID1)sclass %(klass)s(%(subclass)s, %(element)s):',
+ '%(ID2)s%(literal)s',
+ '%(ID2)s%(schema)s',
+ '%(ID2)s%(init)s',
+ '%(ID3)skw["pname"] = ("%(ns)s","%(name)s")',
+ '%(ID3)skw["aname"] = "%(aname)s"',
+ '%(ID3)s%(baseinit)s',
+ ]
+ )
+ self.writeArray(element)
+
+ def setUp(self, tp):
+ self._item = tp
+ assert tp.isElement() is True and tp.content is not None and \
+ tp.content.isLocal() is True and tp.content.isSimple() is True ,\
+ 'expecting local simple type: %s' %tp.getItemTrace()
+
+ self.local = tp.isLocal()
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ content = tp.content.content
+ if content.isRestriction():
+ try:
+ base = content.getTypeDefinition()
+ except XMLSchema.SchemaError, ex:
+ base = None
+
+ qName = content.getAttributeBase()
+ if base is None:
+ self.sKlass = BTI.get_typeclass(qName[1], qName[0])
+ return
+
+ raise Wsdl2PythonError, 'unsupported local simpleType restriction: %s' \
+ %tp.content.getItemTrace()
+
+ if content.isList():
+ try:
+ base = content.getTypeDefinition()
+ except XMLSchema.SchemaError, ex:
+ base = None
+
+ if base is None:
+ qName = content.getItemType()
+ self.sKlass = BTI.get_typeclass(qName[1], qName[0])
+ return
+
+ raise Wsdl2PythonError, 'unsupported local simpleType List: %s' \
+ %tp.content.getItemTrace()
+
+ if content.isUnion():
+ raise Wsdl2PythonError, 'unsupported local simpleType Union: %s' \
+ %tp.content.getItemTrace()
+
+ raise Wsdl2PythonError, 'unexpected schema item: %s' \
+ %tp.content.getItemTrace()
+
+
+class ElementLocalComplexTypeContainer(TypecodeContainerBase, AttributeMixIn):
+ type = DEC
+ logger = _GetLogger("ElementLocalComplexTypeContainer")
+
+ def _setContent(self):
+ kw = KW.copy()
+ try:
+ kw.update(dict(klass=self.getClassName(),
+ subclass='ZSI.TCcompound.ComplexType',
+ element='ElementDeclaration',
+ literal=self.literalTag(),
+ schema=self.schemaTag(),
+ init=self.simpleConstructor(),
+ ns=self.ns, name=self.name,
+ aname=self.getAttributeName(self.name),
+ nsurilogic=self.nsuriLogic(),
+ ofwhat=self.getTypecodeList(),
+ atypecode=self.attribute_typecode,
+ pyclass=self.getPyClass(),
+ ))
+ except Exception, ex:
+ args = ['Failure processing an element w/local complexType: %s' %(
+ self._item.getItemTrace())]
+ args += ex.args
+ ex.args = tuple(args)
+ raise
+
+ if self.local:
+ kw['element'] = 'LocalElementDeclaration'
+
+ element = [
+ '%(ID1)sclass %(klass)s(%(subclass)s, %(element)s):',
+ '%(ID2)s%(literal)s',
+ '%(ID2)s%(schema)s',
+ '%(ID2)s%(init)s',
+ '%(ID3)s%(nsurilogic)s',
+ '%(ID3)sTClist = [%(ofwhat)s]',
+ '%(ID3)skw["pname"] = ("%(ns)s","%(name)s")',
+ '%(ID3)skw["aname"] = "%(aname)s"',
+ '%(ID3)s%(atypecode)s = {}',
+ '%(ID3)sZSI.TCcompound.ComplexType.__init__(self,None,TClist,inorder=0,**kw)',
+ ]
+ for l in self.attrComponents: element.append('%(ID3)s'+str(l))
+ element += self.getPyClassDefinition()
+ element.append('%(ID3)sself.pyclass = %(pyclass)s' %kw)
+ self.writeArray(map(lambda l: l %kw, element))
+
+ def setUp(self, tp):
+ '''
+ {'xsd':['annotation', 'simpleContent', 'complexContent',\
+ 'group', 'all', 'choice', 'sequence', 'attribute', 'attributeGroup',\
+ 'anyAttribute', 'any']}
+ '''
+ #
+ # TODO: Need a Recursive solution, this is incomplete will ignore many
+ # extensions, restrictions, etc.
+ #
+ self._item = tp
+ # JRB HACK SUPPORTING element/no content.
+ assert tp.isElement() is True and \
+ (tp.content is None or (tp.content.isComplex() is True and tp.content.isLocal() is True)),\
+ 'expecting element w/local complexType not: %s' %tp.content.getItemTrace()
+
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ self.local = tp.isLocal()
+
+ complex = tp.content
+ # JRB HACK SUPPORTING element/no content.
+ if complex is None:
+ self.mgContent = ()
+ return
+
+ #attributeContent = complex.getAttributeContent()
+ #self.mgContent = None
+ if complex.content is None:
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ is_simple = complex.content.isSimple()
+ if is_simple and complex.content.content.isExtension():
+ # TODO: Not really supported just passing thru
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ if is_simple and complex.content.content.isRestriction():
+ # TODO: Not really supported just passing thru
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ if is_simple:
+ raise ContainerError, 'not implemented local complexType/simpleContent: %s'\
+ %tp.getItemTrace()
+
+ is_complex = complex.content.isComplex()
+ if is_complex and complex.content.content is None:
+ # TODO: Recursion...
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ if (is_complex and complex.content.content.isExtension() and
+ complex.content.content.content is not None and
+ complex.content.content.content.isModelGroup()):
+
+ self.mgContent = complex.content.content.content.content
+ self.attrComponents = self._setAttributes(
+ complex.content.content.getAttributeContent()
+ )
+ return
+
+ if (is_complex and complex.content.content.isRestriction() and
+ complex.content.content.content is not None and
+ complex.content.content.content.isModelGroup()):
+
+ self.mgContent = complex.content.content.content.content
+ self.attrComponents = self._setAttributes(
+ complex.content.content.getAttributeContent()
+ )
+ return
+
+ if is_complex:
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ if complex.content.isModelGroup():
+ self.mgContent = complex.content.content
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+ return
+
+ # TODO: Scary Fallthru
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(complex.getAttributeContent())
+
+
+class ElementGlobalDefContainer(TypecodeContainerBase):
+ type = DEC
+ logger = _GetLogger("ElementGlobalDefContainer")
+
+ def _setContent(self):
+ '''GED defines element name, so also define typecode aname
+ '''
+ kw = KW.copy()
+ try:
+ kw.update(dict(klass=self.getClassName(),
+ element='ElementDeclaration',
+ literal=self.literalTag(),
+ schema=self.schemaTag(),
+ init=self.simpleConstructor(),
+ ns=self.ns, name=self.name,
+ aname=self.getAttributeName(self.name),
+ baseslogic=self.getBasesLogic(ID3),
+ #ofwhat=self.getTypecodeList(),
+ #atypecode=self.attribute_typecode,
+ #pyclass=self.getPyClass(),
+ alias=NAD.getAlias(self.sKlassNS),
+ subclass=type_class_name(self.sKlass),
+ ))
+ except Exception, ex:
+ args = ['Failure processing an element w/local complexType: %s' %(
+ self._item.getItemTrace())]
+ args += ex.args
+ ex.args = tuple(args)
+ raise
+
+ if self.local:
+ kw['element'] = 'LocalElementDeclaration'
+
+ element = [
+ '%(ID1)sclass %(klass)s(%(element)s):',
+ '%(ID2)s%(literal)s',
+ '%(ID2)s%(schema)s',
+ '%(ID2)s%(init)s',
+ '%(ID3)skw["pname"] = ("%(ns)s","%(name)s")',
+ '%(ID3)skw["aname"] = "%(aname)s"',
+ '%(baseslogic)s',
+ '%(ID3)s%(alias)s.%(subclass)s.__init__(self, **kw)',
+ '%(ID3)sif self.pyclass is not None: self.pyclass.__name__ = "%(klass)s_Holder"',
+ ]
+
+ self.writeArray(map(lambda l: l %kw, element))
+
+ def setUp(self, element):
+ # Save for debugging
+ self._item = element
+ self.local = element.isLocal()
+ self.name = element.getAttribute('name')
+ self.ns = element.getTargetNamespace()
+ tp = element.getTypeDefinition('type')
+ self.sKlass = tp.getAttribute('name')
+ self.sKlassNS = tp.getTargetNamespace()
+
+
+class ComplexTypeComplexContentContainer(TypecodeContainerBase, AttributeMixIn):
+ '''Represents ComplexType with ComplexContent.
+ '''
+ type = DEF
+ logger = _GetLogger("ComplexTypeComplexContentContainer")
+
+ def __init__(self, do_extended=False):
+ TypecodeContainerBase.__init__(self, do_extended=do_extended)
+
+ def setUp(self, tp):
+ '''complexContent/[extension,restriction]
+ restriction
+ extension
+ extType -- used in figuring attrs for extensions
+ '''
+ self._item = tp
+ assert tp.content.isComplex() is True and \
+ (tp.content.content.isRestriction() or tp.content.content.isExtension() is True),\
+ 'expecting complexContent/[extension,restriction]'
+
+ self.extType = None
+ self.restriction = False
+ self.extension = False
+ self._kw_array = None
+ self._is_array = False
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+
+ # xxx: what is this for?
+ #self.attribute_typecode = 'attributes'
+
+ derivation = tp.content.content
+ # Defined in Schema instance?
+ try:
+ base = derivation.getTypeDefinition('base')
+ except XMLSchema.SchemaError, ex:
+ base = None
+
+ # anyType, arrayType, etc...
+ if base is None:
+ base = derivation.getAttributeQName('base')
+ if base is None:
+ raise ContainerError, 'Unsupported derivation: %s'\
+ %derivation.getItemTrace()
+
+ if base != (SOAP.ENC,'Array') and base != (SCHEMA.XSD3,'anyType'):
+ raise ContainerError, 'Unsupported base(%s): %s' %(
+ base, derivation.getItemTrace()
+ )
+
+ if base == (SOAP.ENC,'Array'):
+ # SOAP-ENC:Array expecting arrayType attribute reference
+ self.logger.debug("Derivation of soapenc:Array")
+ self._is_array = True
+ self._kw_array = {'atype':None, 'id3':ID3, 'ofwhat':None}
+ self.sKlass = BTI.get_typeclass(base[1], base[0])
+ self.sKlassNS = base[0]
+ attr = None
+ for a in derivation.getAttributeContent():
+ assert a.isAttribute() is True,\
+ 'only attribute content expected: %s' %a.getItemTrace()
+
+ if a.isReference() is True:
+ if a.getAttribute('ref') == (SOAP.ENC,'arrayType'):
+ self._kw_array['atype'] = a.getAttributeQName((WSDL.BASE, 'arrayType'))
+ attr = a
+ break
+
+ qname = self._kw_array.get('atype')
+ if attr is not None:
+ qname = self._kw_array.get('atype')
+ ncname = qname[1].strip('[]')
+ namespace = qname[0]
+ try:
+ ofwhat = attr.getSchemaItem(XMLSchema.TYPES, namespace, ncname)
+ except XMLSchema.SchemaError, ex:
+ ofwhat = None
+
+ if ofwhat is None:
+ self._kw_array['ofwhat'] = BTI.get_typeclass(ncname, namespace)
+ else:
+ self._kw_array['ofwhat'] = GetClassNameFromSchemaItem(ofwhat, do_extended=self.do_extended)
+
+ if self._kw_array['ofwhat'] is None:
+ raise ContainerError, 'For Array could not resolve ofwhat typecode(%s,%s): %s'\
+ %(namespace, ncname, derivation.getItemTrace())
+
+ self.logger.debug('Attribute soapenc:arrayType="%s"' %
+ str(self._kw_array['ofwhat']))
+
+ elif isinstance(base, XMLSchema.XMLSchemaComponent):
+ self.sKlass = base.getAttribute('name')
+ self.sKlassNS = base.getTargetNamespace()
+ else:
+ # TypeDescriptionComponent
+ self.sKlass = base.getName()
+ self.sKlassNS = base.getTargetNamespace()
+
+ attrs = []
+ if derivation.isRestriction():
+ self.restriction = True
+ self.extension = False
+ # derivation.getAttributeContent subset of tp.getAttributeContent
+ attrs += derivation.getAttributeContent() or ()
+ else:
+ self.restriction = False
+ self.extension = True
+ attrs += tp.getAttributeContent() or ()
+ if isinstance(derivation, XMLSchema.XMLSchemaComponent):
+ attrs += derivation.getAttributeContent() or ()
+
+ # XXX: not sure what this is doing
+ if attrs:
+ self.extType = derivation
+
+ if derivation.content is not None \
+ and derivation.content.isModelGroup():
+ group = derivation.content
+ if group.isReference():
+ group = group.getModelGroupReference()
+ self.mgContent = group.content
+ elif derivation.content:
+ raise Wsdl2PythonError, \
+ 'expecting model group, not: %s' %derivation.content.getItemTrace()
+ else:
+ self.mgContent = ()
+
+ self.attrComponents = self._setAttributes(tuple(attrs))
+
+ def _setContent(self):
+ '''JRB What is the difference between instance data
+ ns, name, -- type definition?
+ sKlass, sKlassNS? -- element declaration?
+ '''
+ kw = KW.copy()
+ definition = []
+ if self._is_array:
+ # SOAP-ENC:Array
+ if _is_xsd_or_soap_ns(self.sKlassNS) is False and self.sKlass == 'Array':
+ raise ContainerError, 'unknown type: (%s,%s)'\
+ %(self.sKlass, self.sKlassNS)
+
+ # No need to xsi:type array items since specify with
+ # SOAP-ENC:arrayType attribute.
+ definition += [\
+ '%sclass %s(ZSI.TC.Array, TypeDefinition):' % (ID1, self.getClassName()),
+ '%s#complexType/complexContent base="SOAP-ENC:Array"' %(ID2),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%(id3)sofwhat = %(ofwhat)s(None, typed=False)' %self._kw_array,
+ '%(id3)satype = %(atype)s' %self._kw_array,
+ '%s%s.__init__(self, atype, ofwhat, pname=pname, childnames=\'item\', **kw)'
+ %(ID3, self.sKlass),
+ ]
+ self.writeArray(definition)
+ return
+
+ definition += [\
+ '%sclass %s(TypeDefinition):' % (ID1, self.getClassName()),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%s%s' % (ID3, self.nsuriLogic()),
+ '%sTClist = [%s]' % (ID3, self.getTypecodeList()),
+ ]
+
+ definition.append(
+ '%(ID3)sattributes = %(atc)s = attributes or {}' %{
+ 'ID3':ID3, 'atc':self.attribute_typecode}
+ )
+
+ #
+ # Special case: anyType restriction
+ isAnyType = (self.sKlassNS, self.sKlass) == (SCHEMA.XSD3, 'anyType')
+ if isAnyType:
+ del definition[0]
+ definition.insert(0,
+ '%sclass %s(ZSI.TC.ComplexType, TypeDefinition):' % (
+ ID1, self.getClassName())
+ )
+ definition.insert(1,
+ '%s#complexType/complexContent restrict anyType' %(
+ ID2)
+ )
+
+ # derived type support
+ definition.append('%sif extend: TClist += ofwhat'%(ID3))
+ definition.append('%sif restrict: TClist = ofwhat' %(ID3))
+ if len(self.attrComponents) > 0:
+ definition.append('%selse:' %(ID3))
+ for l in self.attrComponents:
+ definition.append('%s%s'%(ID4, l))
+
+ if isAnyType:
+ definition.append(\
+ '%sZSI.TC.ComplexType.__init__(self, None, TClist, pname=pname, **kw)' %(
+ ID3),
+ )
+
+ # pyclass class definition
+ definition += self.getPyClassDefinition()
+ kw['pyclass'] = self.getPyClass()
+ definition.append('%(ID3)sself.pyclass = %(pyclass)s' %kw)
+ self.writeArray(definition)
+ return
+
+ for l in self.attrComponents:
+ definition.append('%s%s'%(ID3, l))
+
+ definition.append('%s' % self.getBasesLogic(ID3))
+ prefix = NAD.getAlias(self.sKlassNS)
+ typeClassName = type_class_name(self.sKlass)
+ if self.restriction:
+ definition.append(\
+ '%s%s.%s.__init__(self, pname, ofwhat=TClist, restrict=True, **kw)' %(
+ ID3, prefix, typeClassName),
+ )
+ definition.insert(1, '%s#complexType/complexContent restriction' %ID2)
+ self.writeArray(definition)
+ return
+
+ if self.extension:
+ definition.append(\
+ '%s%s.%s.__init__(self, pname, ofwhat=TClist, extend=True, attributes=attributes, **kw)'%(
+ ID3, prefix, typeClassName),
+ )
+ definition.insert(1, '%s#complexType/complexContent extension' %(ID2))
+ self.writeArray(definition)
+ return
+
+ raise Wsdl2PythonError,\
+ 'ComplexContent must be a restriction or extension'
+
+ def pnameConstructor(self, superclass=None):
+ if superclass:
+ return '%s.__init__(self, pname, ofwhat=(), extend=False, restrict=False, attributes=None, **kw)' % superclass
+
+ return 'def __init__(self, pname, ofwhat=(), extend=False, restrict=False, attributes=None, **kw):'
+
+
+class ComplexTypeContainer(TypecodeContainerBase, AttributeMixIn):
+ '''Represents a global complexType definition.
+ '''
+ type = DEF
+ logger = _GetLogger("ComplexTypeContainer")
+
+ def setUp(self, tp, empty=False):
+ '''Problematic, loose all model group information.
+ <all>, <choice>, <sequence> ..
+
+ tp -- type definition
+ empty -- no model group, just use as a dummy holder.
+ '''
+ self._item = tp
+
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ self.mixed = tp.isMixed()
+ self.mgContent = ()
+ self.attrComponents = self._setAttributes(tp.getAttributeContent())
+
+ # Save reference to type for debugging
+ self._item = tp
+
+ if empty:
+ return
+
+ model = tp.content
+ if model.isReference():
+ model = model.getModelGroupReference()
+
+ if model is None:
+ return
+
+ if model.content is None:
+ return
+
+ # sequence, all or choice
+ #self.mgContent = model.content
+ self.mgContent = model
+
+ def _setContent(self):
+ try:
+ definition = [
+ '%sclass %s(ZSI.TCcompound.ComplexType, TypeDefinition):'
+ % (ID1, self.getClassName()),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ #'%s' % self.getElements(),
+ '%s%s' % (ID3, self.nsuriLogic()),
+ '%sTClist = [%s]' % (ID3, self.getTypecodeList()),
+ ]
+ except Exception, ex:
+ args = ["Failure processing %s" %self._item.getItemTrace()]
+ args += ex.args
+ ex.args = tuple(args)
+ raise
+
+ definition.append('%s%s = attributes or {}' %(ID3,
+ self.attribute_typecode))
+ # IF EXTEND
+ definition.append('%sif extend: TClist += ofwhat'%(ID3))
+ # IF RESTRICT
+ definition.append('%sif restrict: TClist = ofwhat' %(ID3))
+ # ELSE
+ if len(self.attrComponents) > 0:
+ definition.append('%selse:' %(ID3))
+ for l in self.attrComponents: definition.append('%s%s'%(ID4, l))
+
+ definition.append(\
+ '%sZSI.TCcompound.ComplexType.__init__(self, None, TClist, pname=pname, inorder=0, %s**kw)' \
+ %(ID3, self.getExtraFlags())
+ )
+
+ # pyclass class definition
+ definition += self.getPyClassDefinition()
+
+ # set pyclass
+ kw = KW.copy()
+ kw['pyclass'] = self.getPyClass()
+ definition.append('%(ID3)sself.pyclass = %(pyclass)s' %kw)
+ self.writeArray(definition)
+
+ def pnameConstructor(self, superclass=None):
+ ''' TODO: Logic is a little tricky. If superclass is ComplexType this is not used.
+ '''
+ if superclass:
+ return '%s.__init__(self, pname, ofwhat=(), attributes=None, extend=False, restrict=False, **kw)' % superclass
+
+ return 'def __init__(self, pname, ofwhat=(), attributes=None, extend=False, restrict=False, **kw):'
+
+
+class SimpleTypeContainer(TypecodeContainerBase):
+ type = DEF
+ logger = _GetLogger("SimpleTypeContainer")
+
+ def __init__(self):
+ '''
+ Instance Data From TypecodeContainerBase NOT USED...
+ mgContent
+ '''
+ TypecodeContainerBase.__init__(self)
+
+ def setUp(self, tp):
+ raise NotImplementedError, 'abstract method not implemented'
+
+ def _setContent(self, tp):
+ raise NotImplementedError, 'abstract method not implemented'
+
+ def getPythonType(self):
+ pyclass = eval(str(self.sKlass))
+ if issubclass(pyclass, ZSI.TC.String):
+ return 'str'
+ if issubclass(pyclass, ZSI.TC.Ilong) or issubclass(pyclass, ZSI.TC.IunsignedLong):
+ return 'long'
+ if issubclass(pyclass, ZSI.TC.Boolean) or issubclass(pyclass, ZSI.TC.Integer):
+ return 'int'
+ if issubclass(pyclass, ZSI.TC.Decimal):
+ return 'float'
+ if issubclass(pyclass, ZSI.TC.Gregorian) or issubclass(pyclass, ZSI.TC.Duration):
+ return 'tuple'
+ return None
+
+ def getPyClassDefinition(self):
+ definition = []
+ pt = self.getPythonType()
+ if pt is not None:
+ definition.append('%sclass %s(%s):' %(ID3,self.getPyClass(),pt))
+ definition.append('%stypecode = self' %ID4)
+ return definition
+
+
+class RestrictionContainer(SimpleTypeContainer):
+ '''
+ simpleType/restriction
+ '''
+ logger = _GetLogger("RestrictionContainer")
+
+ def setUp(self, tp):
+ self._item = tp
+
+ assert tp.isSimple() is True and tp.isDefinition() is True and \
+ tp.content.isRestriction() is True,\
+ 'expecting simpleType restriction, not: %s' %tp.getItemTrace()
+
+ if tp.content is None:
+ raise Wsdl2PythonError, \
+ 'empty simpleType defintion: %s' %tp.getItemTrace()
+
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ self.sKlass = None
+
+ base = tp.content.getAttribute('base')
+ if base is not None:
+ try:
+ item = tp.content.getTypeDefinition('base')
+ except XMLSchema.SchemaError, ex:
+ pass
+
+ if item is None:
+ self.sKlass = BTI.get_typeclass(base.getName(), base.getTargetNamespace())
+ if self.sKlass is not None:
+ return
+
+ raise Wsdl2PythonError('no built-in type nor schema instance type for base attribute("%s","%s"): %s' %(
+ base.getTargetNamespace(), base.getName(), tp.getItemTrace()))
+
+ raise Wsdl2PythonError, \
+ 'Not Supporting simpleType/Restriction w/User-Defined Base: %s %s' %(tp.getItemTrace(),item.getItemTrace())
+
+ sc = tp.content.getSimpleTypeContent()
+ if sc is not None and True is sc.isSimple() is sc.isLocal() is sc.isDefinition():
+ base = None
+ if sc.content.isRestriction() is True:
+ try:
+ item = tp.content.getTypeDefinition('base')
+ except XMLSchema.SchemaError, ex:
+ pass
+
+ if item is None:
+ base = sc.content.getAttribute('base')
+ if base is not None:
+ self.sKlass = BTI.get_typeclass(base.getTargetNamespace(), base.getName())
+ return
+ raise Wsdl2PythonError, \
+ 'Not Supporting simpleType/Restriction w/User-Defined Base: '\
+ %item.getItemTrace()
+
+ raise Wsdl2PythonError, \
+ 'Not Supporting simpleType/Restriction w/User-Defined Base: '\
+ %item.getItemTrace()
+
+ if sc.content.isList() is True:
+ raise Wsdl2PythonError, \
+ 'iction base in subtypes: %s'\
+ %sc.getItemTrace()
+
+ if sc.content.isUnion() is True:
+ raise Wsdl2PythonError, \
+ 'could not get restriction base in subtypes: %s'\
+ %sc.getItemTrace()
+
+ return
+
+ raise Wsdl2PythonError, 'No Restriction @base/simpleType: %s' %tp.getItemTrace()
+
+ def _setContent(self):
+
+ definition = [
+ '%sclass %s(%s, TypeDefinition):' %(ID1, self.getClassName(),
+ self.sKlass),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ ]
+ if self.getPythonType() is None:
+ definition.append('%s%s.__init__(self, pname, **kw)' %(ID3,
+ self.sKlass))
+ else:
+ definition.append('%s%s.__init__(self, pname, pyclass=None, **kw)' \
+ %(ID3, self.sKlass,))
+
+ # pyclass class definition
+ definition += self.getPyClassDefinition()
+
+ # set pyclass
+ kw = KW.copy()
+ kw['pyclass'] = self.getPyClass()
+ definition.append('%(ID3)sself.pyclass = %(pyclass)s' %kw)
+
+ self.writeArray(definition)
+
+
+class ComplexTypeSimpleContentContainer(SimpleTypeContainer, AttributeMixIn):
+ '''Represents a ComplexType with simpleContent.
+ '''
+ type = DEF
+ logger = _GetLogger("ComplexTypeSimpleContentContainer")
+
+ def setUp(self, tp):
+ '''tp -- complexType/simpleContent/[Exention,Restriction]
+ '''
+ self._item = tp
+
+ assert tp.isComplex() is True and tp.content.isSimple() is True,\
+ 'expecting complexType/simpleContent not: %s' %tp.content.getItemTrace()
+
+ simple = tp.content
+ dv = simple.content
+ assert dv.isExtension() is True or dv.isRestriction() is True,\
+ 'expecting complexType/simpleContent/[Extension,Restriction] not: %s' \
+ %tp.content.getItemTrace()
+
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ # TODO: Why is this being set?
+ self.content.attributeContent = dv.getAttributeContent()
+
+ base = dv.getAttribute('base')
+ if base is not None:
+ self.sKlass = BTI.get_typeclass( base[1], base[0] )
+ if not self.sKlass:
+ self.sKlass,self.sKlassNS = base[1], base[0]
+
+ self.attrComponents = self._setAttributes(
+ self.content.attributeContent
+ )
+ return
+
+ raise Wsdl2PythonError,\
+ 'simple content derivation bad base attribute: ' %tp.getItemTrace()
+
+ def _setContent(self):
+ # TODO: Add derivation logic to constructors.
+ if type(self.sKlass) in (types.ClassType, type):
+ definition = [
+ '%sclass %s(%s, TypeDefinition):' \
+ % (ID1, self.getClassName(), self.sKlass),
+ '%s# ComplexType/SimpleContent derivation of built-in type' %ID2,
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%sif getattr(self, "attribute_typecode_dict", None) is None: %s = {}' %(
+ ID3, self.attribute_typecode),
+ ]
+
+ for l in self.attrComponents:
+ definition.append('%s%s'%(ID3, l))
+
+ definition.append('%s%s.__init__(self, pname, **kw)' %(ID3, self.sKlass))
+ if self.getPythonType() is not None:
+ definition += self.getPyClassDefinition()
+ kw = KW.copy()
+ kw['pyclass'] = self.getPyClass()
+ definition.append('%(ID3)sself.pyclass = %(pyclass)s' %kw)
+
+ self.writeArray(definition)
+ return
+
+ definition = [
+ '%sclass %s(TypeDefinition):' % (ID1, self.getClassName()),
+ '%s# ComplexType/SimpleContent derivation of user-defined type' %ID2,
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%s%s' % (ID3, self.nsuriLogic()),
+ '%s' % self.getBasesLogic(ID3),
+ '%sif getattr(self, "attribute_typecode_dict", None) is None: %s = {}' %(
+ ID3, self.attribute_typecode),
+ ]
+
+ for l in self.attrComponents:
+ definition.append('%s%s'%(ID3, l))
+
+ definition.append('%s%s.%s.__init__(self, pname, **kw)' %(
+ ID3, NAD.getAlias(self.sKlassNS), type_class_name(self.sKlass)))
+
+ self.writeArray(definition)
+
+ def getPyClassDefinition(self):
+ definition = []
+ pt = self.getPythonType()
+ if pt is not None:
+ definition.append('%sclass %s(%s):' %(ID3,self.getPyClass(),pt))
+ if self.metaclass is not None:
+ definition.append('%s__metaclass__ = %s' %(ID4, self.metaclass))
+
+ definition.append('%stypecode = self' %ID4)
+ return definition
+
+
+
+
+
+class UnionContainer(SimpleTypeContainer):
+ '''SimpleType Union
+ '''
+ type = DEF
+ logger = _GetLogger("UnionContainer")
+
+ def __init__(self):
+ SimpleTypeContainer.__init__(self)
+ self.memberTypes = None
+
+ def setUp(self, tp):
+ self._item = tp
+
+ if tp.content.isUnion() is False:
+ raise ContainerError, 'content must be a Union: %s' %tp.getItemTrace()
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ self.sKlass = 'ZSI.TC.Union'
+ self.memberTypes = tp.content.getAttribute('memberTypes')
+
+ def _setContent(self):
+ definition = [
+ '%sclass %s(%s, TypeDefinition):' \
+ % (ID1, self.getClassName(), self.sKlass),
+ '%smemberTypes = %s' % (ID2, self.memberTypes),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%s%s' % (ID3, self.pnameConstructor(self.sKlass)),
+ ]
+
+ # TODO: Union pyclass is None
+ self.writeArray(definition)
+
+
+class ListContainer(SimpleTypeContainer):
+ '''SimpleType List
+ '''
+ type = DEF
+ logger = _GetLogger("ListContainer")
+
+ def setUp(self, tp):
+ self._item = tp
+
+ if tp.content.isList() is False:
+ raise ContainerError, 'content must be a List: %s' %tp.getItemTrace()
+ self.name = tp.getAttribute('name')
+ self.ns = tp.getTargetNamespace()
+ self.sKlass = 'ZSI.TC.List'
+ self.itemType = tp.content.getAttribute('itemType')
+
+ def _setContent(self):
+ definition = [
+ '%sclass %s(%s, TypeDefinition):' \
+ % (ID1, self.getClassName(), self.sKlass),
+ '%sitemType = %s' % (ID2, self.itemType),
+ '%s%s' % (ID2, self.schemaTag()),
+ '%s%s' % (ID2, self.typeTag()),
+ '%s%s' % (ID2, self.pnameConstructor()),
+ '%s%s' % (ID3, self.pnameConstructor(self.sKlass)),
+ ]
+ self.writeArray(definition)
+
+
diff --git a/ZSI/zsi/ZSI/generate/pyclass.py b/ZSI/zsi/ZSI/generate/pyclass.py
new file mode 100644
index 0000000..54dce2f
--- /dev/null
+++ b/ZSI/zsi/ZSI/generate/pyclass.py
@@ -0,0 +1,285 @@
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+import pydoc, sys, warnings
+from ZSI import TC
+
+# If function.__name__ is read-only, fail
+def _x(): return
+try:
+ _x.func_name = '_y'
+except:
+ raise RuntimeError,\
+ 'use python-2.4 or later, cannot set function names in python "%s"'\
+ %sys.version
+del _x
+
+
+#def GetNil(typecode=None):
+# """returns the nilled element, use to set an element
+# as nilled for immutable instance.
+# """
+#
+# nil = TC.Nilled()
+# if typecode is not None: nil.typecode = typecode
+# return nil
+#
+#
+#def GetNilAsSelf(cls, typecode=None):
+# """returns the nilled element with typecode specified,
+# use returned instance to set this element as nilled.
+#
+# Key Word Parameters:
+# typecode -- use to specify a derived type or wildcard as nilled.
+# """
+# if typecode is not None and not isinstance(typecode, TC.TypeCode):
+# raise TypeError, "Expecting a TypeCode instance"
+#
+# nil = TC.Nilled()
+# nil.typecode = typecode or cls.typecode
+# return nil
+
+
+class pyclass_type(type):
+ """Stability: Unstable
+
+ type for pyclasses used with typecodes. expects the typecode to
+ be available in the classdict. creates python properties for accessing
+ and setting the elements specified in the ofwhat list, and factory methods
+ for constructing the elements.
+
+ Known Limitations:
+ 1)Uses XML Schema element names directly to create method names,
+ using characters in this set will cause Syntax Errors:
+
+ (NCNAME)-(letter U digit U "_")
+
+ """
+ def __new__(cls, classname, bases, classdict):
+ """
+ """
+ #import new
+ typecode = classdict.get('typecode')
+ assert typecode is not None, 'MUST HAVE A TYPECODE.'
+
+ # Assume this means immutable type. ie. str
+ if len(bases) > 0:
+ #classdict['new_Nill'] = classmethod(GetNilAsSelf)
+ pass
+ # Assume this means mutable type. ie. ofwhat.
+ else:
+ assert hasattr(typecode, 'ofwhat'), 'typecode has no ofwhat list??'
+ assert hasattr(typecode, 'attribute_typecode_dict'),\
+ 'typecode has no attribute_typecode_dict??'
+
+ #classdict['new_Nill'] = staticmethod(GetNil)
+
+ if typecode.mixed:
+ get,set = cls.__create_text_functions_from_what(typecode)
+
+ if classdict.has_key(get.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %get.__name__
+
+ if classdict.has_key(set.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %set.__name__
+
+ classdict[get.__name__] = get
+ classdict[set.__name__] = set
+
+ for what in typecode.ofwhat:
+ get,set,new_func = cls.__create_functions_from_what(what)
+
+ if classdict.has_key(get.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %get.__name__
+
+ classdict[get.__name__] = get
+ if classdict.has_key(set.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %set.__name__
+
+ classdict[set.__name__] = set
+ if new_func is not None:
+ if classdict.has_key(new_func.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %new_func.__name__
+
+ classdict[new_func.__name__] = new_func
+
+ assert not classdict.has_key(what.pname),\
+ 'collision with pname="%s", bail..' %what.pname
+
+ pname = what.pname
+ if pname is None and isinstance(what, TC.AnyElement): pname = 'any'
+ assert pname is not None, 'Element with no name: %s' %what
+
+ # TODO: for pname if keyword just uppercase first letter.
+ #if pydoc.Helper.keywords.has_key(pname):
+ pname = pname[0].upper() + pname[1:]
+ assert not pydoc.Helper.keywords.has_key(pname), 'unexpected keyword: %s' %pname
+
+ classdict[pname] =\
+ property(get, set, None,
+ 'property for element (%s,%s), minOccurs="%s" maxOccurs="%s" nillable="%s"'\
+ %(what.nspname,what.pname,what.minOccurs,what.maxOccurs,what.nillable)
+ )
+
+ #
+ # mutable type <complexType> complexContent | modelGroup
+ # or immutable type <complexType> simpleContent (float, str, etc)
+ #
+ if hasattr(typecode, 'attribute_typecode_dict'):
+ attribute_typecode_dict = typecode.attribute_typecode_dict or {}
+ for key,what in attribute_typecode_dict.items():
+ get,set = cls.__create_attr_functions_from_what(key, what)
+ if classdict.has_key(get.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %get.__name__
+
+ if classdict.has_key(set.__name__):
+ raise AttributeError,\
+ 'attribute %s previously defined.' %set.__name__
+
+ classdict[get.__name__] = get
+ classdict[set.__name__] = set
+
+ return type.__new__(cls,classname,bases,classdict)
+
+ def __create_functions_from_what(what):
+ if not callable(what):
+ def get(self):
+ return getattr(self, what.aname)
+
+ if what.maxOccurs > 1:
+ def set(self, value):
+ if not (value is None or hasattr(value, '__iter__')):
+ raise TypeError, 'expecting an iterable instance'
+ setattr(self, what.aname, value)
+ else:
+ def set(self, value):
+ setattr(self, what.aname, value)
+ else:
+ def get(self):
+ return getattr(self, what().aname)
+
+ if what.maxOccurs > 1:
+ def set(self, value):
+ if not (value is None or hasattr(value, '__iter__')):
+ raise TypeError, 'expecting an iterable instance'
+ setattr(self, what().aname, value)
+ else:
+ def set(self, value):
+ setattr(self, what().aname, value)
+
+ #
+ # new factory function
+ # if pyclass is None, skip
+ #
+ if not callable(what) and getattr(what, 'pyclass', None) is None:
+ new_func = None
+ elif (isinstance(what, TC.ComplexType) or
+ isinstance(what, TC.Array)):
+
+ def new_func(self):
+ '''returns a mutable type
+ '''
+ return what.pyclass()
+
+ elif not callable(what):
+
+ def new_func(self, value):
+ '''value -- initialize value
+ returns an immutable type
+ '''
+ return what.pyclass(value)
+
+ elif (issubclass(what.klass, TC.ComplexType) or
+ issubclass(what.klass, TC.Array)):
+
+ def new_func(self):
+ '''returns a mutable type or None (if no pyclass).
+ '''
+ p = what().pyclass
+ if p is None: return
+ return p()
+
+ else:
+
+ def new_func(self, value=None):
+ '''if simpleType provide initialization value, else
+ if complexType value should be left as None.
+ Parameters:
+ value -- initialize value or None
+
+ returns a mutable instance (value is None)
+ or an immutable instance or None (if no pyclass)
+ '''
+ p = what().pyclass
+ if p is None: return
+ if value is None: return p()
+ return p(value)
+
+ #TODO: sub all illegal characters in set
+ # (NCNAME)-(letter U digit U "_")
+ if new_func is not None:
+ new_func.__name__ = 'new_%s' %what.pname
+ get.func_name = 'get_element_%s' %what.pname
+ set.func_name = 'set_element_%s' %what.pname
+ return get,set,new_func
+ __create_functions_from_what = staticmethod(__create_functions_from_what)
+
+ def __create_attr_functions_from_what(key, what):
+
+ def get(self):
+ '''returns attribute value for attribute %s, else None.
+ ''' %str(key)
+ return getattr(self, what.attrs_aname, {}).get(key, None)
+
+ def set(self, value):
+ '''set value for attribute %s.
+ value -- initialize value, immutable type
+ ''' %str(key)
+ if not hasattr(self, what.attrs_aname):
+ setattr(self, what.attrs_aname, {})
+ getattr(self, what.attrs_aname)[key] = value
+
+ #TODO: sub all illegal characters in set
+ # (NCNAME)-(letter U digit U "_")
+ if type(key) in (tuple, list):
+ get.__name__ = 'get_attribute_%s' %key[1]
+ set.__name__ = 'set_attribute_%s' %key[1]
+ else:
+ get.__name__ = 'get_attribute_%s' %key
+ set.__name__ = 'set_attribute_%s' %key
+
+ return get,set
+ __create_attr_functions_from_what = \
+ staticmethod(__create_attr_functions_from_what)
+
+ def __create_text_functions_from_what(what):
+
+ def get(self):
+ '''returns text content, else None.
+ '''
+ return getattr(self, what.mixed_aname, None)
+
+ get.im_func = 'get_text'
+
+ def set(self, value):
+ '''set text content.
+ value -- initialize value, immutable type
+ '''
+ setattr(self, what.mixed_aname, value)
+
+ get.im_func = 'set_text'
+
+ return get,set
+ __create_text_functions_from_what = \
+ staticmethod(__create_text_functions_from_what)
+
+
+
diff --git a/ZSI/zsi/ZSI/generate/utility.py b/ZSI/zsi/ZSI/generate/utility.py
new file mode 100644
index 0000000..d7c5073
--- /dev/null
+++ b/ZSI/zsi/ZSI/generate/utility.py
@@ -0,0 +1,159 @@
+############################################################################
+# Monte M. Goode, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+# utility classes used by new generator - mostly 'sugar' classes
+# that are actually imported by the generated code. also includes
+# utilities used by wsdl2python itself.
+
+# $Id: utility.py 1226 2006-05-26 18:11:19Z boverhof $
+
+import re
+from ZSI import EvaluateException
+from ZSI.TCcompound import Struct
+from ZSI.generate import WsdlGeneratorError, Wsdl2PythonError
+from ZSI.wstools.Utility import SplitQName
+from ZSI.wstools.Namespaces import SCHEMA
+
+NCName_to_ModuleName = lambda name: re.sub('\.', '_', TextProtect(name))
+NCName_to_ClassName = lambda name: re.sub('\.', '_', TextProtect(name))
+TextProtect = lambda s: re.sub('[-./:# ]', '_', s)
+TextProtectAttributeName = lambda name: TextProtect('_%s' %name)
+Namespace2ModuleName = lambda ns: TextProtect(ns.lstrip('http://')).rstrip('_')
+
+
+def GetModuleBaseNameFromWSDL(wsdl):
+ """By default try to construct a reasonable base name for all
+ generated modules. Otherwise return None.
+ """
+ base_name = wsdl.name or wsdl.services[0].name
+ base_name = SplitQName(base_name)[1]
+ if base_name is None:
+ return None
+ return NCName_to_ModuleName(base_name)
+
+namespace_name = lambda cls, ns: 'ns%s' % len(cls.alias_list)
+
+class NamespaceAliasDict:
+ """a lookup table to store relevant namespaces and their aliases"""
+ alias_dict = {}
+ alias_list = []
+
+ def add(cls, ns):
+ if cls.alias_dict.has_key(ns):
+ return
+ cls.alias_dict[ns] = (Namespace2ModuleName(ns), '%s' % namespace_name(cls,ns))
+ cls.alias_list.append(ns)
+ add = classmethod(add)
+
+ def getModuleName(cls, ns):
+ if cls.alias_dict.has_key(ns):
+ return cls.alias_dict[ns][0]
+
+ msg = 'failed to find import for schema "%s"'%ns +\
+ 'possibly missing @schemaLocation attribute.'
+ if ns in SCHEMA.XSD_LIST:
+ msg = 'missing built-in typecode for schema "%s"' %ns
+
+ raise WsdlGeneratorError, msg
+
+ getModuleName = classmethod(getModuleName)
+
+ def getAlias(cls, ns):
+ if cls.alias_dict.has_key(ns):
+ return cls.alias_dict[ns][1]
+
+ msg = 'failed to find import for schema "%s"'%ns +\
+ 'possibly missing @schemaLocation attribute.'
+ if ns in SCHEMA.XSD_LIST:
+ msg = 'missing built-in typecode for schema "%s"' %ns
+
+ raise WsdlGeneratorError, msg
+
+ getAlias = classmethod(getAlias)
+
+ def getNSList(cls):
+ return tuple(cls.alias_list)
+ getNSList = classmethod(getNSList)
+
+
+class StringWriter:
+ """generator util"""
+ def __init__(self, val=None):
+ self.data = []
+ if val:
+ self.data.append(val)
+
+ def set(self, val):
+ if self.data:
+ # in some cases the empty list reassignment fails, so....
+ self.data = None
+ self.data = []
+
+ self.data.append(val)
+
+ def write(self, val):
+ self.data.append(val)
+
+ def getvalue(self):
+ if self.data:
+ return ''.join(self.data)
+ else:
+ return ''
+
+ def __iadd__(self, val):
+ self.data.append(val)
+ return self
+
+ def __str__(self):
+ return self.getvalue()
+
+
+# ---- generated code utils
+
+class MessageContainer:
+ """generator util - used by address.py"""
+ pass
+
+# Extract sub names from message parts so they can be used when mapping
+# a message's contents to a function's arguments.
+# Args is a list of Message Parts. i.e.: op.getInputMessage().parts.values()
+def GetPartsSubNames(args, wsdl):
+ do_extended = True
+ from wsdl2python import WriteServiceModule, SchemaDescription
+ wsm = WriteServiceModule(wsdl, do_extended=do_extended)
+ wsm.gatherNamespaces()
+ toReturn = []
+ for arg in args:
+ argSubnames = []
+ for l in wsm.usedNamespaces.values():
+ for schema in l:
+ sd = SchemaDescription(do_extended=do_extended)
+ sd.fromSchema(schema)
+ argNamespace = arg.element[0]
+ if (sd.targetNamespace == argNamespace):
+ for i in sd.items:
+ # arg.name is the part name, but we want it's type
+ argElementType = arg.element[1]
+ if str(argElementType) == str(i.content.name):
+ argSubnames = []
+ # I'm not sure when the name attribute was dropped
+ # but at some point, or in some circumstance it's not
+ # there, but instead a ref attribute is there which is
+ # tuple of (namespace, name). This hack fixes things,
+ # but I'm not sure why this happens or has happened.
+ # IRJ - 2005-05-25
+ if i.content.mgContent != None:
+ for c in i.content.mgContent:
+ nValue = "None"
+ if c.isWildCard():
+ nValue="any"
+ elif c.attributes.has_key("name"):
+ nValue = c.attributes["name"]
+ elif c.attributes.has_key("ref"):
+ nValue = c.attributes["ref"][1]
+ argSubnames.append(nValue)
+
+ toReturn.append(argSubnames)
+ return toReturn
diff --git a/ZSI/zsi/ZSI/generate/wsdl2dispatch.py b/ZSI/zsi/ZSI/generate/wsdl2dispatch.py
new file mode 100755
index 0000000..d08c2ed
--- /dev/null
+++ b/ZSI/zsi/ZSI/generate/wsdl2dispatch.py
@@ -0,0 +1,531 @@
+#!/usr/bin/env python
+from cStringIO import StringIO
+import ZSI, string, sys, getopt, urlparse, types, warnings
+from ZSI.wstools import WSDLTools
+from ZSI.generate.wsdl2python import WriteServiceModule, MessageTypecodeContainer
+from ZSI.ServiceContainer import ServiceSOAPBinding, SimpleWSResource
+from ZSI.generate.utility import TextProtect, GetModuleBaseNameFromWSDL, NCName_to_ClassName, GetPartsSubNames, TextProtectAttributeName
+from ZSI.generate import WsdlGeneratorError, Wsdl2PythonError
+from ZSI.generate.wsdl2python import SchemaDescription
+
+
+# Split last token
+rsplit = lambda x,sep,: (x[:x.rfind(sep)], x[x.rfind(sep)+1:],)
+if sys.version_info[0:2] == (2, 4, 0, 'final', 0)[0:2]:
+ rsplit = lambda x,sep,: x.rsplit(sep, 1)
+
+class SOAPService:
+ def __init__(self, service):
+ self.classdef = StringIO()
+ self.initdef = StringIO()
+ self.location = ''
+ self.methods = []
+
+ def newMethod(self):
+ '''name -- operation name
+ '''
+ self.methods.append(StringIO())
+ return self.methods[-1]
+
+class ServiceModuleWriter:
+ '''Creates a skeleton for a SOAP service instance.
+ '''
+ indent = ' '*4
+ server_module_suffix = '_services_server'
+ func_aname = TextProtectAttributeName
+ func_aname = staticmethod(func_aname)
+ separate_messages = False # Whether to write message definitions into a separate file.
+
+ def __init__(self, base=ServiceSOAPBinding, prefix='soap', service_class=SOAPService, do_extended=False):
+ '''
+ parameters:
+ base -- either a class definition, or a str representing a qualified class name.
+ prefix -- method prefix.
+ '''
+ self.wsdl = None
+ self.base_class = base
+ self.method_prefix = prefix
+ self._service_class = SOAPService
+
+ self.header = None
+ self.imports = None
+ self._services = None
+ self.client_module_path = None
+ self.client_module_name = None
+ self.messages_module_name = None
+ self.do_extended = do_extended
+
+ def reset(self):
+ self.header = StringIO()
+ self.imports = StringIO()
+ self._services = {}
+
+ def _getBaseClassName(self):
+ '''return base class name, do not override.
+ '''
+ if type(self.base_class) is types.ClassType:
+ return self.base_class.__name__
+ return rsplit(self.base_class, '.')[-1]
+
+ def _getBaseClassModule(self):
+ '''return base class module, do not override.
+ '''
+ if type(self.base_class) is types.ClassType:
+ return self.base_class.__module__
+ if self.base_class.find('.') >= 0:
+ return rsplit(self.base_class, '.')[0]
+ return None
+
+ def getIndent(self, level=1):
+ '''return indent.
+ '''
+ assert 0 < level < 10, 'bad indent level %d' %level
+ return self.indent*level
+
+ def getMethodName(self, method):
+ '''return method name.
+ '''
+ return '%s_%s' %(self.method_prefix, TextProtect(method))
+
+ def getClassName(self, name):
+ '''return class name.
+ '''
+ return NCName_to_ClassName(name)
+
+ def setClientModuleName(self, name):
+ self.client_module_name = name
+
+ def getClientModuleName(self):
+ '''return module name.
+ '''
+ assert self.wsdl is not None, 'initialize, call fromWSDL'
+ if self.client_module_name is not None:
+ return self.client_module_name
+
+ wsm = WriteServiceModule(self.wsdl, do_extended=self.do_extended)
+ return wsm.getClientModuleName()
+
+ def getMessagesModuleName(self):
+ '''return module name.
+ '''
+ assert self.wsdl is not None, 'initialize, call fromWSDL'
+ if self.messages_module_name is not None:
+ return self.messages_module_name
+
+ wsm = WriteServiceModule(self.wsdl, do_extended=self.do_extended)
+ return wsm.getMessagesModuleName()
+
+ def getServiceModuleName(self):
+ '''return module name.
+ '''
+ name = GetModuleBaseNameFromWSDL(self.wsdl)
+ if not name:
+ raise WsdlGeneratorError, 'could not determine a service name'
+
+ if self.server_module_suffix is None:
+ return name
+ return '%s%s' %(name, self.server_module_suffix)
+
+ def getClientModulePath(self):
+ return self.client_module_path
+
+ def setClientModulePath(self, path):
+ '''setup module path to where client module before calling fromWSDL.
+ '''
+ self.client_module_path = path
+
+ def setUpClassDef(self, service):
+ '''set class definition and class variables.
+ service -- ServiceDescription instance
+ '''
+ assert isinstance(service, WSDLTools.Service) is True,\
+ 'expecting WSDLTools.Service instance.'
+
+ s = self._services[service.name].classdef
+ print >>s, 'class %s(%s):' %(self.getClassName(service.name), self._getBaseClassName())
+ print >>s, '%ssoapAction = {}' % self.getIndent(level=1)
+ print >>s, '%sroot = {}' % self.getIndent(level=1)
+ print >>s, "%s_wsdl = \"\"\"%s\"\"\"" % (self.getIndent(level=1), self.raw_wsdl)
+
+ def setUpImports(self):
+ '''set import statements
+ '''
+ path = self.getClientModulePath()
+ i = self.imports
+ if path is None:
+ if self.separate_messages:
+ print >>i, 'from %s import *' %self.getMessagesModuleName()
+ else:
+ print >>i, 'from %s import *' %self.getClientModuleName()
+ else:
+ if self.separate_messages:
+ print >>i, 'from %s.%s import *' %(path, self.getMessagesModuleName())
+ else:
+ print >>i, 'from %s.%s import *' %(path, self.getClientModuleName())
+
+ mod = self._getBaseClassModule()
+ name = self._getBaseClassName()
+ if mod is None:
+ print >>i, 'import %s' %name
+ else:
+ print >>i, 'from %s import %s' %(mod, name)
+
+ def setUpInitDef(self, service):
+ '''set __init__ function
+ '''
+ assert isinstance(service, WSDLTools.Service), 'expecting WSDLTools.Service instance.'
+ sd = self._services[service.name]
+ d = sd.initdef
+
+ if sd.location is not None:
+ scheme,netloc,path,params,query,fragment = urlparse.urlparse(sd.location)
+ print >>d, '%sdef __init__(self, post=\'%s\', **kw):' %(self.getIndent(level=1), path)
+ else:
+ print >>d, '%sdef __init__(self, post, **kw):' %self.getIndent(level=1)
+
+ print >>d, '%s%s.__init__(self, post)' %(self.getIndent(level=2),self._getBaseClassName())
+
+
+ def mangle(self, name):
+ return TextProtect(name)
+
+ def getAttributeName(self, name):
+ return self.func_aname(name)
+
+ def setUpMethods(self, port):
+ '''set up all methods representing the port operations.
+ Parameters:
+ port -- Port that defines the operations.
+ '''
+ assert isinstance(port, WSDLTools.Port), \
+ 'expecting WSDLTools.Port not: ' %type(port)
+
+ sd = self._services.get(port.getService().name)
+ assert sd is not None, 'failed to initialize.'
+
+ binding = port.getBinding()
+ portType = port.getPortType()
+ action_in = ''
+ for bop in binding.operations:
+ try:
+ op = portType.operations[bop.name]
+ except KeyError, ex:
+ raise WsdlGeneratorError,\
+ 'Port(%s) PortType(%s) missing operation(%s) defined in Binding(%s)' \
+ %(port.name,portType.name,bop.name,binding.name)
+
+ for ext in bop.extensions:
+ if isinstance(ext, WSDLTools.SoapOperationBinding):
+ action_in = ext.soapAction
+ break
+ else:
+ warnings.warn('Port(%s) operation(%s) defined in Binding(%s) missing soapAction' \
+ %(port.name,op.name,binding.name)
+ )
+
+ msgin = op.getInputMessage()
+ msgin_name = TextProtect(msgin.name)
+ method_name = self.getMethodName(op.name)
+
+ m = sd.newMethod()
+ print >>m, '%sdef %s(self, ps):' %(self.getIndent(level=1), method_name)
+ if msgin is not None:
+ print >>m, '%sself.request = ps.Parse(%s.typecode)' %(self.getIndent(level=2), msgin_name)
+ else:
+ print >>m, '%s# NO input' %self.getIndent(level=2)
+
+ msgout = op.getOutputMessage()
+
+ if self.do_extended:
+ input_args = msgin.parts.values()
+ iargs = ["%s" % x.name for x in input_args]
+ if msgout is not None:
+ output_args = msgout.parts.values()
+ else:
+ output_args = []
+ oargs = ["%s" % x.name for x in output_args]
+ if output_args:
+ if len(output_args) > 1:
+ print "Message has more than one return value (Bad Design)."
+ output_args = "(%s)" % output_args
+ else:
+ output_args = ""
+ # Get arg list
+ iSubNames = GetPartsSubNames(msgin.parts.values(), self.wsdl)
+ for i in range( len(iargs) ): # should only be one part to messages here anyway
+ argSubNames = iSubNames[i]
+ if len(argSubNames) > 0:
+ subNamesStr = "self.request." + ", self.request.".join(map(self.getAttributeName, argSubNames))
+ if len(argSubNames) > 1:
+ subNamesStr = "(" + subNamesStr + ")"
+ print >>m, "%s%s = %s" % (self.getIndent(level=2), iargs[i], subNamesStr)
+
+ print >>m, "\n%s# If we have an implementation object use it" % (self.getIndent(level=2))
+ print >>m, "%sif hasattr(self,'impl'):" % (self.getIndent(level=2))
+
+ iargsStrList = []
+ for arg in iargs:
+ argSubNames = iSubNames[i]
+ if len(argSubNames) > 0:
+ if len(argSubNames) > 1:
+ for i in range(len(argSubNames)):
+ iargsStrList.append( arg + "[%i]" % i )
+ else:
+ iargsStrList.append( arg )
+ iargsStr = ",".join(iargsStrList)
+ oargsStr = ", ".join(oargs)
+ if len(oargsStr) > 0:
+ oargsStr += " = "
+ print >>m, "%s%sself.impl.%s(%s)" % (self.getIndent(level=3), oargsStr, op.name, iargsStr)
+
+ if msgout is not None:
+ msgout_name = TextProtect(msgout.name)
+ if self.do_extended:
+ print >>m, '\n%sresult = %s()' %(self.getIndent(level=2), msgout_name)
+ oSubNames = GetPartsSubNames(msgout.parts.values(), self.wsdl)
+ if (len(oSubNames) > 0) and (len(oSubNames[0]) > 0):
+ print >>m, "%s# If we have an implementation object, copy the result " % (self.getIndent(level=2))
+ print >>m, "%sif hasattr(self,'impl'):" % (self.getIndent(level=2))
+ # copy result's members
+ for i in range( len(oargs) ): # should only be one part messages here anyway
+ oargSubNames = oSubNames[i]
+ if len(oargSubNames) > 1:
+ print >>m, '%s# Should have a tuple of %i args' %(self.getIndent(level=3), len(oargSubNames))
+ for j in range(len(oargSubNames)):
+ oargSubName = oargSubNames[j]
+ print >>m, '%sresult.%s = %s[%i]' %(self.getIndent(level=3), self.getAttributeName(oargSubName), oargs[i], j)
+ elif len(oargSubNames) == 1:
+ oargSubName = oargSubNames[0]
+ print >>m, '%sresult.%s = %s' %(self.getIndent(level=3), self.getAttributeName(oargSubName), oargs[i])
+ else:
+ raise Exception("The subnames within message " + msgout_name + "'s part were not found. Message is the output of operation " + op.name)
+ print >>m, '%sreturn result' %(self.getIndent(level=2))
+ else:
+ print >>m, '%sreturn %s()' %(self.getIndent(level=2), msgout_name)
+ else:
+ print >>m, '%s# NO output' % self.getIndent(level=2)
+ print >>m, '%sreturn None' % self.getIndent(level=2)
+
+ print >>m, ''
+ print >>m, '%ssoapAction[\'%s\'] = \'%s\'' %(self.getIndent(level=1), action_in, method_name)
+ print >>m, '%sroot[(%s.typecode.nspname,%s.typecode.pname)] = \'%s\'' \
+ %(self.getIndent(level=1), msgin_name, msgin_name, method_name)
+
+ return
+
+ def setUpHeader(self):
+ print >>self.header, '##################################################'
+ print >>self.header, '# %s.py' %self.getServiceModuleName()
+ print >>self.header, '# Generated by %s' %(self.__class__)
+ print >>self.header, '#'
+ print >>self.header, '##################################################'
+
+ def write(self, fd=sys.stdout):
+ '''write out to file descriptor,
+ should not need to override.
+ '''
+ print >>fd, self.header.getvalue()
+ print >>fd, self.imports.getvalue()
+ for k,v in self._services.items():
+ print >>fd, v.classdef.getvalue()
+ print >>fd, v.initdef.getvalue()
+ for s in v.methods:
+ print >>fd, s.getvalue()
+
+ def fromWSDL(self, wsdl):
+ '''setup the service description from WSDL,
+ should not need to override.
+ '''
+ assert isinstance(wsdl, WSDLTools.WSDL), 'expecting WSDL instance'
+
+ if len(wsdl.services) == 0:
+ raise WsdlGeneratorError, 'No service defined'
+
+ self.reset()
+ self.wsdl = wsdl
+ self.raw_wsdl = wsdl.document.toxml().replace("\"", "\\\"")
+ self.setUpHeader()
+ self.setUpImports()
+ for service in wsdl.services:
+ sd = self._service_class(service.name)
+ self._services[service.name] = sd
+
+ for port in service.ports:
+ for e in port.extensions:
+ if isinstance(e, WSDLTools.SoapAddressBinding):
+ sd.location = e.location
+
+ self.setUpMethods(port)
+
+ self.setUpClassDef(service)
+ self.setUpInitDef(service)
+
+
+class WSAServiceModuleWriter(ServiceModuleWriter):
+ '''Creates a skeleton for a WS-Address service instance.
+ '''
+ def __init__(self, base=SimpleWSResource, prefix='wsa', service_class=SOAPService, strict=True):
+ '''
+ Parameters:
+ strict -- check that soapAction and input ws-action do not collide.
+ '''
+ ServiceModuleWriter.__init__(self, base, prefix, service_class)
+ self.strict = strict
+
+ def createMethodBody(msgInName, msgOutName, **kw):
+ '''return a tuple of strings containing the body of a method.
+ msgInName -- None or a str
+ msgOutName -- None or a str
+ '''
+ body = []
+ if msgInName is not None:
+ body.append('self.request = ps.Parse(%s.typecode)' %msgInName)
+
+ if msgOutName is not None:
+ body.append('return %s()' %msgOutName)
+ else:
+ body.append('return None')
+
+ return tuple(body)
+ createMethodBody = staticmethod(createMethodBody)
+
+ def setUpClassDef(self, service):
+ '''use soapAction dict for WS-Action input, setup wsAction
+ dict for grabbing WS-Action output values.
+ '''
+ assert isinstance(service, WSDLTools.Service), 'expecting WSDLTools.Service instance'
+
+ s = self._services[service.name].classdef
+ print >>s, 'class %s(%s):' %(self.getClassName(service.name), self._getBaseClassName())
+ print >>s, '%ssoapAction = {}' % self.getIndent(level=1)
+ print >>s, '%swsAction = {}' % self.getIndent(level=1)
+ print >>s, '%sroot = {}' % self.getIndent(level=1)
+
+ def setUpMethods(self, port):
+ '''set up all methods representing the port operations.
+ Parameters:
+ port -- Port that defines the operations.
+ '''
+ assert isinstance(port, WSDLTools.Port), \
+ 'expecting WSDLTools.Port not: ' %type(port)
+
+ binding = port.getBinding()
+ portType = port.getPortType()
+ service = port.getService()
+ s = self._services[service.name]
+ for bop in binding.operations:
+ try:
+ op = portType.operations[bop.name]
+ except KeyError, ex:
+ raise WsdlGeneratorError,\
+ 'Port(%s) PortType(%s) missing operation(%s) defined in Binding(%s)' \
+ %(port.name, portType.name, op.name, binding.name)
+
+ soap_action = wsaction_in = wsaction_out = None
+ if op.input is not None:
+ wsaction_in = op.getInputAction()
+ if op.output is not None:
+ wsaction_out = op.getOutputAction()
+
+ for ext in bop.extensions:
+ if isinstance(ext, WSDLTools.SoapOperationBinding) is False: continue
+ soap_action = ext.soapAction
+ if wsaction_in is None: break
+ if wsaction_in == soap_action: break
+ if self.strict is False:
+ warnings.warn(\
+ 'Port(%s) operation(%s) in Binding(%s) soapAction(%s) != WS-Action(%s)' \
+ %(port.name, op.name, binding.name, soap_action, wsaction_in),
+ )
+ break
+ raise WsdlGeneratorError,\
+ 'Port(%s) operation(%s) in Binding(%s) soapAction(%s) MUST match WS-Action(%s)' \
+ %(port.name, op.name, binding.name, soap_action, wsaction_in)
+
+ method_name = self.getMethodName(op.name)
+
+ m = s.newMethod()
+ print >>m, '%sdef %s(self, ps, address):' %(self.getIndent(level=1), method_name)
+
+ msgin_name = msgout_name = None
+ msgin,msgout = op.getInputMessage(),op.getOutputMessage()
+ if msgin is not None:
+ msgin_name = TextProtect(msgin.name)
+ if msgout is not None:
+ msgout_name = TextProtect(msgout.name)
+
+ indent = self.getIndent(level=2)
+ for l in self.createMethodBody(msgin_name, msgout_name):
+ print >>m, indent + l
+
+ print >>m, ''
+ print >>m, '%ssoapAction[\'%s\'] = \'%s\'' %(self.getIndent(level=1), wsaction_in, method_name)
+ print >>m, '%swsAction[\'%s\'] = \'%s\'' %(self.getIndent(level=1), method_name, wsaction_out)
+ print >>m, '%sroot[(%s.typecode.nspname,%s.typecode.pname)] = \'%s\'' \
+ %(self.getIndent(level=1), msgin_name, msgin_name, method_name)
+
+
+class DelAuthServiceModuleWriter(ServiceModuleWriter):
+ ''' Includes the generation of lines to call an authorization method on the server side
+ if an authorization function has been defined.
+ '''
+ def __init__(self, base=ServiceSOAPBinding, prefix='soap', service_class=SOAPService, do_extended=False):
+ ServiceModuleWriter.__init__(self, base=base, prefix=prefix, service_class=service_class, do_extended=do_extended)
+
+ def fromWSDL(self, wsdl):
+ ServiceModuleWriter.fromWSDL(self, wsdl)
+ for service in wsdl.services:
+ self.setUpAuthDef(service)
+
+ def setUpInitDef(self, service):
+ ServiceModuleWriter.setUpInitDef(self, service)
+ sd = self._services[service.name]
+ d = sd.initdef
+ print >>d, '%sif kw.has_key(\'impl\'):' % self.getIndent(level=2)
+ print >>d, '%sself.impl = kw[\'impl\']' % self.getIndent(level=3)
+
+ print >>d, '%sself.auth_method_name = None' % self.getIndent(level=2)
+ print >>d, '%sif kw.has_key(\'auth_method_name\'):' % self.getIndent(level=2)
+ print >>d, '%sself.auth_method_name = kw[\'auth_method_name\']' % self.getIndent(level=3)
+
+ def setUpAuthDef(self, service):
+ '''set __auth__ function
+ '''
+ sd = self._services[service.name]
+ e = sd.initdef
+ print >>e, "%sdef authorize(self, auth_info, post, action):" % self.getIndent(level=1)
+ print >>e, "%sif self.auth_method_name and hasattr(self.impl, self.auth_method_name):" % self.getIndent(level=2)
+ print >>e, "%sreturn getattr(self.impl, self.auth_method_name)(auth_info, post, action)" % self.getIndent(level=3)
+ print >>e, "%selse:" % self.getIndent(level=2)
+ print >>e, "%sreturn 1" % self.getIndent(level=3)
+
+class DelAuthWSAServiceModuleWriter(WSAServiceModuleWriter):
+ def __init__(self, base=SimpleWSResource, prefix='wsa', service_class=SOAPService, strict=True):
+ WSAServiceModuleWriter.__init__(self, base=base, prefix=prefix, service_class=service_class, strict=strict)
+
+ def fromWSDL(self, wsdl):
+ WSAServiceModuleWriter.fromWSDL(self, wsdl)
+ for service in wsdl.services:
+ self.setUpAuthDef(service)
+
+ def setUpInitDef(self, service):
+ WSAServiceModuleWriter.setUpInitDef(self, service)
+ sd = self._services[service.name]
+ d = sd.initdef
+ print >>d, '%sif kw.has_key(\'impl\'):' % self.getIndent(level=2)
+ print >>d, '%sself.impl = kw[\'impl\']' % self.getIndent(level=3)
+
+ print >>d, '%sif kw.has_key(\'auth_method_name\'):' % self.getIndent(level=2)
+ print >>d, '%sself.auth_method_name = kw[\'auth_method_name\']' % self.getIndent(level=3)
+
+ def setUpAuthDef(self, service):
+ '''set __auth__ function
+ '''
+ sd = self._services[service.name]
+ e = sd.initdef
+ print >>e, "%sdef authorize(self, auth_info, post, action):" % self.getIndent(level=1)
+ print >>e, "%sif self.auth_method_name and hasattr(self.impl, self.auth_method_name):" % self.getIndent(level=2)
+ print >>e, "%sreturn getattr(self.impl, self.auth_method_name)(auth_info, post, action)" % self.getIndent(level=3)
+ print >>e, "%selse:" % self.getIndent(level=2)
+ print >>e, "%sreturn 1" % self.getIndent(level=3)
+
diff --git a/ZSI/zsi/ZSI/generate/wsdl2python.py b/ZSI/zsi/ZSI/generate/wsdl2python.py
new file mode 100644
index 0000000..d028955
--- /dev/null
+++ b/ZSI/zsi/ZSI/generate/wsdl2python.py
@@ -0,0 +1,536 @@
+############################################################################
+# Monte M. Goode, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+# main generator engine for new generation generator
+
+# $Id: wsdl2python.py 1203 2006-05-03 00:13:20Z boverhof $
+
+import os
+from ZSI import _get_idstr
+from ZSI.wstools.logging import getLogger as _GetLogger
+from ZSI.wstools import WSDLTools
+from ZSI.wstools.WSDLTools import SoapAddressBinding,\
+ SoapBodyBinding, SoapBinding,MimeContentBinding,\
+ HttpUrlEncodedBinding
+from ZSI.wstools.XMLSchema import SchemaReader, ElementDeclaration, SchemaError
+from ZSI.typeinterpreter import BaseTypeInterpreter
+from ZSI.generate import WsdlGeneratorError, Wsdl2PythonError
+from containers import *
+from ZSI.generate import utility
+from ZSI.generate.utility import NamespaceAliasDict as NAD
+from ZSI.generate.utility import GetModuleBaseNameFromWSDL
+
+"""
+classes:
+ WriteServiceModule
+ -- composes/writes out client stubs and types module.
+
+ ServiceDescription
+ -- represents a single WSDL service.
+
+ MessageWriter
+ -- represents a single WSDL Message and associated bindings
+ of the port/binding.
+
+ SchemaDescription
+ -- generates classes for defs and decs in the schema instance.
+
+ TypeWriter
+ -- represents a type definition.
+
+ ElementWriter
+ -- represents a element declaration.
+
+"""
+
+class WriteServiceModule:
+ """top level driver class invoked by wsd2py
+ class variables:
+ client_module_suffix -- suffix of client module.
+ types_module_suffix -- suffix of types module.
+ """
+ client_module_suffix = '_services'
+ messages_module_suffix = '_messages'
+ types_module_suffix = '_services_types'
+ logger = _GetLogger("WriteServiceModule")
+
+ def __init__(self, wsdl, addressing=False, notification=False,
+ do_extended=False, extPyClasses=None, configParser = None):
+ self._wsdl = wsdl
+ self._addressing = addressing
+ self._notification = notification
+ self._configParser = configParser
+ self.usedNamespaces = None
+ self.services = []
+ self.client_module_path = None
+ self.types_module_name = None
+ self.types_module_path = None
+ self.messages_module_path = None # used in extended generation
+ self.do_extended = do_extended
+ self.extPyClasses = extPyClasses
+
+ def getClientModuleName(self):
+ """client module name.
+ """
+ name = GetModuleBaseNameFromWSDL(self._wsdl)
+ if not name:
+ raise WsdlGeneratorError, 'could not determine a service name'
+
+ if self.client_module_suffix is None:
+ return name
+
+ return '%s%s' %(name, self.client_module_suffix)
+
+ def getMessagesModuleName(self):
+ name = GetModuleBaseNameFromWSDL(self._wsdl)
+ if not name:
+ raise WsdlGeneratorError, 'could not determine a service name'
+
+ if self.messages_module_suffix is None:
+ return name
+
+ if len(self.messages_module_suffix) == 0:
+ return self.getClientModuleName()
+
+ return '%s%s' %(name, self.messages_module_suffix)
+
+ def setTypesModuleName(self, name):
+ self.types_module_name = name
+
+ def getTypesModuleName(self):
+ """types module name.
+ """
+ if self.types_module_name is not None:
+ return self.types_module_name
+
+ name = GetModuleBaseNameFromWSDL(self._wsdl)
+ if not name:
+ raise WsdlGeneratorError, 'could not determine a service name'
+
+ if self.types_module_suffix is None:
+ return name
+
+ return '%s%s' %(name, self.types_module_suffix)
+
+ def setClientModulePath(self, path):
+ """setup module path to where client module before calling fromWsdl.
+ module path to types module eg. MyApp.client
+ """
+ self.client_module_path = path
+
+ def getTypesModulePath(self):
+ """module path to types module eg. MyApp.types
+ """
+ return self.types_module_path
+
+ def getMessagesModulePath(self):
+ '''module path to messages module
+ same as types path
+ '''
+ return self.messages_module_path
+
+ def setTypesModulePath(self, path):
+ """setup module path to where service module before calling fromWsdl.
+ module path to types module eg. MyApp.types
+ """
+ self.types_module_path = path
+
+ def setMessagesModulePath(self, path):
+ """setup module path to where message module before calling fromWsdl.
+ module path to types module eg. MyApp.types
+ """
+ self.messages_module_path = path
+
+ def gatherNamespaces(self):
+ '''This method must execute once.. Grab all schemas
+ representing each targetNamespace.
+ '''
+ if self.usedNamespaces is not None:
+ return
+
+ self.logger.debug('gatherNamespaces')
+ self.usedNamespaces = {}
+
+ # Add all schemas defined in wsdl
+ # to used namespace and to the Alias dict
+ for schema in self._wsdl.types.values():
+ tns = schema.getTargetNamespace()
+ self.logger.debug('Register schema(%s) -- TNS(%s)'\
+ %(_get_idstr(schema), tns),)
+ if self.usedNamespaces.has_key(tns) is False:
+ self.usedNamespaces[tns] = []
+ self.usedNamespaces[tns].append(schema)
+ NAD.add(tns)
+
+ # Add all xsd:import schema instances
+ # to used namespace and to the Alias dict
+ for k,v in SchemaReader.namespaceToSchema.items():
+ self.logger.debug('Register schema(%s) -- TNS(%s)'\
+ %(_get_idstr(v), k),)
+ if self.usedNamespaces.has_key(k) is False:
+ self.usedNamespaces[k] = []
+ self.usedNamespaces[k].append(v)
+ NAD.add(k)
+
+ def writeClient(self, fd, sdClass=None, msg_fd=None, **kw):
+ """write out client module to file descriptor.
+ Parameters and Keywords arguments:
+ fd -- file descriptor
+ msg_fd -- optional messsages module file descriptor
+ sdClass -- service description class name
+ imports -- list of imports
+ readerclass -- class name of ParsedSoap reader
+ writerclass -- class name of SoapWriter writer
+ """
+ sdClass = sdClass or ServiceDescription
+ assert issubclass(sdClass, ServiceDescription), \
+ 'parameter sdClass must subclass ServiceDescription'
+
+ header = '%s \n# %s.py \n# generated by %s\n%s\n\n'\
+ %('#'*50, self.getClientModuleName(), self.__module__, '#'*50)
+ fd.write(header)
+
+ if msg_fd is not None:
+ # TODO: Why is this not getMessagesModuleName?
+ msg_filename = str(self.getClientModuleName()).replace("client", "messages")
+ if self.getMessagesModulePath() is not None:
+ msg_filename = os.path.join(self.getMessagesModulePath(), msg_filename)
+ messages_header = header.replace("client", "messages")
+ msg_fd.write(messages_header)
+
+ self.services = []
+ for service in self._wsdl.services:
+ sd = sdClass(self._addressing, do_extended=self.do_extended, wsdl=self._wsdl)
+ if len(self._wsdl.types) > 0:
+ sd.setTypesModuleName(self.getTypesModuleName(), self.getTypesModulePath())
+ sd.setMessagesModuleName(self.getMessagesModuleName(), self.getMessagesModulePath())
+
+ self.gatherNamespaces()
+ sd.fromWsdl(service, **kw)
+ sd.write(fd,msg_fd)
+ self.services.append(sd)
+
+ def writeTypes(self, fd):
+ """write out types module to file descriptor.
+ """
+ header = '%s \n# %s.py \n# generated by %s\n%s\n\n'\
+ %('#'*50, self.getTypesModuleName(), self.__module__, '#'*50)
+ fd.write(header)
+ print >>fd, TypesHeaderContainer()
+ self.gatherNamespaces()
+ for l in self.usedNamespaces.values():
+ sd = SchemaDescription(do_extended=self.do_extended, extPyClasses=self.extPyClasses)
+ for schema in l:
+ sd.fromSchema(schema)
+ sd.write(fd)
+
+
+class ServiceDescription:
+ """client interface - locator, port, etc classes"""
+ separate_messages = False
+ logger = _GetLogger("ServiceDescription")
+
+ def __init__(self, addressing=False, do_extended=False, wsdl=None):
+ self.typesModuleName = None
+ self.messagesModuleName = None
+ self.wsAddressing = addressing
+ self.imports = ServiceHeaderContainer()
+ self.messagesImports = ServiceHeaderContainer()
+ self.locator = ServiceLocatorContainer()
+ self.methods = []
+ self.messages = []
+ self.do_extended=do_extended
+ self._wsdl = wsdl # None unless do_extended == True
+
+ def setTypesModuleName(self, name, modulePath=None):
+ """The types module to be imported.
+ Parameters
+ name -- name of types module
+ modulePath -- optional path where module is located.
+ """
+ self.typesModuleName = '%s' %name
+ if modulePath is not None:
+ self.typesModuleName = '%s.%s' %(modulePath,name)
+
+ def setMessagesModuleName(self, name, modulePath=None):
+ '''The types module to be imported.
+ Parameters
+ name -- name of types module
+ modulePath -- optional path where module is located.
+ '''
+ self.messagesModuleName = '%s' %name
+ if modulePath is not None:
+ self.messagesModuleName = '%s.%s' %(modulePath,name)
+
+ def fromWsdl(self, service, **kw):
+ self.imports.setTypesModuleName(self.typesModuleName)
+ if self.separate_messages:
+ self.messagesImports.setMessagesModuleName(self.messagesModuleName)
+ self.imports.appendImport(kw.get('imports', []))
+
+ self.locator.setUp(service)
+
+ for port in service.ports:
+ sop_container = ServiceOperationsClassContainer(useWSA=self.wsAddressing, do_extended=self.do_extended, wsdl=self._wsdl)
+ try:
+ sop_container.setUp(port)
+ except Wsdl2PythonError, ex:
+ self.logger.warning('Skipping port(%s)' %port.name)
+ if len(ex.args):
+ self.logger.warning(ex.args[0])
+ else:
+ sop_container.setReaderClass(kw.get('readerclass'))
+ sop_container.setWriterClass(kw.get('writerclass'))
+ for soc in sop_container.operations:
+ if soc.hasInput() is True:
+ mw = MessageWriter(do_extended=self.do_extended)
+ mw.setUp(soc, port, input=True)
+ self.messages.append(mw)
+
+ if soc.hasOutput() is True:
+ mw = MessageWriter(do_extended=self.do_extended)
+ mw.setUp(soc, port, input=False)
+ self.messages.append(mw)
+
+ self.methods.append(sop_container)
+
+
+ def write(self, fd, msg_fd=None):
+ """write out module to file descriptor.
+ fd -- file descriptor to write out service description.
+ msg_fd -- optional file descriptor for messages module.
+ """
+ if msg_fd != None:
+ print >>fd, self.messagesImports
+ print >>msg_fd, self.imports
+ else:
+ print >>fd, self.imports
+ print >>fd, self.locator
+ for m in self.methods:
+ print >>fd, m
+
+ if msg_fd != None:
+ for m in self.messages:
+ print >>msg_fd, m
+ else:
+ for m in self.messages:
+ print >>fd, m
+
+
+class MessageWriter:
+ logger = _GetLogger("MessageWriter")
+
+ def __init__(self, do_extended=False):
+ """Representation of a WSDL Message and associated WSDL Binding.
+ operation --
+ boperation --
+ input --
+ rpc --
+ literal --
+ simple --
+ """
+ self.content = None
+ self.do_extended = do_extended
+
+ def __str__(self):
+ if not self.content:
+ raise Wsdl2PythonError, 'Must call setUp.'
+ return self.content.getvalue()
+
+ def setUp(self, soc, port, input=False):
+ assert isinstance(soc, ServiceOperationContainer),\
+ 'expecting a ServiceOperationContainer instance'
+ assert isinstance(port, WSDLTools.Port),\
+ 'expecting a WSDL.Port instance'
+
+ rpc,literal = soc.isRPC(), soc.isLiteral(input)
+ kw,klass = {}, None
+
+ if rpc and literal:
+ klass = ServiceRPCLiteralMessageContainer
+ elif not rpc and literal:
+ kw['do_extended'] = self.do_extended
+ klass = ServiceDocumentLiteralMessageContainer
+ elif rpc and not literal:
+ klass = ServiceRPCEncodedMessageContainer
+ else:
+ raise WsdlGeneratorError, 'doc/enc not supported.'
+
+ self.content = klass(**kw)
+ self.content.setUp(port, soc, input)
+
+
+class SchemaDescription:
+ """generates classes for defs and decs in the schema instance.
+ """
+ logger = _GetLogger("SchemaDescription")
+
+ def __init__(self, do_extended=False, extPyClasses=None):
+ self.classHead = NamespaceClassHeaderContainer()
+ self.classFoot = NamespaceClassFooterContainer()
+ self.items = []
+ self.__types = []
+ self.__elements = []
+ self.targetNamespace = None
+ self.do_extended=do_extended
+ self.extPyClasses = extPyClasses
+
+ def fromSchema(self, schema):
+ ''' Can be called multiple times, but will not redefine a
+ previously defined type definition or element declaration.
+ '''
+ ns = schema.getTargetNamespace()
+ assert self.targetNamespace is None or self.targetNamespace == ns,\
+ 'SchemaDescription instance represents %s, not %s'\
+ %(self.targetNamespace, ns)
+
+ if self.targetNamespace is None:
+ self.targetNamespace = ns
+
+ self.classHead.ns = self.classFoot.ns = ns
+ for item in [t for t in schema.types if t.getAttributeName() not in self.__types]:
+ self.__types.append(item.getAttributeName())
+ self.items.append(TypeWriter(do_extended=self.do_extended, extPyClasses=self.extPyClasses))
+ self.items[-1].fromSchemaItem(item)
+
+ for item in [e for e in schema.elements if e.getAttributeName() not in self.__elements]:
+ self.__elements.append(item.getAttributeName())
+ self.items.append(ElementWriter(do_extended=self.do_extended))
+ self.items[-1].fromSchemaItem(item)
+
+ def getTypes(self):
+ return self.__types
+
+ def getElements(self):
+ return self.__elements
+
+ def write(self, fd):
+ """write out to file descriptor.
+ """
+ print >>fd, self.classHead
+ for t in self.items:
+ print >>fd, t
+ print >>fd, self.classFoot
+
+class SchemaItemWriter:
+ """contains/generates a single declaration"""
+ logger = _GetLogger("SchemaItemWriter")
+
+ def __init__(self, do_extended=False, extPyClasses=None):
+ self.content = None
+ self.do_extended=do_extended
+ self.extPyClasses=extPyClasses
+
+ def __str__(self):
+ '''this appears to set up whatever is in self.content.localElements,
+ local elements simpleType|complexType.
+ '''
+ assert self.content is not None, 'Must call fromSchemaItem to setup.'
+ return str(self.content)
+
+ def fromSchemaItem(self, item):
+ raise NotImplementedError, ''
+
+
+class ElementWriter(SchemaItemWriter):
+ """contains/generates a single declaration"""
+ logger = _GetLogger("ElementWriter")
+
+ def fromSchemaItem(self, item):
+ """set up global elements.
+ """
+ if item.isElement() is False or item.isLocal() is True:
+ raise TypeError, 'expecting global element declaration: %s' %item.getItemTrace()
+
+ local = False
+ qName = item.getAttribute('type')
+ if not qName:
+ etp = item.content
+ local = True
+ else:
+ etp = item.getTypeDefinition('type')
+
+ if etp is None:
+ if local is True:
+ self.content = ElementLocalComplexTypeContainer(do_extended=self.do_extended)
+ else:
+ self.content = ElementSimpleTypeContainer()
+ elif etp.isLocal() is False:
+ self.content = ElementGlobalDefContainer()
+ elif etp.isSimple() is True:
+ self.content = ElementLocalSimpleTypeContainer()
+ elif etp.isComplex():
+ self.content = ElementLocalComplexTypeContainer(do_extended=self.do_extended)
+ else:
+ raise Wsdl2PythonError, "Unknown element declaration: %s" %item.getItemTrace()
+
+ self.logger.debug('ElementWriter setUp container "%r", Schema Item "%s"' %(
+ self.content, item.getItemTrace()))
+
+ self.content.setUp(item)
+
+
+class TypeWriter(SchemaItemWriter):
+ """contains/generates a single definition"""
+ logger = _GetLogger("TypeWriter")
+
+ def fromSchemaItem(self, item):
+ if item.isDefinition() is False or item.isLocal() is True:
+ raise TypeError, \
+ 'expecting global type definition not: %s' %item.getItemTrace()
+
+ self.content = None
+ if item.isSimple():
+ if item.content.isRestriction():
+ self.content = RestrictionContainer()
+ elif item.content.isUnion():
+ self.content = UnionContainer()
+ elif item.content.isList():
+ self.content = ListContainer()
+ else:
+ raise Wsdl2PythonError,\
+ 'unknown simple type definition: %s' %item.getItemTrace()
+
+ self.content.setUp(item)
+ return
+
+ if item.isComplex():
+ kw = {}
+ if item.content is None or item.content.isModelGroup():
+ self.content = \
+ ComplexTypeContainer(\
+ do_extended=self.do_extended,
+ extPyClasses=self.extPyClasses
+ )
+ kw['empty'] = item.content is None
+ elif item.content.isSimple():
+ self.content = ComplexTypeSimpleContentContainer()
+ elif item.content.isComplex():
+ self.content = \
+ ComplexTypeComplexContentContainer(\
+ do_extended=self.do_extended
+ )
+ else:
+ raise Wsdl2PythonError,\
+ 'unknown complex type definition: %s' %item.getItemTrace()
+
+ self.logger.debug('TypeWriter setUp container "%r", Schema Item "%s"' %(
+ self.content, item.getItemTrace()))
+
+ try:
+ self.content.setUp(item, **kw)
+ except Exception, ex:
+ args = ['Failure in setUp: %s' %item.getItemTrace()]
+ args += ex.args
+ ex.args = tuple(args)
+ raise
+
+ return
+
+ raise TypeError,\
+ 'expecting SimpleType or ComplexType: %s' %item.getItemTrace()
+
+
+
diff --git a/ZSI/zsi/ZSI/parse.py b/ZSI/zsi/ZSI/parse.py
new file mode 100644
index 0000000..b559b09
--- /dev/null
+++ b/ZSI/zsi/ZSI/parse.py
@@ -0,0 +1,375 @@
+#! /usr/bin/env python
+# $Header$
+'''SOAP messaging parsing.
+'''
+
+from ZSI import _copyright, _children, _attrs, _child_elements, _stringtypes, \
+ _backtrace, EvaluateException, ParseException, _valid_encoding, \
+ _Node, _find_attr, _resolve_prefix
+from ZSI.TC import AnyElement
+import types
+
+from ZSI.wstools.Namespaces import SOAP, XMLNS
+from ZSI.wstools.Utility import SplitQName
+
+_find_actor = lambda E: E.getAttributeNS(SOAP.ENV, "actor") or None
+_find_mu = lambda E: E.getAttributeNS(SOAP.ENV, "mustUnderstand")
+_find_root = lambda E: E.getAttributeNS(SOAP.ENC, "root")
+_find_id = lambda E: _find_attr(E, 'id')
+
+class ParsedSoap:
+ '''A Parsed SOAP object.
+ Convert the text to a DOM tree and parse SOAP elements.
+ Instance data:
+ reader -- the DOM reader
+ dom -- the DOM object
+ ns_cache -- dictionary (by id(node)) of namespace dictionaries
+ id_cache -- dictionary (by XML ID attr) of elements
+ envelope -- the node holding the SOAP Envelope
+ header -- the node holding the SOAP Header (or None)
+ body -- the node holding the SOAP Body
+ body_root -- the serialization root in the SOAP Body
+ data_elements -- list of non-root elements in the SOAP Body
+ trailer_elements -- list of elements following the SOAP body
+ '''
+ defaultReaderClass = None
+
+ def __init__(self, input, readerclass=None, keepdom=False,
+ trailers=False, resolver=None, envelope=True, **kw):
+ '''Initialize.
+ Keyword arguments:
+ trailers -- allow trailer elments (default is zero)
+ resolver -- function (bound method) to resolve URI's
+ readerclass -- factory class to create a reader
+ keepdom -- do not release the DOM
+ envelope -- look for a SOAP envelope.
+ '''
+
+ self.readerclass = readerclass
+ self.keepdom = keepdom
+ if not self.readerclass:
+ if self.defaultReaderClass != None:
+ self.readerclass = self.defaultReaderClass
+ else:
+ from xml.dom.ext.reader import PyExpat
+ self.readerclass = PyExpat.Reader
+ try:
+ self.reader = self.readerclass()
+ if type(input) in _stringtypes:
+ self.dom = self.reader.fromString(input)
+ else:
+ self.dom = self.reader.fromStream(input)
+ except Exception, e:
+ # Is this in the header? Your guess is as good as mine.
+ #raise ParseException("Can't parse document (" + \
+ # str(e.__class__) + "): " + str(e), 0)
+ raise
+
+ self.ns_cache = {
+ id(self.dom): {
+ 'xml': XMLNS.XML,
+ 'xmlns': XMLNS.BASE,
+ '': ''
+ }
+ }
+ self.trailers, self.resolver, self.id_cache = trailers, resolver, {}
+
+ # Exactly one child element
+ c = [ E for E in _children(self.dom)
+ if E.nodeType == _Node.ELEMENT_NODE]
+ if len(c) == 0:
+ raise ParseException("Document has no Envelope", 0)
+ if len(c) != 1:
+ raise ParseException("Document has extra child elements", 0)
+
+ if envelope is False:
+ self.body_root = c[0]
+ return
+
+ # And that one child must be the Envelope
+ elt = c[0]
+ if elt.localName != "Envelope" \
+ or elt.namespaceURI != SOAP.ENV:
+ raise ParseException('Document has "' + elt.localName + \
+ '" element, not Envelope', 0)
+ self._check_for_legal_children("Envelope", elt)
+ for a in _attrs(elt):
+ name = a.nodeName
+ if name.find(":") == -1 and name not in [ "xmlns", "id" ]:
+ raise ParseException('Unqualified attribute "' + \
+ name + '" in Envelope', 0)
+ self.envelope = elt
+ if not _valid_encoding(self.envelope):
+ raise ParseException("Envelope has invalid encoding", 0)
+
+ # Get Envelope's child elements.
+ c = [ E for E in _children(self.envelope)
+ if E.nodeType == _Node.ELEMENT_NODE ]
+ if len(c) == 0:
+ raise ParseException("Envelope is empty (no Body)", 0)
+
+ # Envelope's first child might be the header; if so, nip it off.
+ elt = c[0]
+ if elt.localName == "Header" \
+ and elt.namespaceURI == SOAP.ENV:
+ self._check_for_legal_children("Header", elt)
+ self._check_for_pi_nodes(_children(elt), 1)
+ self.header = c.pop(0)
+ self.header_elements = _child_elements(self.header)
+ else:
+ self.header, self.header_elements = None, []
+
+ # Now the first child must be the body
+ if len(c) == 0:
+ raise ParseException("Envelope has header but no Body", 0)
+ elt = c.pop(0)
+ if elt.localName != "Body" \
+ or elt.namespaceURI != SOAP.ENV:
+ if self.header:
+ raise ParseException('Header followed by "' + \
+ elt.localName + \
+ '" element, not Body', 0, elt, self.dom)
+ else:
+ raise ParseException('Document has "' + \
+ elt.localName + \
+ '" element, not Body', 0, elt, self.dom)
+ self._check_for_legal_children("Body", elt, 0)
+ self._check_for_pi_nodes(_children(elt), 0)
+ self.body = elt
+ if not _valid_encoding(self.body):
+ raise ParseException("Body has invalid encoding", 0)
+
+ # Trailer elements.
+ if not self.trailers:
+ if len(c):
+ raise ParseException("Element found after Body",
+ 0, elt, self.dom)
+ # Don't set self.trailer_elements = []; if user didn't ask
+ # for trailers we *want* to throw an exception.
+ else:
+ self.trailer_elements = c
+ for elt in self.trailer_elements:
+ if not elt.namespaceURI:
+ raise ParseException('Unqualified trailer element',
+ 0, elt, self.dom)
+
+ # Find the serialization root. Divide the Body children into
+ # root (root=1), no (root=0), maybe (no root attribute).
+ self.body_root, no, maybe = None, [], []
+ for elt in _child_elements(self.body):
+ root = _find_root(elt)
+ if root == "1":
+ if self.body_root:
+ raise ParseException("Multiple seralization roots found",
+ 0, elt, self.dom)
+ self.body_root = elt
+ elif root == "0":
+ no.append(elt)
+ elif not root:
+ maybe.append(elt)
+ else:
+ raise ParseException('Illegal value for root attribute',
+ 0, elt, self.dom)
+
+ # If we didn't find a root, get the first one that didn't
+ # say "not me", unless they all said "not me."
+ if self.body_root is None:
+ if len(maybe):
+ self.body_root = maybe[0]
+ else:
+ raise ParseException('No serialization root found',
+ 0, self.body, self.dom)
+ if not _valid_encoding(self.body_root):
+ raise ParseException("Invalid encoding", 0,
+ elt, self.dom)
+
+ # Now get all the non-roots (in order!).
+ rootid = id(self.body_root)
+ self.data_elements = [ E for E in _child_elements(self.body)
+ if id(E) != rootid ]
+ self._check_for_pi_nodes(self.data_elements, 0)
+
+ def __del__(self):
+ try:
+ if not self.keepdom:
+ self.reader.releaseNode(self.dom)
+ except:
+ pass
+
+ def _check_for_legal_children(self, name, elt, mustqualify=1):
+ '''Check if all children of this node are elements or whitespace-only
+ text nodes.
+ '''
+ inheader = name == "Header"
+ for n in _children(elt):
+ t = n.nodeType
+ if t == _Node.COMMENT_NODE: continue
+ if t != _Node.ELEMENT_NODE:
+ if t == _Node.TEXT_NODE and n.nodeValue.strip() == "":
+ continue
+ raise ParseException("Non-element child in " + name,
+ inheader, elt, self.dom)
+ if mustqualify and not n.namespaceURI:
+ raise ParseException('Unqualified element "' + \
+ n.nodeName + '" in ' + name, inheader, elt, self.dom)
+
+ def _check_for_pi_nodes(self, list, inheader):
+ '''Raise an exception if any of the list descendants are PI nodes.
+ '''
+ list = list[:]
+ while list:
+ elt = list.pop()
+ t = elt.nodeType
+ if t == _Node.PROCESSING_INSTRUCTION_NODE:
+ raise ParseException('Found processing instruction "<?' + \
+ elt.nodeName + '...>"',
+ inheader, elt.parentNode, self.dom)
+ elif t == _Node.DOCUMENT_TYPE_NODE:
+ raise ParseException('Found DTD', inheader,
+ elt.parentNode, self.dom)
+ list += _children(elt)
+
+ def Backtrace(self, elt):
+ '''Return a human-readable "backtrace" from the document root to
+ the specified element.
+ '''
+ return _backtrace(elt, self.dom)
+
+ def FindLocalHREF(self, href, elt, headers=1):
+ '''Find a local HREF in the data elements.
+ '''
+ if href[0] != '#':
+ raise EvaluateException(
+ 'Absolute HREF ("%s") not implemented' % href,
+ self.Backtrace(elt))
+ frag = href[1:]
+ # Already found?
+ e = self.id_cache.get(frag)
+ if e: return e
+ # Do a breadth-first search, in the data first. Most likely
+ # to find multi-ref targets shallow in the data area.
+ list = self.data_elements[:] + [self.body_root]
+ if headers: list.extend(self.header_elements)
+ while list:
+ e = list.pop()
+ if e.nodeType == _Node.ELEMENT_NODE:
+ nodeid = _find_id(e)
+ if nodeid:
+ self.id_cache[nodeid] = e
+ if nodeid == frag: return e
+ list += _children(e)
+ raise EvaluateException('''Can't find node for HREF "%s"''' % href,
+ self.Backtrace(elt))
+
+ def ResolveHREF(self, uri, tc, **keywords):
+ r = getattr(tc, 'resolver', self.resolver)
+ if not r:
+ raise EvaluateException('No resolver for "' + uri + '"')
+ try:
+ if type(uri) == types.UnicodeType: uri = str(uri)
+ retval = r(uri, tc, self, **keywords)
+ except Exception, e:
+ raise EvaluateException('''Can't resolve "''' + uri + '" (' + \
+ str(e.__class__) + "): " + str(e))
+ return retval
+
+ def GetMyHeaderElements(self, actorlist=None):
+ '''Return a list of all elements intended for these actor(s).
+ '''
+ if actorlist is None:
+ actorlist = [None, SOAP.ACTOR_NEXT]
+ else:
+ actorlist = list(actorlist) + [None, SOAP.ACTOR_NEXT]
+ return [ E for E in self.header_elements
+ if _find_actor(E) in actorlist ]
+
+ def GetElementNSdict(self, elt):
+ '''Get a dictionary of all the namespace attributes for the indicated
+ element. The dictionaries are cached, and we recurse up the tree
+ as necessary.
+ '''
+ d = self.ns_cache.get(id(elt))
+ if not d:
+ if elt != self.dom: d = self.GetElementNSdict(elt.parentNode)
+ for a in _attrs(elt):
+ if a.namespaceURI == XMLNS.BASE:
+ if a.localName == "xmlns":
+ d[''] = a.nodeValue
+ else:
+ d[a.localName] = a.nodeValue
+ self.ns_cache[id(elt)] = d
+ return d.copy()
+
+ def GetDomAndReader(self):
+ '''Returns a tuple containing the dom and reader objects. (dom, reader)
+ Unless keepdom is true, the dom and reader objects will go out of scope
+ when the ParsedSoap instance is deleted. If keepdom is true, the reader
+ object is needed to properly clean up the dom tree with
+ reader.releaseNode(dom).
+ '''
+ return (self.dom, self.reader)
+
+ def IsAFault(self):
+ '''Is this a fault message?
+ '''
+ e = self.body_root
+ if not e: return 0
+ return e.namespaceURI == SOAP.ENV and e.localName == 'Fault'
+
+ def Parse(self, how):
+ '''Parse the message.
+ '''
+ if type(how) == types.ClassType: how = how.typecode
+ return how.parse(self.body_root, self)
+
+ def WhatMustIUnderstand(self):
+ '''Return a list of (uri,localname) tuples for all elements in the
+ header that have mustUnderstand set.
+ '''
+ return [ ( E.namespaceURI, E.localName )
+ for E in self.header_elements if _find_mu(E) == "1" ]
+
+ def WhatActorsArePresent(self):
+ '''Return a list of URI's of all the actor attributes found in
+ the header. The special actor "next" is ignored.
+ '''
+ results = []
+ for E in self.header_elements:
+ a = _find_actor(E)
+ if a not in [ None, SOAP.ACTOR_NEXT ]: results.append(a)
+ return results
+
+ def ParseHeaderElements(self, ofwhat):
+ '''Returns a dictionary of pyobjs.
+ ofhow -- list of typecodes w/matching nspname/pname to the header_elements.
+ '''
+ d = {}
+ lenofwhat = len(ofwhat)
+ c, crange = self.header_elements[:], range(len(self.header_elements))
+ for i,what in [ (i, ofwhat[i]) for i in range(lenofwhat) ]:
+ if isinstance(what, AnyElement):
+ raise EvaluateException, 'not supporting <any> as child of SOAP-ENC:Header'
+
+ v = []
+ occurs = 0
+ namespaceURI,tagName = what.nspname,what.pname
+ for j,c_elt in [ (j, c[j]) for j in crange if c[j] ]:
+ prefix,name = SplitQName(c_elt.tagName)
+ nsuri = _resolve_prefix(c_elt, prefix)
+ if tagName == name and namespaceURI == nsuri:
+ pyobj = what.parse(c_elt, self)
+ else:
+ continue
+ v.append(pyobj)
+ c[j] = None
+ if what.minOccurs > len(v) > what.maxOccurs:
+ raise EvaluateException, 'number of occurances(%d) doesnt fit constraints (%d,%s)'\
+ %(len(v),what.minOccurs,what.maxOccurs)
+ if what.maxOccurs == 1:
+ if len(v) == 0: v = None
+ else: v = v[0]
+ d[(what.nspname,what.pname)] = v
+ return d
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/resolvers.py b/ZSI/zsi/ZSI/resolvers.py
new file mode 100644
index 0000000..33a182a
--- /dev/null
+++ b/ZSI/zsi/ZSI/resolvers.py
@@ -0,0 +1,147 @@
+#! /usr/bin/env python
+# $Header$
+'''SOAP messaging parsing.
+'''
+
+from ZSI import _copyright, _child_elements, EvaluateException, TC
+import multifile, mimetools, urllib
+from base64 import decodestring as b64decode
+import cStringIO as StringIO
+
+
+def Opaque(uri, tc, ps, **keywords):
+ '''Resolve a URI and return its content as a string.
+ '''
+ source = urllib.urlopen(uri, **keywords)
+ enc = source.info().getencoding()
+ if enc in ['7bit', '8bit', 'binary']: return source.read()
+
+ data = StringIO.StringIO()
+ mimetools.decode(source, data, enc)
+ return data.getvalue()
+
+
+def XML(uri, tc, ps, **keywords):
+ '''Resolve a URI and return its content as an XML DOM.
+ '''
+ source = urllib.urlopen(uri, **keywords)
+ enc = source.info().getencoding()
+ if enc in ['7bit', '8bit', 'binary']:
+ data = source
+ else:
+ data = StringIO.StringIO()
+ mimetools.decode(source, data, enc)
+ data.seek(0)
+ dom = ps.readerclass().fromStream(data)
+ return _child_elements(dom)[0]
+
+
+class NetworkResolver:
+ '''A resolver that support string and XML.
+ '''
+
+ def __init__(self, prefix=None):
+ self.allowed = prefix or []
+
+ def _check_allowed(self, uri):
+ for a in self.allowed:
+ if uri.startswith(a): return
+ raise EvaluateException("Disallowed URI prefix")
+
+ def Opaque(self, uri, tc, ps, **keywords):
+ self._check_allowed(uri)
+ return Opaque(uri, tc, ps, **keywords)
+
+ def XML(self, uri, tc, ps, **keywords):
+ self._check_allowed(uri)
+ return XML(uri, tc, ps, **keywords)
+
+ def Resolve(self, uri, tc, ps, **keywords):
+ if isinstance(tc, TC.XML):
+ return XML(uri, tc, ps, **keywords)
+ return Opaque(uri, tc, ps, **keywords)
+
+
+class MIMEResolver:
+ '''Multi-part MIME resolver -- SOAP With Attachments, mostly.
+ '''
+
+ def __init__(self, ct, f, next=None, uribase='thismessage:/',
+ seekable=0, **kw):
+ # Get the boundary. It's too bad I have to write this myself,
+ # but no way am I going to import cgi for 10 lines of code!
+ for param in ct.split(';'):
+ a = param.strip()
+ if a.startswith('boundary='):
+ if a[9] in [ '"', "'" ]:
+ boundary = a[10:-1]
+ else:
+ boundary = a[9:]
+ break
+ else:
+ raise ValueError('boundary parameter not found')
+
+ self.id_dict, self.loc_dict, self.parts = {}, {}, []
+ self.next = next
+ self.base = uribase
+
+ mf = multifile.MultiFile(f, seekable)
+ mf.push(boundary)
+ while mf.next():
+ head = mimetools.Message(mf)
+ body = StringIO.StringIO()
+ mimetools.decode(mf, body, head.getencoding())
+ body.seek(0)
+ part = (head, body)
+ self.parts.append(part)
+ key = head.get('content-id')
+ if key:
+ if key[0] == '<' and key[-1] == '>': key = key[1:-1]
+ self.id_dict[key] = part
+ key = head.get('content-location')
+ if key: self.loc_dict[key] = part
+ mf.pop()
+
+ def GetSOAPPart(self):
+ '''Get the SOAP body part.
+ '''
+ head, part = self.parts[0]
+ return StringIO.StringIO(part.getvalue())
+
+ def get(self, uri):
+ '''Get the content for the bodypart identified by the uri.
+ '''
+ if uri.startswith('cid:'):
+ # Content-ID, so raise exception if not found.
+ head, part = self.id_dict[uri[4:]]
+ return StringIO.StringIO(part.getvalue())
+ if self.loc_dict.has_key(uri):
+ head, part = self.loc_dict[uri]
+ return StringIO.StringIO(part.getvalue())
+ return None
+
+ def Opaque(self, uri, tc, ps, **keywords):
+ content = self.get(uri)
+ if content: return content.getvalue()
+ if not self.next: raise EvaluateException("Unresolvable URI " + uri)
+ return self.next.Opaque(uri, tc, ps, **keywords)
+
+ def XML(self, uri, tc, ps, **keywords):
+ content = self.get(uri)
+ if content:
+ dom = ps.readerclass().fromStream(content)
+ return _child_elements(dom)[0]
+ if not self.next: raise EvaluateException("Unresolvable URI " + uri)
+ return self.next.XML(uri, tc, ps, **keywords)
+
+ def Resolve(self, uri, tc, ps, **keywords):
+ if isinstance(tc, TC.XML):
+ return self.XML(uri, tc, ps, **keywords)
+ return self.Opaque(uri, tc, ps, **keywords)
+
+ def __getitem__(self, cid):
+ head, body = self.id_dict[cid]
+ newio = StringIO.StringIO(body.getvalue())
+ return newio
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/schema.py b/ZSI/zsi/ZSI/schema.py
new file mode 100644
index 0000000..fa16dd9
--- /dev/null
+++ b/ZSI/zsi/ZSI/schema.py
@@ -0,0 +1,327 @@
+#! /usr/bin/env python
+# $Header$
+'''XML Schema support
+'''
+
+from ZSI import _copyright, _seqtypes, _find_type, EvaluateException
+from ZSI.wstools.Namespaces import SCHEMA, SOAP
+from ZSI.wstools.Utility import SplitQName
+
+
+def _get_type_definition(namespaceURI, name, **kw):
+ return SchemaInstanceType.getTypeDefinition(namespaceURI, name, **kw)
+
+def _get_global_element_declaration(namespaceURI, name, **kw):
+ return SchemaInstanceType.getElementDeclaration(namespaceURI, name, **kw)
+
+def _get_substitute_element(elt, what):
+ raise NotImplementedError, 'Not implemented'
+
+def _has_type_definition(namespaceURI, name):
+ return SchemaInstanceType.getTypeDefinition(namespaceURI, name) is not None
+
+
+#
+# functions for retrieving schema items from
+# the global schema instance.
+#
+GED = _get_global_element_declaration
+GTD = _get_type_definition
+
+
+def WrapImmutable(pyobj, what):
+ '''Wrap immutable instance so a typecode can be
+ set, making it self-describing ie. serializable.
+ '''
+ return _GetPyobjWrapper.WrapImmutable(pyobj, what)
+
+def RegisterBuiltin(arg):
+ '''Add a builtin to be registered, and register it
+ with the Any typecode.
+ '''
+ _GetPyobjWrapper.RegisterBuiltin(arg)
+ _GetPyobjWrapper.RegisterAnyElement()
+
+def RegisterAnyElement():
+ '''register all Wrapper classes with the Any typecode.
+ This allows instances returned by Any to be self-describing.
+ ie. serializable. AnyElement falls back on Any to parse
+ anything it doesn't understand.
+ '''
+ return _GetPyobjWrapper.RegisterAnyElement()
+
+
+class SchemaInstanceType(type):
+ '''Register all types/elements, when hit already defined
+ class dont create a new one just give back reference. Thus
+ import order determines which class is loaded.
+
+ class variables:
+ types -- dict of typecode classes definitions
+ representing global type definitions.
+ elements -- dict of typecode classes representing
+ global element declarations.
+ element_typecode_cache -- dict of typecode instances
+ representing global element declarations.
+ '''
+ types = {}
+ elements = {}
+ element_typecode_cache = {}
+
+ def __new__(cls,classname,bases,classdict):
+ '''If classdict has literal and schema register it as a
+ element declaration, else if has type and schema register
+ it as a type definition.
+ '''
+ if classname in ['ElementDeclaration', 'TypeDefinition', 'LocalElementDeclaration',]:
+ return type.__new__(cls,classname,bases,classdict)
+
+ if ElementDeclaration in bases:
+ if classdict.has_key('schema') is False or classdict.has_key('literal') is False:
+ raise AttributeError, 'ElementDeclaration must define schema and literal attributes'
+
+ key = (classdict['schema'],classdict['literal'])
+ if SchemaInstanceType.elements.has_key(key) is False:
+ SchemaInstanceType.elements[key] = type.__new__(cls,classname,bases,classdict)
+ return SchemaInstanceType.elements[key]
+
+ if TypeDefinition in bases:
+ if classdict.has_key('type') is None:
+ raise AttributeError, 'TypeDefinition must define type attribute'
+
+ key = classdict['type']
+ if SchemaInstanceType.types.has_key(key) is False:
+ SchemaInstanceType.types[key] = type.__new__(cls,classname,bases,classdict)
+ return SchemaInstanceType.types[key]
+
+ if LocalElementDeclaration in bases:
+ return type.__new__(cls,classname,bases,classdict)
+
+ raise TypeError, 'SchemaInstanceType must be an ElementDeclaration or TypeDefinition '
+
+ def getTypeDefinition(cls, namespaceURI, name, lazy=False):
+ '''Grab a type definition, returns a typecode class definition
+ because the facets (name, minOccurs, maxOccurs) must be provided.
+
+ Parameters:
+ namespaceURI --
+ name --
+ '''
+ klass = cls.types.get((namespaceURI, name), None)
+ if lazy and klass is not None:
+ return _Mirage(klass)
+ return klass
+ getTypeDefinition = classmethod(getTypeDefinition)
+
+ def getElementDeclaration(cls, namespaceURI, name, isref=False, lazy=False):
+ '''Grab an element declaration, returns a typecode instance
+ representation or a typecode class definition. An element
+ reference has its own facets, and is local so it will not be
+ cached.
+
+ Parameters:
+ namespaceURI --
+ name --
+ isref -- if element reference, return class definition.
+ '''
+ key = (namespaceURI, name)
+ if isref:
+ klass = cls.elements.get(key,None)
+ if klass is not None and lazy is True:
+ return _Mirage(klass)
+ return klass
+
+ typecode = cls.element_typecode_cache.get(key, None)
+ if typecode is None:
+ tcls = cls.elements.get(key,None)
+ if tcls is not None:
+ typecode = cls.element_typecode_cache[key] = tcls()
+ typecode.typed = False
+
+ return typecode
+ getElementDeclaration = classmethod(getElementDeclaration)
+
+
+class ElementDeclaration:
+ '''Typecodes subclass to represent a Global Element Declaration by
+ setting class variables schema and literal.
+
+ schema = namespaceURI
+ literal = NCName
+ '''
+ __metaclass__ = SchemaInstanceType
+
+
+class LocalElementDeclaration:
+ '''Typecodes subclass to represent a Local Element Declaration.
+ '''
+ __metaclass__ = SchemaInstanceType
+
+
+class TypeDefinition:
+ '''Typecodes subclass to represent a Global Type Definition by
+ setting class variable type.
+
+ type = (namespaceURI, NCName)
+ '''
+ __metaclass__ = SchemaInstanceType
+
+ def getSubstituteType(self, elt, ps):
+ '''if xsi:type does not match the instance type attr,
+ check to see if it is a derived type substitution.
+
+ DONT Return the element's type.
+
+ Parameters:
+ elt -- the DOM element being parsed
+ ps -- the ParsedSoap object.
+ '''
+ pyclass = SchemaInstanceType.getTypeDefinition(*self.type)
+ if pyclass is None:
+ raise EvaluateException(
+ 'No Type registed for xsi:type=(%s, %s)' %
+ (self.type[0], self.type[1]), ps.Backtrace(elt))
+
+ typeName = _find_type(elt)
+ prefix,typeName = SplitQName(typeName)
+ uri = ps.GetElementNSdict(elt).get(prefix)
+ subclass = SchemaInstanceType.getTypeDefinition(uri, typeName)
+ if subclass is None:
+ raise EvaluateException(
+ 'No registered xsi:type=(%s, %s), substitute for xsi:type=(%s, %s)' %
+ (uri, typeName, self.type[0], self.type[1]), ps.Backtrace(elt))
+
+ if not issubclass(subclass, pyclass) and subclass(None) and not issubclass(subclass, pyclass):
+ raise TypeError(
+ 'Substitute Type (%s, %s) is not derived from %s' %
+ (self.type[0], self.type[1], pyclass), ps.Backtrace(elt))
+
+ return subclass((self.nspname, self.pname))
+
+
+
+class _Mirage:
+ '''Used with SchemaInstanceType for lazy evaluation, eval during serialize or
+ parse as needed. Mirage is callable, TypeCodes are not. When called it returns the
+ typecode. Tightly coupled with generated code.
+
+ NOTE: **Must Use ClassType** for intended MRO of __call__ since setting it in
+ an instance attribute rather than a class attribute (will not work for object).
+ '''
+ def __init__(self, klass):
+ self.klass = klass
+ self.__reveal = False
+ self.__cache = None
+ if issubclass(klass, ElementDeclaration):
+ self.__call__ = self._hide_element
+
+ def __str__(self):
+ msg = "<Mirage id=%s, Local Element %s>"
+ if issubclass(self.klass, ElementDeclaration):
+ msg = "<Mirage id=%s, GED %s>"
+ return msg %(id(self), self.klass)
+
+ def _hide_type(self, pname, aname, minOccurs=0, maxOccurs=1, nillable=False,
+ **kw):
+ self.__call__ = self._reveal_type
+ self.__reveal = True
+
+ # store all attributes, make some visable for pyclass_type
+ self.__kw = kw
+ self.minOccurs,self.maxOccurs,self.nillable = minOccurs,maxOccurs,nillable
+ self.nspname,self.pname,self.aname = None,pname,aname
+ if type(self.pname) in (tuple,list):
+ self.nspname,self.pname = pname
+
+ return self
+
+ def _hide_element(self, minOccurs=0, maxOccurs=1, nillable=False, **kw):
+ self.__call__ = self._reveal_element
+ self.__reveal = True
+
+ # store all attributes, make some visable for pyclass_type
+ self.__kw = kw
+ self.nspname = self.klass.schema
+ self.pname = self.klass.literal
+ #TODO: Fix hack
+ #self.aname = '_%s' %self.pname
+ self.minOccurs,self.maxOccurs,self.nillable = minOccurs,maxOccurs,nillable
+
+ return self
+
+ def _reveal_type(self):
+ if self.__cache is None:
+ self.__cache = self.klass(pname=self.pname,
+ aname=self.aname, minOccurs=self.minOccurs,
+ maxOccurs=self.maxOccurs, nillable=self.nillable,
+ **self.__kw)
+ return self.__cache
+
+ def _reveal_element(self):
+ if self.__cache is None:
+ self.__cache = self.klass(minOccurs=self.minOccurs,
+ maxOccurs=self.maxOccurs, nillable=self.nillable,
+ **self.__kw)
+ return self.__cache
+
+ __call__ = _hide_type
+
+
+class _GetPyobjWrapper:
+ '''Get a python object that wraps data and typecode. Used by
+ <any> parse routine, so that typecode information discovered
+ during parsing is retained in the pyobj representation
+ and thus can be serialized.
+ '''
+ types_dict = {}
+
+ def RegisterBuiltin(cls, arg):
+ '''register a builtin, create a new wrapper.
+ '''
+ if arg in cls.types_dict:
+ raise RuntimeError, '%s already registered' %arg
+ class _Wrapper(arg):
+ 'Wrapper for builtin %s\n%s' %(arg, cls.__doc__)
+ _Wrapper.__name__ = '_%sWrapper' %arg.__name__
+ cls.types_dict[arg] = _Wrapper
+ RegisterBuiltin = classmethod(RegisterBuiltin)
+
+ def RegisterAnyElement(cls):
+ '''If find registered TypeCode instance, add Wrapper class
+ to TypeCode class serialmap and Re-RegisterType. Provides
+ Any serialzation of any instances of the Wrapper.
+ '''
+ for k,v in cls.types_dict.items():
+ what = Any.serialmap.get(k)
+ if what is None: continue
+ if v in what.__class__.seriallist: continue
+ what.__class__.seriallist.append(v)
+ RegisterType(what.__class__, clobber=1, **what.__dict__)
+ RegisterAnyElement = classmethod(RegisterAnyElement)
+
+ def WrapImmutable(cls, pyobj, what):
+ '''return a wrapper for pyobj, with typecode attribute set.
+ Parameters:
+ pyobj -- instance of builtin type (immutable)
+ what -- typecode describing the data
+ '''
+ d = cls.types_dict
+ if type(pyobj) is bool:
+ pyclass = d[int]
+ elif d.has_key(type(pyobj)) is True:
+ pyclass = d[type(pyobj)]
+ else:
+ raise TypeError,\
+ 'Expecting a built-in type in %s (got %s).' %(
+ d.keys(),type(pyobj))
+
+ newobj = pyclass(pyobj)
+ newobj.typecode = what
+ return newobj
+ WrapImmutable = classmethod(WrapImmutable)
+
+
+from TC import Any, RegisterType
+
+if __name__ == '__main__': print _copyright
+
diff --git a/ZSI/zsi/ZSI/twisted/WSresource.py b/ZSI/zsi/ZSI/twisted/WSresource.py
new file mode 100644
index 0000000..2adf67b
--- /dev/null
+++ b/ZSI/zsi/ZSI/twisted/WSresource.py
@@ -0,0 +1,355 @@
+###########################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+# $Id: WSresource.py 1172 2006-04-04 05:01:27Z boverhof $
+###########################################################################
+
+import sys, warnings
+
+# twisted & related imports
+from zope.interface import classProvides, implements, Interface
+from twisted.python import log, failure
+from twisted.web.error import NoResource
+from twisted.web.server import NOT_DONE_YET
+import twisted.web.http
+import twisted.web.resource
+
+# ZSI imports
+from ZSI import _get_element_nsuri_name, EvaluateException, ParseException
+from ZSI.parse import ParsedSoap
+from ZSI.writer import SoapWriter
+from ZSI import fault
+
+# WS-Address related imports
+from ZSI.address import Address
+from ZSI.ServiceContainer import WSActionException
+
+
+#
+# Stability: Unstable
+#
+
+class HandlerChainInterface(Interface):
+
+ def processRequest(self, input, **kw):
+ """returns a representation of the request, the
+ last link in the chain must return a response
+ pyobj with a typecode attribute.
+ Parameters:
+ input --
+ Keyword Parameters:
+ request -- HTTPRequest instance
+ resource -- Resource instance
+ """
+ def processResponse(self, output, **kw):
+ """returns a string representing the soap response.
+ Parameters
+ output --
+ Keyword Parameters:
+ request -- HTTPRequest instance
+ resource -- Resource instance
+ """
+
+class CallbackChainInterface(Interface):
+
+ def processRequest(self, input, **kw):
+ """returns a response pyobj with a typecode
+ attribute.
+ Parameters:
+ input --
+ Keyword Parameters:
+ request -- HTTPRequest instance
+ resource -- Resource instance
+ """
+
+class DataHandler:
+ """
+ class variables:
+ readerClass -- factory class to create reader for ParsedSoap instances.
+ writerClass -- ElementProxy implementation to use for SoapWriter instances.
+ """
+ classProvides(HandlerChainInterface)
+ readerClass = None
+ writerClass = None
+
+ @classmethod
+ def processRequest(cls, input, **kw):
+ return ParsedSoap(input, readerclass=cls.readerClass)
+
+ @classmethod
+ def processResponse(cls, output, **kw):
+ sw = SoapWriter(outputclass=cls.writerClass)
+ sw.serialize(output)
+ return sw
+
+
+class DefaultCallbackHandler:
+ classProvides(CallbackChainInterface)
+
+ @classmethod
+ def processRequest(cls, ps, **kw):
+ """invokes callback that should return a (request,response) tuple.
+ representing the SOAP request and response respectively.
+ ps -- ParsedSoap instance representing HTTP Body.
+ request -- twisted.web.server.Request
+ """
+ resource = kw['resource']
+ request = kw['request']
+ method = getattr(resource, 'soap_%s' %
+ _get_element_nsuri_name(ps.body_root)[-1])
+
+ try:
+ req_pyobj,rsp_pyobj = method(ps, request=request)
+ except TypeError, ex:
+ log.err(
+ 'ERROR: service %s is broken, method MUST return request, response'\
+ % cls.__name__
+ )
+ raise
+ except Exception, ex:
+ log.err('failure when calling bound method')
+ raise
+
+ return rsp_pyobj
+
+
+class WSAddressHandler:
+ """General WS-Address handler. This implementation depends on a
+ 'wsAction' dictionary in the service stub which contains keys to
+ WS-Action values.
+
+ Implementation saves state on request response flow, so using this
+ handle is not reliable if execution is deferred between proceesRequest
+ and processResponse.
+
+ TODO: sink this up with wsdl2dispatch
+ TODO: reduce coupling with WSAddressCallbackHandler.
+ """
+ implements(HandlerChainInterface)
+
+ def processRequest(self, ps, **kw):
+ # TODO: Clean this up
+ resource = kw['resource']
+
+ d = getattr(resource, 'root', None)
+ key = _get_element_nsuri_name(ps.body_root)
+ if d is None or d.has_key(key) is False:
+ raise RuntimeError,\
+ 'Error looking for key(%s) in root dictionary(%s)' %(key, str(d))
+
+ self.op_name = d[key]
+ self.address = address = Address()
+ address.parse(ps)
+ action = address.getAction()
+ if not action:
+ raise WSActionException('No WS-Action specified in Request')
+
+ request = kw['request']
+ http_headers = request.getAllHeaders()
+ soap_action = http_headers.get('soapaction')
+ if soap_action and soap_action.strip('\'"') != action:
+ raise WSActionException(\
+ 'SOAP Action("%s") must match WS-Action("%s") if specified.'\
+ %(soap_action,action)
+ )
+
+ # Save WS-Address in ParsedSoap instance.
+ ps.address = address
+ return ps
+
+ def processResponse(self, sw, **kw):
+ if sw is None:
+ self.address = None
+ return
+
+ request, resource = kw['request'], kw['resource']
+ if isinstance(request, twisted.web.http.Request) is False:
+ raise TypeError, '%s instance expected' %http.Request
+
+ d = getattr(resource, 'wsAction', None)
+ key = self.op_name
+ if d is None or d.has_key(key) is False:
+ raise WSActionNotSpecified,\
+ 'Error looking for key(%s) in wsAction dictionary(%s)' %(key, str(d))
+
+ addressRsp = Address(action=d[key])
+ if request.transport.TLS == 0:
+ addressRsp.setResponseFromWSAddress(\
+ self.address, 'http://%s:%d%s' %(
+ request.host.host, request.host.port, request.path)
+ )
+ else:
+ addressRsp.setResponseFromWSAddress(\
+ self.address, 'https://%s:%d%s' %(
+ request.host.host, request.host.port, request.path)
+ )
+
+ addressRsp.serialize(sw, typed=False)
+ self.address = None
+ return sw
+
+
+class WSAddressCallbackHandler:
+ classProvides(CallbackChainInterface)
+
+ @classmethod
+ def processRequest(cls, ps, **kw):
+ """invokes callback that should return a (request,response) tuple.
+ representing the SOAP request and response respectively.
+ ps -- ParsedSoap instance representing HTTP Body.
+ request -- twisted.web.server.Request
+ """
+ resource = kw['resource']
+ request = kw['request']
+ method = getattr(resource, 'wsa_%s' %
+ _get_element_nsuri_name(ps.body_root)[-1])
+
+ # TODO: grab ps.address, clean this up.
+ try:
+ req_pyobj,rsp_pyobj = method(ps, ps.address, request=request)
+ except TypeError, ex:
+ log.err(
+ 'ERROR: service %s is broken, method MUST return request, response'\
+ %self.__class__.__name__
+ )
+ raise
+ except Exception, ex:
+ log.err('failure when calling bound method')
+ raise
+
+ return rsp_pyobj
+
+
+def CheckInputArgs(*interfaces):
+ """Must provide at least one interface, the last one may be repeated.
+ """
+ l = len(interfaces)
+ def wrapper(func):
+ def check_args(self, *args, **kw):
+ for i in range(len(args)):
+ if (l > i and interfaces[i].providedBy(args[i])) or interfaces[-1].providedBy(args[i]):
+ continue
+ if l > i: raise TypeError, 'arg %s does not implement %s' %(args[i], interfaces[i])
+ raise TypeError, 'arg %s does not implement %s' %(args[i], interfaces[-1])
+ func(self, *args, **kw)
+ return check_args
+ return wrapper
+
+
+class DefaultHandlerChain:
+
+ @CheckInputArgs(CallbackChainInterface, HandlerChainInterface)
+ def __init__(self, cb, *handlers):
+ self.handlercb = cb
+ self.handlers = handlers
+ self.debug = len(log.theLogPublisher.observers) > 0
+
+ def processRequest(self, arg, **kw):
+ if self.debug:
+ log.msg('--->PROCESS REQUEST\n%s' %arg, debug=1)
+
+ for h in self.handlers:
+ arg = h.processRequest(arg, **kw)
+
+ return self.handlercb.processRequest(arg, **kw)
+
+ def processResponse(self, arg, **kw):
+ if self.debug:
+ log.msg('===>PROCESS RESPONSE: %s' %str(arg), debug=1)
+
+ if arg is None:
+ return
+
+ for h in self.handlers:
+ arg = h.processResponse(arg, **kw)
+
+ s = str(arg)
+ if self.debug:
+ log.msg(s, debug=1)
+
+ return s
+
+
+class DefaultHandlerChainFactory:
+ protocol = DefaultHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+ return cls.protocol(DefaultCallbackHandler, DataHandler)
+
+
+class WSAddressHandlerChainFactory:
+ protocol = DefaultHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+ return cls.protocol(WSAddressCallbackHandler, DataHandler,
+ WSAddressHandler())
+
+
+class WSResource(twisted.web.resource.Resource, object):
+ """
+ class variables:
+ encoding --
+ factory -- hander chain, which has a factory method "newInstance"
+ that returns a
+ """
+ encoding = "UTF-8"
+ factory = DefaultHandlerChainFactory
+
+ def __init__(self):
+ """
+ """
+ twisted.web.resource.Resource.__init__(self)
+
+ def _writeResponse(self, request, response, status=200):
+ """
+ request -- request message
+ response --- response message
+ status -- HTTP Status
+ """
+ request.setResponseCode(status)
+ if self.encoding is not None:
+ mimeType = 'text/xml; charset="%s"' % self.encoding
+ else:
+ mimeType = "text/xml"
+
+ request.setHeader("Content-type", mimeType)
+ request.setHeader("Content-length", str(len(response)))
+ request.write(response)
+ request.finish()
+ return NOT_DONE_YET
+
+ def _writeFault(self, request, ex):
+ """
+ request -- request message
+ ex -- Exception
+ """
+ response = None
+ response = fault.FaultFromException(ex, False, sys.exc_info()[2]).AsSOAP()
+ log.err('SOAP FAULT: %s' % response)
+ return self._writeResponse(request, response, status=500)
+
+ def render_POST(self, request):
+ """Dispatch Method called by twisted render, creates a
+ request/response handler chain.
+ request -- twisted.web.server.Request
+ """
+ chain = self.factory.newInstance()
+ data = request.content.read()
+ try:
+ pyobj = chain.processRequest(data, request=request, resource=self)
+ except Exception, ex:
+ return self._writeFault(request, ex)
+
+ try:
+ soap = chain.processResponse(pyobj, request=request, resource=self)
+ except Exception, ex:
+ return self._writeFault(request, ex)
+
+ if soap is not None:
+ return self._writeResponse(request, soap)
+
+ request.finish()
+ return NOT_DONE_YET
+
diff --git a/ZSI/zsi/ZSI/twisted/WSsecurity.py b/ZSI/zsi/ZSI/twisted/WSsecurity.py
new file mode 100644
index 0000000..b082efe
--- /dev/null
+++ b/ZSI/zsi/ZSI/twisted/WSsecurity.py
@@ -0,0 +1,389 @@
+###########################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+# $Id: WSsecurity.py 1134 2006-02-24 00:23:06Z boverhof $
+###########################################################################
+
+import sys, time, warnings
+import sha, base64
+
+# twisted & related imports
+from zope.interface import classProvides, implements, Interface
+from twisted.python import log, failure
+from twisted.web.error import NoResource
+from twisted.web.server import NOT_DONE_YET
+from twisted.internet import reactor
+import twisted.web.http
+import twisted.web.resource
+
+# ZSI imports
+from ZSI import _get_element_nsuri_name, EvaluateException, ParseException
+from ZSI.parse import ParsedSoap
+from ZSI.writer import SoapWriter
+from ZSI.TC import _get_global_element_declaration as GED
+from ZSI import fault
+from ZSI.wstools.Namespaces import OASIS, DSIG
+from WSresource import DefaultHandlerChain, HandlerChainInterface,\
+ WSAddressCallbackHandler, DataHandler, WSAddressHandler
+
+
+#
+# Global Element Declarations
+#
+UsernameTokenDec = GED(OASIS.WSSE, "UsernameToken")
+SecurityDec = GED(OASIS.WSSE, "Security")
+SignatureDec = GED(DSIG.BASE, "Signature")
+PasswordDec = GED(OASIS.WSSE, "Password")
+NonceDec = GED(OASIS.WSSE, "Nonce")
+CreatedDec = GED(OASIS.UTILITY, "Created")
+
+if None in [UsernameTokenDec,SecurityDec,SignatureDec,PasswordDec,NonceDec,CreatedDec]:
+ raise ImportError, 'required global element(s) unavailable: %s ' %({
+ (OASIS.WSSE, "UsernameToken"):UsernameTokenDec,
+ (OASIS.WSSE, "Security"):SecurityDec,
+ (DSIG.BASE, "Signature"):SignatureDec,
+ (OASIS.WSSE, "Password"):PasswordDec,
+ (OASIS.WSSE, "Nonce"):NonceDec,
+ (OASIS.UTILITY, "Created"):CreatedDec,
+ })
+
+
+#
+# Stability: Unstable, Untested, Not Finished.
+#
+
+class WSSecurityHandler:
+ """Web Services Security: SOAP Message Security 1.0
+
+ Class Variables:
+ debug -- If True provide more detailed SOAP:Fault information to clients.
+ """
+ classProvides(HandlerChainInterface)
+ debug = True
+
+ @classmethod
+ def processRequest(cls, ps, **kw):
+ if type(ps) is not ParsedSoap:
+ raise TypeError,'Expecting ParsedSoap instance'
+
+ security = ps.ParseHeaderElements([cls.securityDec])
+
+ # Assume all security headers are supposed to be processed here.
+ for pyobj in security or []:
+ for any in pyobj.Any or []:
+
+ if any.typecode is UsernameTokenDec:
+ try:
+ ps = cls.UsernameTokenProfileHandler.processRequest(ps, any)
+ except Exception, ex:
+ if cls.debug: raise
+ raise RuntimeError, 'Unauthorized Username/passphrase combination'
+ continue
+
+ if any.typecode is SignatureDec:
+ try:
+ ps = cls.SignatureHandler.processRequest(ps, any)
+ except Exception, ex:
+ if cls.debug: raise
+ raise RuntimeError, 'Invalid Security Header'
+ continue
+
+ raise RuntimeError, 'WS-Security, Unsupported token %s' %str(any)
+
+ return ps
+
+ @classmethod
+ def processResponse(cls, output, **kw):
+ return output
+
+
+ class UsernameTokenProfileHandler:
+ """Web Services Security UsernameToken Profile 1.0
+
+ Class Variables:
+ targetNamespace --
+ """
+ classProvides(HandlerChainInterface)
+
+ # Class Variables
+ targetNamespace = OASIS.WSSE
+ sweepInterval = 60*5
+ nonces = None
+
+ # Set to None to disable
+ PasswordText = targetNamespace + "#PasswordText"
+ PasswordDigest = targetNamespace + "#PasswordDigest"
+
+ # Override passwordCallback
+ passwordCallback = lambda cls,username: None
+
+ @classmethod
+ def sweep(cls, index):
+ """remove nonces every sweepInterval.
+ Parameters:
+ index -- remove all nonces up to this index.
+ """
+ if cls.nonces is None:
+ cls.nonces = []
+
+ seconds = cls.sweepInterval
+ cls.nonces = cls.nonces[index:]
+ reactor.callLater(seconds, cls.sweep, len(cls.nonces))
+
+ @classmethod
+ def processRequest(cls, ps, token, **kw):
+ """
+ Parameters:
+ ps -- ParsedSoap instance
+ token -- UsernameToken pyclass instance
+ """
+ if token.typecode is not UsernameTokenDec:
+ raise TypeError, 'expecting GED (%s,%s) representation.' %(
+ UsernameTokenDec.nspname, UsernameTokenDec.pname)
+
+ username = token.Username
+
+ # expecting only one password
+ # may have a nonce and a created
+ password = nonce = timestamp = None
+ for any in token.Any or []:
+ if any.typecode is PasswordDec:
+ password = any
+ continue
+
+ if any.typecode is NonceTypeDec:
+ nonce = any
+ continue
+
+ if any.typecode is CreatedTypeDec:
+ timestamp = any
+ continue
+
+ raise TypeError, 'UsernameTokenProfileHander unexpected %s' %str(any)
+
+ if password is None:
+ raise RuntimeError, 'Unauthorized, no password'
+
+ # TODO: not yet supporting complexType simpleContent in pyclass_type
+ attrs = getattr(password, password.typecode.attrs_aname, {})
+ pwtype = attrs.get('Type', cls.PasswordText)
+
+ # Clear Text Passwords
+ if cls.PasswordText is not None and pwtype == cls.PasswordText:
+ if password == cls.passwordCallback(username):
+ return ps
+
+ raise RuntimeError, 'Unauthorized, clear text password failed'
+
+ if cls.nonces is None: cls.sweep(0)
+ if nonce is not None:
+ if nonce in cls.nonces:
+ raise RuntimeError, 'Invalid Nonce'
+
+ # created was 10 seconds ago or sooner
+ if created is not None and created < time.gmtime(time.time()-10):
+ raise RuntimeError, 'UsernameToken created is expired'
+
+ cls.nonces.append(nonce)
+
+ # PasswordDigest, recommended that implemenations
+ # require a Nonce and Created
+ if cls.PasswordDigest is not None and pwtype == cls.PasswordDigest:
+ digest = sha.sha()
+ for i in (nonce, created, cls.passwordCallback(username)):
+ if i is None: continue
+ digest.update(i)
+
+ if password == base64.encodestring(digest.digest()).strip():
+ return ps
+
+ raise RuntimeError, 'Unauthorized, digest failed'
+
+ raise RuntimeError, 'Unauthorized, contents of UsernameToken unknown'
+
+ @classmethod
+ def processResponse(cls, output, **kw):
+ return output
+
+ @staticmethod
+ def hmac_sha1(xml):
+ return
+
+ class SignatureHandler:
+ """Web Services Security UsernameToken Profile 1.0
+ """
+ digestMethods = {
+ DSIG.BASE+"#sha1":sha.sha,
+ }
+ signingMethods = {
+ DSIG.BASE+"#hmac-sha1":hmac_sha1,
+ }
+ canonicalizationMethods = {
+ DSIG.C14N_EXCL:lambda node: Canonicalize(node, unsuppressedPrefixes=[]),
+ DSIG.C14N:lambda node: Canonicalize(node),
+ }
+
+ @classmethod
+ def processRequest(cls, ps, signature, **kw):
+ """
+ Parameters:
+ ps -- ParsedSoap instance
+ signature -- Signature pyclass instance
+ """
+ if token.typecode is not SignatureDec:
+ raise TypeError, 'expecting GED (%s,%s) representation.' %(
+ SignatureDec.nspname, SignatureDec.pname)
+
+ si = signature.SignedInfo
+ si.CanonicalizationMethod
+ calgo = si.CanonicalizationMethod.get_attribute_Algorithm()
+ for any in si.CanonicalizationMethod.Any:
+ pass
+
+ # Check Digest
+ si.Reference
+ context = XPath.Context.Context(ps.dom, processContents={'wsu':OASIS.UTILITY})
+ exp = XPath.Compile('//*[@wsu:Id="%s"]' %si.Reference.get_attribute_URI())
+ nodes = exp.evaluate(context)
+ if len(nodes) != 1:
+ raise RuntimeError, 'A SignedInfo Reference must refer to one node %s.' %(
+ si.Reference.get_attribute_URI())
+
+ try:
+ xml = cls.canonicalizeMethods[calgo](nodes[0])
+ except IndexError:
+ raise RuntimeError, 'Unsupported canonicalization algorithm'
+
+ try:
+ digest = cls.digestMethods[salgo]
+ except IndexError:
+ raise RuntimeError, 'unknown digestMethods Algorithm'
+
+ digestValue = base64.encodestring(digest(xml).digest()).strip()
+ if si.Reference.DigestValue != digestValue:
+ raise RuntimeError, 'digest does not match'
+
+ if si.Reference.Transforms:
+ pass
+
+ signature.KeyInfo
+ signature.KeyInfo.KeyName
+ signature.KeyInfo.KeyValue
+ signature.KeyInfo.RetrievalMethod
+ signature.KeyInfo.X509Data
+ signature.KeyInfo.PGPData
+ signature.KeyInfo.SPKIData
+ signature.KeyInfo.MgmtData
+ signature.KeyInfo.Any
+
+ signature.Object
+
+ # TODO: Check Signature
+ signature.SignatureValue
+ si.SignatureMethod
+ salgo = si.SignatureMethod.get_attribute_Algorithm()
+ if si.SignatureMethod.HMACOutputLength:
+ pass
+ for any in si.SignatureMethod.Any:
+ pass
+
+ # <SignedInfo><Reference URI="">
+ exp = XPath.Compile('//child::*[attribute::URI = "%s"]/..' %(
+ si.Reference.get_attribute_URI()))
+ nodes = exp.evaluate(context)
+ if len(nodes) != 1:
+ raise RuntimeError, 'A SignedInfo Reference must refer to one node %s.' %(
+ si.Reference.get_attribute_URI())
+
+ try:
+ xml = cls.canonicalizeMethods[calgo](nodes[0])
+ except IndexError:
+ raise RuntimeError, 'Unsupported canonicalization algorithm'
+
+ # TODO: Check SignatureValue
+
+ @classmethod
+ def processResponse(cls, output, **kw):
+ return output
+
+
+ class X509TokenProfileHandler:
+ """Web Services Security UsernameToken Profile 1.0
+ """
+ targetNamespace = DSIG.BASE
+
+ # Token Types
+ singleCertificate = targetNamespace + "#X509v3"
+ certificatePath = targetNamespace + "#X509PKIPathv1"
+ setCerticatesCRLs = targetNamespace + "#PKCS7"
+
+ @classmethod
+ def processRequest(cls, ps, signature, **kw):
+ return ps
+
+
+
+"""
+<element name="KeyInfo" type="ds:KeyInfoType"/>
+<complexType name="KeyInfoType" mixed="true">
+ <choice maxOccurs="unbounded">
+ <element ref="ds:KeyName"/>
+ <element ref="ds:KeyValue"/>
+ <element ref="ds:RetrievalMethod"/>
+ <element ref="ds:X509Data"/>
+ <element ref="ds:PGPData"/>
+ <element ref="ds:SPKIData"/>
+ <element ref="ds:MgmtData"/>
+ <any processContents="lax" namespace="##other"/>
+ <!-- (1,1) elements from (0,unbounded) namespaces -->
+ </choice>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+
+
+<element name="Signature" type="ds:SignatureType"/>
+<complexType name="SignatureType">
+ <sequence>
+ <element ref="ds:SignedInfo"/>
+ <element ref="ds:SignatureValue"/>
+ <element ref="ds:KeyInfo" minOccurs="0"/>
+ <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+ <element name="SignatureValue" type="ds:SignatureValueType"/>
+ <complexType name="SignatureValueType">
+ <simpleContent>
+ <extension base="base64Binary">
+ <attribute name="Id" type="ID" use="optional"/>
+ </extension>
+ </simpleContent>
+ </complexType>
+
+<!-- Start SignedInfo -->
+
+<element name="SignedInfo" type="ds:SignedInfoType"/>
+<complexType name="SignedInfoType">
+ <sequence>
+ <element ref="ds:CanonicalizationMethod"/>
+ <element ref="ds:SignatureMethod"/>
+ <element ref="ds:Reference" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+"""
+
+
+class WSSecurityHandlerChainFactory:
+ protocol = DefaultHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+
+ return cls.protocol(WSAddressCallbackHandler, DataHandler,
+ WSSecurityHandler, WSAddressHandler())
+
+
+
diff --git a/ZSI/zsi/ZSI/twisted/__init__.py b/ZSI/zsi/ZSI/twisted/__init__.py
new file mode 100644
index 0000000..f9b565d
--- /dev/null
+++ b/ZSI/zsi/ZSI/twisted/__init__.py
@@ -0,0 +1,7 @@
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+# $Id: __init__.py 1132 2006-02-17 01:55:41Z boverhof $
+###########################################################################
+
+__all__=['WSresource', 'WSsecurity']
diff --git a/ZSI/zsi/ZSI/twisted/client.py b/ZSI/zsi/ZSI/twisted/client.py
new file mode 100644
index 0000000..7813e1f
--- /dev/null
+++ b/ZSI/zsi/ZSI/twisted/client.py
@@ -0,0 +1,344 @@
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import time
+
+# twisted & related imports
+from zope.interface import classProvides, implements, Interface
+from twisted.web import client
+from twisted.internet import defer
+from twisted.internet import reactor
+from twisted.python import log
+from twisted.python.failure import Failure
+
+from ZSI.parse import ParsedSoap
+from ZSI.writer import SoapWriter
+from ZSI.fault import FaultFromFaultMessage
+from ZSI.wstools.Namespaces import WSA
+
+from WSresource import HandlerChainInterface, CheckInputArgs
+
+
+#
+# Stability: Unstable
+#
+
+class HTTPPageGetter(client.HTTPPageGetter):
+ def handleStatus_500(self):
+ """potentially a SOAP:Fault.
+ """
+ log.err('HTTP Error 500')
+ def handleStatus_404(self):
+ """client error, not found
+ """
+ log.err('HTTP Error 404')
+
+
+client.HTTPClientFactory.protocol = HTTPPageGetter
+
+
+def getPage(url, contextFactory=None, *args, **kwargs):
+ """Download a web page as a string.
+
+ Download a page. Return a deferred, which will callback with a
+ page (as a string) or errback with a description of the error.
+
+ See HTTPClientFactory to see what extra args can be passed.
+ """
+ scheme, host, port, path = client._parse(url)
+ factory = client.HTTPClientFactory(url, *args, **kwargs)
+ if scheme == 'https':
+ if contextFactory is None:
+ raise RuntimeError, 'must provide a contextFactory'
+ conn = reactor.connectSSL(host, port, factory, contextFactory)
+ else:
+ conn = reactor.connectTCP(host, port, factory)
+
+ return factory
+
+
+class ClientDataHandler:
+ """
+ class variables:
+ readerClass -- factory class to create reader for ParsedSoap instances.
+ writerClass -- ElementProxy implementation to use for SoapWriter
+ instances.
+ """
+ classProvides(HandlerChainInterface)
+ readerClass = None
+ writerClass = None
+
+ @classmethod
+ def processResponse(cls, soapdata, **kw):
+ """called by deferred, returns pyobj representing reply.
+ Parameters and Key Words:
+ soapdata -- SOAP Data
+ replytype -- reply type of response
+ """
+ if len(soapdata) == 0:
+ raise TypeError('Received empty response')
+
+# log.msg("_" * 33, time.ctime(time.time()),
+# "RESPONSE: \n%s" %soapdata, debug=True)
+
+ ps = ParsedSoap(soapdata, readerclass=cls.readerClass)
+ if ps.IsAFault() is True:
+ log.msg('Received SOAP:Fault', debug=True)
+ raise FaultFromFaultMessage(ps)
+
+ return ps
+
+ @classmethod
+ def processRequest(cls, obj, nsdict={}, header=True,
+ **kw):
+ tc = None
+ if kw.has_key('requesttypecode'):
+ tc = kw['requesttypecode']
+ elif kw.has_key('requestclass'):
+ tc = kw['requestclass'].typecode
+ else:
+ tc = getattr(obj.__class__, 'typecode', None)
+
+ sw = SoapWriter(nsdict=nsdict, header=header,
+ outputclass=cls.writerClass)
+ sw.serialize(obj, tc)
+ return sw
+
+
+class WSAddressHandler:
+ """Minimal WS-Address handler. Most of the logic is in
+ the ZSI.address.Address class.
+
+ class variables:
+ uri -- default WSA Addressing URI
+ """
+ implements(HandlerChainInterface)
+ uri = WSA.ADDRESS
+
+ def processResponse(self, ps, wsaction=None, soapaction=None, **kw):
+ addr = self.address
+ addr.parse(ps)
+ action = addr.getAction()
+ if not action:
+ raise WSActionException('No WS-Action specified in Request')
+
+ if not soapaction:
+ return ps
+
+ soapaction = soapaction.strip('\'"')
+ if soapaction and soapaction != wsaction:
+ raise WSActionException(\
+ 'SOAP Action("%s") must match WS-Action("%s") if specified.'%(
+ soapaction, wsaction)
+ )
+
+ return ps
+
+ def processRequest(self, sw, wsaction=None, url=None, endPointReference=None, **kw):
+ from ZSI.address import Address
+ if sw is None:
+ self.address = None
+ return
+
+ if not sw.header:
+ raise RuntimeError, 'expecting SOAP:Header'
+
+ self.address = addr = Address(url, wsAddressURI=self.uri)
+ addr.setRequest(endPointReference, wsaction)
+ addr.serialize(sw, typed=False)
+
+ return sw
+
+
+class DefaultClientHandlerChain:
+
+ @CheckInputArgs(HandlerChainInterface)
+ def __init__(self, *handlers):
+ self.handlers = handlers
+ self.debug = len(log.theLogPublisher.observers) > 0
+ self.flow = None
+
+ @staticmethod
+ def parseResponse(ps, replytype):
+ return ps.Parse(replytype)
+
+ def processResponse(self, arg, replytype, **kw):
+ """
+ Parameters:
+ arg -- deferred
+ replytype -- typecode
+ """
+ if self.debug:
+ log.msg('--->PROCESS REQUEST\n%s' %arg, debug=1)
+
+ for h in self.handlers:
+ arg.addCallback(h.processResponse, **kw)
+
+ arg.addCallback(self.parseResponse, replytype)
+
+ def processRequest(self, arg, **kw):
+ """
+ Parameters:
+ arg -- XML Soap data string
+ """
+ if self.debug:
+ log.msg('===>PROCESS RESPONSE: %s' %str(arg), debug=1)
+
+ if arg is None:
+ return
+
+ for h in self.handlers:
+ arg = h.processRequest(arg, **kw)
+
+ s = str(arg)
+ if self.debug:
+ log.msg(s, debug=1)
+
+ return s
+
+
+class DefaultClientHandlerChainFactory:
+ protocol = DefaultClientHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+ return cls.protocol(ClientDataHandler)
+
+
+class WSAddressClientHandlerChainFactory:
+ protocol = DefaultClientHandlerChain
+
+ @classmethod
+ def newInstance(cls):
+ return cls.protocol(ClientDataHandler,
+ WSAddressHandler())
+
+
+class Binding:
+ """Object that represents a binding (connection) to a SOAP server.
+ """
+ agent='ZSI.twisted client'
+ factory = DefaultClientHandlerChainFactory
+ defer = False
+
+ def __init__(self, url=None, nsdict=None, contextFactory=None,
+ tracefile=None, **kw):
+ """Initialize.
+ Keyword arguments include:
+ url -- URL of resource, POST is path
+ nsdict -- namespace entries to add
+ contextFactory -- security contexts
+ tracefile -- file to dump packet traces
+ """
+ self.url = url
+ self.nsdict = nsdict or {}
+ self.contextFactory = contextFactory
+ self.http_headers = {'content-type': 'text/xml',}
+ self.trace = tracefile
+
+ def addHTTPHeader(self, key, value):
+ self.http_headers[key] = value
+
+ def getHTTPHeaders(self):
+ return self.http_headers
+
+ def Send(self, url, opname, pyobj, nsdict={}, soapaction=None, chain=None,
+ **kw):
+ """Returns a ProcessingChain which needs to be passed to Receive if
+ Send is being called consecutively.
+ """
+ url = url or self.url
+ cookies = None
+ if chain is not None:
+ cookies = chain.flow.cookies
+
+ d = {}
+ d.update(self.nsdict)
+ d.update(nsdict)
+
+ if soapaction is not None:
+ self.addHTTPHeader('SOAPAction', soapaction)
+
+ chain = self.factory.newInstance()
+ soapdata = chain.processRequest(pyobj, nsdict=nsdict,
+ soapaction=soapaction, **kw)
+
+ if self.trace:
+ print >>self.trace, "_" * 33, time.ctime(time.time()), "REQUEST:"
+ print >>self.trace, soapdata
+
+ f = getPage(str(url), contextFactory=self.contextFactory,
+ postdata=soapdata, agent=self.agent,
+ method='POST', headers=self.getHTTPHeaders(),
+ cookies=cookies)
+
+ if isinstance(f, Failure):
+ return f
+
+ chain.flow = f
+ self.chain = chain
+ return chain
+
+ def Receive(self, replytype, chain=None, **kw):
+ """This method allows code to act in a synchronous manner, it waits to
+ return until the deferred fires but it doesn't prevent other queued
+ calls from being executed. Send must be called first, which sets up
+ the chain/factory.
+
+ WARNING: If defer is set to True, must either call Receive
+ immediately after Send (ie. no intervening Sends) or pass
+ chain in as a paramter.
+
+ Parameters:
+ replytype -- TypeCode
+ KeyWord Parameters:
+ chain -- processing chain, optional
+
+ """
+ chain = chain or self.chain
+ d = chain.flow.deferred
+ if self.trace:
+ def trace(soapdata):
+ print >>self.trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
+ print >>self.trace, soapdata
+ return soapdata
+
+ d.addCallback(trace)
+
+ chain.processResponse(d, replytype, **kw)
+ if self.defer:
+ return d
+
+ failure = []
+ append = failure.append
+ def errback(result):
+ """Used with Response method to suppress 'Unhandled error in
+ Deferred' messages by adding an errback.
+ """
+ append(result)
+ return None
+
+ d.addErrback(errback)
+
+ # spin reactor
+ while not d.called:
+ reactor.runUntilCurrent()
+ t2 = reactor.timeout()
+ t = reactor.running and t2
+ reactor.doIteration(t)
+
+ pyobj = d.result
+ if len(failure):
+ failure[0].raiseException()
+
+ return pyobj
+
+def trace():
+ if trace:
+ print >>trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
+ for i in (self.reply_code, self.reply_msg,):
+ print >>trace, str(i)
+ print >>trace, "-------"
+ print >>trace, str(self.reply_headers)
+ print >>trace, self.data
diff --git a/ZSI/zsi/ZSI/typeinterpreter.py b/ZSI/zsi/ZSI/typeinterpreter.py
new file mode 100644
index 0000000..8fe47c5
--- /dev/null
+++ b/ZSI/zsi/ZSI/typeinterpreter.py
@@ -0,0 +1,133 @@
+###########################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+import ZSI
+from ZSI import TC, TCtimes, TCcompound
+from ZSI.TC import TypeCode
+from ZSI import _copyright, EvaluateException
+from ZSI.wstools.Utility import SplitQName
+from ZSI.wstools.Namespaces import SOAP, SCHEMA
+
+###########################################################################
+# Module Classes: BaseTypeInterpreter
+###########################################################################
+
+class NamespaceException(Exception): pass
+class BaseTypeInterpreter:
+ """Example mapping of xsd/soapenc types to zsi python types.
+ Checks against all available classes in ZSI.TC. Used in
+ wsdl2python, wsdlInterpreter, and ServiceProxy.
+ """
+
+ def __init__(self):
+ self._type_list = [TC.Iinteger, TC.IunsignedShort, TC.gYearMonth, \
+ TC.InonNegativeInteger, TC.Iint, TC.String, \
+ TC.gDateTime, TC.IunsignedInt, TC.Duration,\
+ TC.IpositiveInteger, TC.FPfloat, TC.gDay, TC.gMonth, \
+ TC.InegativeInteger, TC.gDate, TC.URI, \
+ TC.HexBinaryString, TC.IunsignedByte, \
+ TC.gMonthDay, TC.InonPositiveInteger, \
+ TC.Ibyte, TC.FPdouble, TC.gTime, TC.gYear, \
+ TC.Ilong, TC.IunsignedLong, TC.Ishort, \
+ TC.Token, TC.QName]
+
+ self._tc_to_int = [
+ ZSI.TCnumbers.IEnumeration,
+ ZSI.TCnumbers.Iint,
+ ZSI.TCnumbers.Iinteger,
+ ZSI.TCnumbers.Ilong,
+ ZSI.TCnumbers.InegativeInteger,
+ ZSI.TCnumbers.InonNegativeInteger,
+ ZSI.TCnumbers.InonPositiveInteger,
+ ZSI.TC.Integer,
+ ZSI.TCnumbers.IpositiveInteger,
+ ZSI.TCnumbers.Ishort]
+
+ self._tc_to_float = [
+ ZSI.TC.Decimal,
+ ZSI.TCnumbers.FPEnumeration,
+ ZSI.TCnumbers.FPdouble,
+ ZSI.TCnumbers.FPfloat]
+
+ self._tc_to_string = [
+ ZSI.TC.Base64String,
+ ZSI.TC.Enumeration,
+ ZSI.TC.HexBinaryString,
+ ZSI.TCnumbers.Ibyte,
+ ZSI.TCnumbers.IunsignedByte,
+ ZSI.TCnumbers.IunsignedInt,
+ ZSI.TCnumbers.IunsignedLong,
+ ZSI.TCnumbers.IunsignedShort,
+ ZSI.TC.String,
+ ZSI.TC.URI,
+ ZSI.TC.XMLString,
+ ZSI.TC.Token]
+
+ self._tc_to_tuple = [
+ ZSI.TC.Duration,
+ ZSI.TC.QName,
+ ZSI.TCtimes.gDate,
+ ZSI.TCtimes.gDateTime,
+ ZSI.TCtimes.gDay,
+ ZSI.TCtimes.gMonthDay,
+ ZSI.TCtimes.gTime,
+ ZSI.TCtimes.gYear,
+ ZSI.TCtimes.gMonth,
+ ZSI.TCtimes.gYearMonth]
+
+ return
+
+ def _get_xsd_typecode(self, msg_type):
+ untaged_xsd_types = {'boolean':TC.Boolean,
+ 'decimal':TC.Decimal,
+ 'base64Binary':TC.Base64String}
+ if untaged_xsd_types.has_key(msg_type):
+ return untaged_xsd_types[msg_type]
+ for tc in self._type_list:
+ if tc.type == (SCHEMA.XSD3,msg_type):
+ break
+ else:
+ tc = TC.AnyType
+ return tc
+
+ def _get_soapenc_typecode(self, msg_type):
+ if msg_type == 'Array':
+ return TCcompound.Array
+ if msg_type == 'Struct':
+ return TCcompound.Struct
+
+ return self._get_xsd_typecode(msg_type)
+
+ def get_typeclass(self, msg_type, targetNamespace):
+ prefix, name = SplitQName(msg_type)
+ if targetNamespace in SCHEMA.XSD_LIST:
+ return self._get_xsd_typecode(name)
+ elif targetNamespace in [SOAP.ENC]:
+ return self._get_soapenc_typecode(name)
+ return None
+
+ def get_pythontype(self, msg_type, targetNamespace, typeclass=None):
+ if not typeclass:
+ tc = self.get_typeclass(msg_type, targetNamespace)
+ else:
+ tc = typeclass
+ if tc in self._tc_to_int:
+ return 'int'
+ elif tc in self._tc_to_float:
+ return 'float'
+ elif tc in self._tc_to_string:
+ return 'str'
+ elif tc in self._tc_to_tuple:
+ return 'tuple'
+ elif tc in [TCcompound.Array]:
+ return 'list'
+ elif tc in [TC.Boolean]:
+ return 'bool'
+ elif isinstance(tc, TypeCode):
+ raise EvaluateException,\
+ 'failed to map zsi typecode to a python type'
+ return None
+
+
diff --git a/ZSI/zsi/ZSI/version.py b/ZSI/zsi/ZSI/version.py
new file mode 100644
index 0000000..4936345
--- /dev/null
+++ b/ZSI/zsi/ZSI/version.py
@@ -0,0 +1,2 @@
+# Auto-generated file; do not edit
+Version = (2, 0, 0)
diff --git a/ZSI/zsi/ZSI/writer.py b/ZSI/zsi/ZSI/writer.py
new file mode 100644
index 0000000..27fd8ab
--- /dev/null
+++ b/ZSI/zsi/ZSI/writer.py
@@ -0,0 +1,190 @@
+#! /usr/bin/env python
+# $Id: writer.py 1270 2006-10-19 22:31:07Z boverhof $
+'''SOAP message serialization.
+'''
+
+from ZSI import _copyright, _get_idstr, ZSI_SCHEMA_URI
+from ZSI import _backtrace, _stringtypes, _seqtypes
+from ZSI.wstools.Utility import MessageInterface, ElementProxy
+from ZSI.wstools.Namespaces import XMLNS, SOAP, SCHEMA
+from ZSI.wstools.c14n import Canonicalize
+import types
+
+_standard_ns = [ ('xml', XMLNS.XML), ('xmlns', XMLNS.BASE) ]
+
+_reserved_ns = {
+ 'SOAP-ENV': SOAP.ENV,
+ 'SOAP-ENC': SOAP.ENC,
+ 'ZSI': ZSI_SCHEMA_URI,
+ 'xsd': SCHEMA.BASE,
+ 'xsi': SCHEMA.BASE + '-instance',
+}
+
+class SoapWriter:
+ '''SOAP output formatter.
+ Instance Data:
+ memo -- memory for id/href
+ envelope -- add Envelope?
+ encodingStyle --
+ header -- add SOAP Header?
+ outputclass -- ElementProxy class.
+ '''
+
+ def __init__(self, envelope=True, encodingStyle=None, header=True,
+ nsdict={}, outputclass=None, **kw):
+ '''Initialize.
+ '''
+ outputclass = outputclass or ElementProxy
+ if not issubclass(outputclass, MessageInterface):
+ raise TypeError, 'outputclass must subclass MessageInterface'
+
+ self.dom, self.memo, self.nsdict= \
+ outputclass(self), [], nsdict
+ self.envelope = envelope
+ self.encodingStyle = encodingStyle
+ self.header = header
+ self.body = None
+ self.callbacks = []
+ self.closed = False
+
+ def __str__(self):
+ self.close()
+ return str(self.dom)
+
+ def getSOAPHeader(self):
+ if self.header in (True, False):
+ return None
+ return self.header
+
+ def serialize_header(self, pyobj, typecode=None, **kw):
+ '''Serialize a Python object in SOAP-ENV:Header, make
+ sure everything in Header unique (no #href). Must call
+ serialize first to create a document.
+
+ Parameters:
+ pyobjs -- instances to serialize in SOAP Header
+ typecode -- default typecode
+ '''
+ kw['unique'] = True
+ soap_env = _reserved_ns['SOAP-ENV']
+ #header = self.dom.getElement(soap_env, 'Header')
+ header = self._header
+ if header is None:
+ header = self._header = self.dom.createAppendElement(soap_env,
+ 'Header')
+
+ typecode = getattr(pyobj, 'typecode', typecode)
+ if typecode is None:
+ raise RuntimeError(
+ 'typecode is required to serialize pyobj in header')
+
+ helt = typecode.serialize(header, self, pyobj, **kw)
+
+ def serialize(self, pyobj, typecode=None, root=None, header_pyobjs=(), **kw):
+ '''Serialize a Python object to the output stream.
+ pyobj -- python instance to serialize in body.
+ typecode -- typecode describing body
+ root -- SOAP-ENC:root
+ header_pyobjs -- list of pyobj for soap header inclusion, each
+ instance must specify the typecode attribute.
+ '''
+ self.body = None
+ if self.envelope:
+ soap_env = _reserved_ns['SOAP-ENV']
+ self.dom.createDocument(soap_env, 'Envelope')
+ for prefix, nsuri in _reserved_ns.items():
+ self.dom.setNamespaceAttribute(prefix, nsuri)
+ self.writeNSdict(self.nsdict)
+ if self.encodingStyle:
+ self.dom.setAttributeNS(soap_env, 'encodingStyle',
+ self.encodingStyle)
+ if self.header:
+ self._header = self.dom.createAppendElement(soap_env, 'Header')
+
+ for h in header_pyobjs:
+ self.serialize_header(h, **kw)
+
+ self.body = self.dom.createAppendElement(soap_env, 'Body')
+ else:
+ self.dom.createDocument(None,None)
+
+ if typecode is None: typecode = pyobj.__class__.typecode
+ kw = kw.copy()
+
+ if self.body is None:
+ elt = typecode.serialize(self.dom, self, pyobj, **kw)
+ else:
+ elt = typecode.serialize(self.body, self, pyobj, **kw)
+
+ if root is not None:
+ if root not in [ 0, 1 ]:
+ raise ValueError, "SOAP-ENC root attribute not in [0,1]"
+ elt.setAttributeNS(SOAP.ENC, 'root', root)
+
+ return self
+
+ def writeNSdict(self, nsdict):
+ '''Write a namespace dictionary, taking care to not clobber the
+ standard (or reserved by us) prefixes.
+ '''
+ for k,v in nsdict.items():
+ if (k,v) in _standard_ns: continue
+ rv = _reserved_ns.get(k)
+ if rv:
+ if rv != v:
+ raise KeyError("Reserved namespace " + str((k,v)) + " used")
+ continue
+ if k:
+ self.dom.setNamespaceAttribute(k, v)
+ else:
+ self.dom.setNamespaceAttribute('xmlns', v)
+
+
+ def ReservedNS(self, prefix, uri):
+ '''Is this namespace (prefix,uri) reserved by us?
+ '''
+ return _reserved_ns.get(prefix, uri) != uri
+
+ def AddCallback(self, func, *arglist):
+ '''Add a callback function and argument list to be invoked before
+ closing off the SOAP Body.
+ '''
+ self.callbacks.append((func, arglist))
+
+ def Known(self, obj):
+ '''Seen this object (known by its id()? Return 1 if so,
+ otherwise add it to our memory and return 0.
+ '''
+ obj = _get_idstr(obj)
+ if obj in self.memo: return 1
+ self.memo.append(obj)
+ return 0
+
+ def Forget(self, obj):
+ '''Forget we've seen this object.
+ '''
+ obj = _get_idstr(obj)
+ try:
+ self.memo.remove(obj)
+ except ValueError:
+ pass
+
+ def Backtrace(self, elt):
+ '''Return a human-readable "backtrace" from the document root to
+ the specified element.
+ '''
+ return _backtrace(elt._getNode(), self.dom._getNode())
+
+ def close(self):
+ '''Invoke all the callbacks, and close off the SOAP message.
+ '''
+ if self.closed: return
+ for func,arglist in self.callbacks:
+ apply(func, arglist)
+ self.closed = True
+
+ def __del__(self):
+ if not self.closed: self.close()
+
+
+if __name__ == '__main__': print _copyright
diff --git a/ZSI/zsi/ZSI/wstools/Namespaces.py b/ZSI/zsi/ZSI/wstools/Namespaces.py
new file mode 100755
index 0000000..da7e0c7
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/Namespaces.py
@@ -0,0 +1,194 @@
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+"""Namespace module, so you don't need PyXML
+"""
+
+ident = "$Id: Namespaces.py 1160 2006-03-17 19:28:11Z boverhof $"
+try:
+ from xml.ns import SOAP, SCHEMA, WSDL, XMLNS, DSIG, ENCRYPTION
+ DSIG.C14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
+
+except:
+ class SOAP:
+ ENV = "http://schemas.xmlsoap.org/soap/envelope/"
+ ENC = "http://schemas.xmlsoap.org/soap/encoding/"
+ ACTOR_NEXT = "http://schemas.xmlsoap.org/soap/actor/next"
+
+ class SCHEMA:
+ XSD1 = "http://www.w3.org/1999/XMLSchema"
+ XSD2 = "http://www.w3.org/2000/10/XMLSchema"
+ XSD3 = "http://www.w3.org/2001/XMLSchema"
+ XSD_LIST = [ XSD1, XSD2, XSD3 ]
+ XSI1 = "http://www.w3.org/1999/XMLSchema-instance"
+ XSI2 = "http://www.w3.org/2000/10/XMLSchema-instance"
+ XSI3 = "http://www.w3.org/2001/XMLSchema-instance"
+ XSI_LIST = [ XSI1, XSI2, XSI3 ]
+ BASE = XSD3
+
+ class WSDL:
+ BASE = "http://schemas.xmlsoap.org/wsdl/"
+ BIND_HTTP = "http://schemas.xmlsoap.org/wsdl/http/"
+ BIND_MIME = "http://schemas.xmlsoap.org/wsdl/mime/"
+ BIND_SOAP = "http://schemas.xmlsoap.org/wsdl/soap/"
+ BIND_SOAP12 = "http://schemas.xmlsoap.org/wsdl/soap12/"
+
+ class XMLNS:
+ BASE = "http://www.w3.org/2000/xmlns/"
+ XML = "http://www.w3.org/XML/1998/namespace"
+ HTML = "http://www.w3.org/TR/REC-html40"
+
+ class DSIG:
+ BASE = "http://www.w3.org/2000/09/xmldsig#"
+ C14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
+ C14N_COMM = "http://www.w3.org/TR/2000/CR-xml-c14n-20010315#WithComments"
+ C14N_EXCL = "http://www.w3.org/2001/10/xml-exc-c14n#"
+ DIGEST_MD2 = "http://www.w3.org/2000/09/xmldsig#md2"
+ DIGEST_MD5 = "http://www.w3.org/2000/09/xmldsig#md5"
+ DIGEST_SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
+ ENC_BASE64 = "http://www.w3.org/2000/09/xmldsig#base64"
+ ENVELOPED = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
+ HMAC_SHA1 = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"
+ SIG_DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"
+ SIG_RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
+ XPATH = "http://www.w3.org/TR/1999/REC-xpath-19991116"
+ XSLT = "http://www.w3.org/TR/1999/REC-xslt-19991116"
+
+ class ENCRYPTION:
+ BASE = "http://www.w3.org/2001/04/xmlenc#"
+ BLOCK_3DES = "http://www.w3.org/2001/04/xmlenc#des-cbc"
+ BLOCK_AES128 = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"
+ BLOCK_AES192 = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"
+ BLOCK_AES256 = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
+ DIGEST_RIPEMD160 = "http://www.w3.org/2001/04/xmlenc#ripemd160"
+ DIGEST_SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256"
+ DIGEST_SHA512 = "http://www.w3.org/2001/04/xmlenc#sha512"
+ KA_DH = "http://www.w3.org/2001/04/xmlenc#dh"
+ KT_RSA_1_5 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ KT_RSA_OAEP = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
+ STREAM_ARCFOUR = "http://www.w3.org/2001/04/xmlenc#arcfour"
+ WRAP_3DES = "http://www.w3.org/2001/04/xmlenc#kw-3des"
+ WRAP_AES128 = "http://www.w3.org/2001/04/xmlenc#kw-aes128"
+ WRAP_AES192 = "http://www.w3.org/2001/04/xmlenc#kw-aes192"
+ WRAP_AES256 = "http://www.w3.org/2001/04/xmlenc#kw-aes256"
+
+
+class WSRF_V1_2:
+ '''OASIS WSRF Specifications Version 1.2
+ '''
+ class LIFETIME:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd"
+ XSD_DRAFT4 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceLifetime-1.2-draft-04.xsd"
+
+ WSDL_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.wsdl"
+ WSDL_DRAFT4 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceLifetime-1.2-draft-04.wsdl"
+ LATEST = WSDL_DRAFT4
+ WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT4)
+ XSD_LIST = (XSD_DRAFT1, XSD_DRAFT4)
+
+ class PROPERTIES:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd"
+ XSD_DRAFT5 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceProperties-1.2-draft-05.xsd"
+
+ WSDL_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
+ WSDL_DRAFT5 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceProperties-1.2-draft-05.wsdl"
+ LATEST = WSDL_DRAFT5
+ WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT5)
+ XSD_LIST = (XSD_DRAFT1, XSD_DRAFT5)
+
+ class BASENOTIFICATION:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd"
+
+ WSDL_DRAFT1 = "http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl"
+ LATEST = WSDL_DRAFT1
+ WSDL_LIST = (WSDL_DRAFT1,)
+ XSD_LIST = (XSD_DRAFT1,)
+
+ class BASEFAULTS:
+ XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-BaseFaults-1.2-draft-01.xsd"
+ XSD_DRAFT3 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-BaseFaults-1.2-draft-03.xsd"
+ #LATEST = DRAFT3
+ #WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT3)
+ XSD_LIST = (XSD_DRAFT1, XSD_DRAFT3)
+
+WSRF = WSRF_V1_2
+WSRFLIST = (WSRF_V1_2,)
+
+
+class OASIS:
+ '''URLs for Oasis specifications
+ '''
+ WSSE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
+ UTILITY = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
+
+ class X509TOKEN:
+ Base64Binary = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
+ STRTransform = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0"
+ PKCS7 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#PKCS7"
+ X509 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509"
+ X509PKIPathv1 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"
+ X509v3SubjectKeyIdentifier = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3SubjectKeyIdentifier"
+
+ LIFETIME = WSRF_V1_2.LIFETIME.XSD_DRAFT1
+ PROPERTIES = WSRF_V1_2.PROPERTIES.XSD_DRAFT1
+ BASENOTIFICATION = WSRF_V1_2.BASENOTIFICATION.XSD_DRAFT1
+ BASEFAULTS = WSRF_V1_2.BASEFAULTS.XSD_DRAFT1
+
+
+class WSTRUST:
+ BASE = "http://schemas.xmlsoap.org/ws/2004/04/trust"
+ ISSUE = "http://schemas.xmlsoap.org/ws/2004/04/trust/Issue"
+
+class WSSE:
+ BASE = "http://schemas.xmlsoap.org/ws/2002/04/secext"
+ TRUST = WSTRUST.BASE
+
+
+class WSU:
+ BASE = "http://schemas.xmlsoap.org/ws/2002/04/utility"
+ UTILITY = "http://schemas.xmlsoap.org/ws/2002/07/utility"
+
+
+class WSR:
+ PROPERTIES = "http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceProperties"
+ LIFETIME = "http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceLifetime"
+
+
+class WSA200408:
+ ADDRESS = "http://schemas.xmlsoap.org/ws/2004/08/addressing"
+ ANONYMOUS = "%s/role/anonymous" %ADDRESS
+ FAULT = "%s/fault" %ADDRESS
+
+class WSA200403:
+ ADDRESS = "http://schemas.xmlsoap.org/ws/2004/03/addressing"
+ ANONYMOUS = "%s/role/anonymous" %ADDRESS
+ FAULT = "%s/fault" %ADDRESS
+
+class WSA200303:
+ ADDRESS = "http://schemas.xmlsoap.org/ws/2003/03/addressing"
+ ANONYMOUS = "%s/role/anonymous" %ADDRESS
+ FAULT = None
+
+
+WSA = WSA200408
+WSA_LIST = (WSA200408, WSA200403, WSA200303)
+
+class WSP:
+ POLICY = "http://schemas.xmlsoap.org/ws/2002/12/policy"
+
+class BEA:
+ SECCONV = "http://schemas.xmlsoap.org/ws/2004/04/sc"
+ SCTOKEN = "http://schemas.xmlsoap.org/ws/2004/04/security/sc/sct"
+
+class GLOBUS:
+ SECCONV = "http://wsrf.globus.org/core/2004/07/security/secconv"
+ CORE = "http://www.globus.org/namespaces/2004/06/core"
+ SIG = "http://www.globus.org/2002/04/xmlenc#gssapi-sign"
+ TOKEN = "http://www.globus.org/ws/2004/09/security/sc#GSSAPI_GSI_TOKEN"
+
+ZSI_SCHEMA_URI = 'http://www.zolera.com/schemas/ZSI/'
diff --git a/ZSI/zsi/ZSI/wstools/TimeoutSocket.py b/ZSI/zsi/ZSI/wstools/TimeoutSocket.py
new file mode 100755
index 0000000..9e2f2db
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/TimeoutSocket.py
@@ -0,0 +1,179 @@
+"""Based on code from timeout_socket.py, with some tweaks for compatibility.
+ These tweaks should really be rolled back into timeout_socket, but it's
+ not totally clear who is maintaining it at this point. In the meantime,
+ we'll use a different module name for our tweaked version to avoid any
+ confusion.
+
+ The original timeout_socket is by:
+
+ Scott Cotton <scott@chronis.pobox.com>
+ Lloyd Zusman <ljz@asfast.com>
+ Phil Mayes <pmayes@olivebr.com>
+ Piers Lauder <piers@cs.su.oz.au>
+ Radovan Garabik <garabik@melkor.dnp.fmph.uniba.sk>
+"""
+
+ident = "$Id: TimeoutSocket.py 237 2003-05-20 21:10:14Z warnes $"
+
+import string, socket, select, errno
+
+WSAEINVAL = getattr(errno, 'WSAEINVAL', 10022)
+
+
+class TimeoutSocket:
+ """A socket imposter that supports timeout limits."""
+
+ def __init__(self, timeout=20, sock=None):
+ self.timeout = float(timeout)
+ self.inbuf = ''
+ if sock is None:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock = sock
+ self.sock.setblocking(0)
+ self._rbuf = ''
+ self._wbuf = ''
+
+ def __getattr__(self, name):
+ # Delegate to real socket attributes.
+ return getattr(self.sock, name)
+
+ def connect(self, *addr):
+ timeout = self.timeout
+ sock = self.sock
+ try:
+ # Non-blocking mode
+ sock.setblocking(0)
+ apply(sock.connect, addr)
+ sock.setblocking(timeout != 0)
+ return 1
+ except socket.error,why:
+ if not timeout:
+ raise
+ sock.setblocking(1)
+ if len(why.args) == 1:
+ code = 0
+ else:
+ code, why = why
+ if code not in (
+ errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK
+ ):
+ raise
+ r,w,e = select.select([],[sock],[],timeout)
+ if w:
+ try:
+ apply(sock.connect, addr)
+ return 1
+ except socket.error,why:
+ if len(why.args) == 1:
+ code = 0
+ else:
+ code, why = why
+ if code in (errno.EISCONN, WSAEINVAL):
+ return 1
+ raise
+ raise TimeoutError('socket connect() timeout.')
+
+ def send(self, data, flags=0):
+ total = len(data)
+ next = 0
+ while 1:
+ r, w, e = select.select([],[self.sock], [], self.timeout)
+ if w:
+ buff = data[next:next + 8192]
+ sent = self.sock.send(buff, flags)
+ next = next + sent
+ if next == total:
+ return total
+ continue
+ raise TimeoutError('socket send() timeout.')
+
+ def recv(self, amt, flags=0):
+ if select.select([self.sock], [], [], self.timeout)[0]:
+ return self.sock.recv(amt, flags)
+ raise TimeoutError('socket recv() timeout.')
+
+ buffsize = 4096
+ handles = 1
+
+ def makefile(self, mode="r", buffsize=-1):
+ self.handles = self.handles + 1
+ self.mode = mode
+ return self
+
+ def close(self):
+ self.handles = self.handles - 1
+ if self.handles == 0 and self.sock.fileno() >= 0:
+ self.sock.close()
+
+ def read(self, n=-1):
+ if not isinstance(n, type(1)):
+ n = -1
+ if n >= 0:
+ k = len(self._rbuf)
+ if n <= k:
+ data = self._rbuf[:n]
+ self._rbuf = self._rbuf[n:]
+ return data
+ n = n - k
+ L = [self._rbuf]
+ self._rbuf = ""
+ while n > 0:
+ new = self.recv(max(n, self.buffsize))
+ if not new: break
+ k = len(new)
+ if k > n:
+ L.append(new[:n])
+ self._rbuf = new[n:]
+ break
+ L.append(new)
+ n = n - k
+ return "".join(L)
+ k = max(4096, self.buffsize)
+ L = [self._rbuf]
+ self._rbuf = ""
+ while 1:
+ new = self.recv(k)
+ if not new: break
+ L.append(new)
+ k = min(k*2, 1024**2)
+ return "".join(L)
+
+ def readline(self, limit=-1):
+ data = ""
+ i = self._rbuf.find('\n')
+ while i < 0 and not (0 < limit <= len(self._rbuf)):
+ new = self.recv(self.buffsize)
+ if not new: break
+ i = new.find('\n')
+ if i >= 0: i = i + len(self._rbuf)
+ self._rbuf = self._rbuf + new
+ if i < 0: i = len(self._rbuf)
+ else: i = i+1
+ if 0 <= limit < len(self._rbuf): i = limit
+ data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
+ return data
+
+ def readlines(self, sizehint = 0):
+ total = 0
+ list = []
+ while 1:
+ line = self.readline()
+ if not line: break
+ list.append(line)
+ total += len(line)
+ if sizehint and total >= sizehint:
+ break
+ return list
+
+ def writelines(self, list):
+ self.send(''.join(list))
+
+ def write(self, data):
+ self.send(data)
+
+ def flush(self):
+ pass
+
+
+class TimeoutError(Exception):
+ pass
diff --git a/ZSI/zsi/ZSI/wstools/UserTuple.py b/ZSI/zsi/ZSI/wstools/UserTuple.py
new file mode 100644
index 0000000..86ff8e5
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/UserTuple.py
@@ -0,0 +1,99 @@
+"""
+A more or less complete user-defined wrapper around tuple objects.
+Adapted version of the standard library's UserList.
+
+Taken from Stefan Schwarzer's ftputil library, available at
+<http://www.ndh.net/home/sschwarzer/python/python_software.html>, and used under this license:
+
+
+
+
+Copyright (C) 1999, Stefan Schwarzer
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+- Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+- Neither the name of the above author nor the names of the
+ contributors to the software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+
+
+
+
+# $Id: UserTuple.py 277 2003-07-21 14:18:54Z warnes $
+
+#XXX tuple instances (in Python 2.2) contain also:
+# __class__, __delattr__, __getattribute__, __hash__, __new__,
+# __reduce__, __setattr__, __str__
+# What about these?
+
+class UserTuple:
+ def __init__(self, inittuple=None):
+ self.data = ()
+ if inittuple is not None:
+ # XXX should this accept an arbitrary sequence?
+ if type(inittuple) == type(self.data):
+ self.data = inittuple
+ elif isinstance(inittuple, UserTuple):
+ # this results in
+ # self.data is inittuple.data
+ # but that's ok for tuples because they are
+ # immutable. (Builtin tuples behave the same.)
+ self.data = inittuple.data[:]
+ else:
+ # the same applies here; (t is tuple(t)) == 1
+ self.data = tuple(inittuple)
+ def __repr__(self): return repr(self.data)
+ def __lt__(self, other): return self.data < self.__cast(other)
+ def __le__(self, other): return self.data <= self.__cast(other)
+ def __eq__(self, other): return self.data == self.__cast(other)
+ def __ne__(self, other): return self.data != self.__cast(other)
+ def __gt__(self, other): return self.data > self.__cast(other)
+ def __ge__(self, other): return self.data >= self.__cast(other)
+ def __cast(self, other):
+ if isinstance(other, UserTuple): return other.data
+ else: return other
+ def __cmp__(self, other):
+ return cmp(self.data, self.__cast(other))
+ def __contains__(self, item): return item in self.data
+ def __len__(self): return len(self.data)
+ def __getitem__(self, i): return self.data[i]
+ def __getslice__(self, i, j):
+ i = max(i, 0); j = max(j, 0)
+ return self.__class__(self.data[i:j])
+ def __add__(self, other):
+ if isinstance(other, UserTuple):
+ return self.__class__(self.data + other.data)
+ elif isinstance(other, type(self.data)):
+ return self.__class__(self.data + other)
+ else:
+ return self.__class__(self.data + tuple(other))
+ # dir( () ) contains no __radd__ (at least in Python 2.2)
+ def __mul__(self, n):
+ return self.__class__(self.data*n)
+ __rmul__ = __mul__
+
diff --git a/ZSI/zsi/ZSI/wstools/Utility.py b/ZSI/zsi/ZSI/wstools/Utility.py
new file mode 100755
index 0000000..ebe62fe
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/Utility.py
@@ -0,0 +1,1376 @@
+# Copyright (c) 2003, The Regents of the University of California,
+# through Lawrence Berkeley National Laboratory (subject to receipt of
+# any required approvals from the U.S. Dept. of Energy). All rights
+# reserved.
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+
+ident = "$Id: Utility.py 1116 2006-01-24 20:51:57Z boverhof $"
+
+import sys, types, httplib, smtplib, urllib, socket, weakref
+from os.path import isfile
+from string import join, strip, split
+from UserDict import UserDict
+from cStringIO import StringIO
+from TimeoutSocket import TimeoutSocket, TimeoutError
+from urlparse import urlparse
+from httplib import HTTPConnection, HTTPSConnection
+from exceptions import Exception
+try:
+ from ZSI import _get_idstr
+except:
+ def _get_idstr(pyobj):
+ '''Python 2.3.x generates a FutureWarning for negative IDs, so
+ we use a different prefix character to ensure uniqueness, and
+ call abs() to avoid the warning.'''
+ x = id(pyobj)
+ if x < 0:
+ return 'x%x' % abs(x)
+ return 'o%x' % x
+
+import xml.dom.minidom
+from xml.dom import Node
+
+import logging
+from c14n import Canonicalize
+from Namespaces import SCHEMA, SOAP, XMLNS, ZSI_SCHEMA_URI
+
+
+try:
+ from xml.dom.ext import SplitQName
+except:
+ def SplitQName(qname):
+ '''SplitQName(qname) -> (string, string)
+
+ Split Qualified Name into a tuple of len 2, consisting
+ of the prefix and the local name.
+
+ (prefix, localName)
+
+ Special Cases:
+ xmlns -- (localName, 'xmlns')
+ None -- (None, localName)
+ '''
+
+ l = qname.split(':')
+ if len(l) == 1:
+ l.insert(0, None)
+ elif len(l) == 2:
+ if l[0] == 'xmlns':
+ l.reverse()
+ else:
+ return
+ return tuple(l)
+
+#
+# python2.3 urllib.basejoin does not remove current directory ./
+# from path and this causes problems on subsequent basejoins.
+#
+basejoin = urllib.basejoin
+if sys.version_info[0:2] < (2, 4, 0, 'final', 0)[0:2]:
+ #basejoin = lambda base,url: urllib.basejoin(base,url.lstrip('./'))
+ token = './'
+ def basejoin(base, url):
+ if url.startswith(token) is True:
+ return urllib.basejoin(base,url[2:])
+ return urllib.basejoin(base,url)
+
+class NamespaceError(Exception):
+ """Used to indicate a Namespace Error."""
+
+
+class RecursionError(Exception):
+ """Used to indicate a HTTP redirect recursion."""
+
+
+class ParseError(Exception):
+ """Used to indicate a XML parsing error."""
+
+
+class DOMException(Exception):
+ """Used to indicate a problem processing DOM."""
+
+
+class Base:
+ """Base class for instance level Logging"""
+ def __init__(self, module=__name__):
+ self.logger = logging.getLogger('%s-%s(%s)' %(module, self.__class__, _get_idstr(self)))
+
+
+class HTTPResponse:
+ """Captures the information in an HTTP response message."""
+
+ def __init__(self, response):
+ self.status = response.status
+ self.reason = response.reason
+ self.headers = response.msg
+ self.body = response.read() or None
+ response.close()
+
+class TimeoutHTTP(HTTPConnection):
+ """A custom http connection object that supports socket timeout."""
+ def __init__(self, host, port=None, timeout=20):
+ HTTPConnection.__init__(self, host, port)
+ self.timeout = timeout
+
+ def connect(self):
+ self.sock = TimeoutSocket(self.timeout)
+ self.sock.connect((self.host, self.port))
+
+
+class TimeoutHTTPS(HTTPSConnection):
+ """A custom https object that supports socket timeout. Note that this
+ is not really complete. The builtin SSL support in the Python socket
+ module requires a real socket (type) to be passed in to be hooked to
+ SSL. That means our fake socket won't work and our timeout hacks are
+ bypassed for send and recv calls. Since our hack _is_ in place at
+ connect() time, it should at least provide some timeout protection."""
+ def __init__(self, host, port=None, timeout=20, **kwargs):
+ HTTPSConnection.__init__(self, str(host), port, **kwargs)
+ self.timeout = timeout
+
+ def connect(self):
+ sock = TimeoutSocket(self.timeout)
+ sock.connect((self.host, self.port))
+ realsock = getattr(sock.sock, '_sock', sock.sock)
+ ssl = socket.ssl(realsock, self.key_file, self.cert_file)
+ self.sock = httplib.FakeSocket(sock, ssl)
+
+
+def urlopen(url, timeout=20, redirects=None):
+ """A minimal urlopen replacement hack that supports timeouts for http.
+ Note that this supports GET only."""
+ scheme, host, path, params, query, frag = urlparse(url)
+
+ if not scheme in ('http', 'https'):
+ return urllib.urlopen(url)
+ if params: path = '%s;%s' % (path, params)
+ if query: path = '%s?%s' % (path, query)
+ if frag: path = '%s#%s' % (path, frag)
+
+ if scheme == 'https':
+ # If ssl is not compiled into Python, you will not get an exception
+ # until a conn.endheaders() call. We need to know sooner, so use
+ # getattr.
+ if hasattr(socket, 'ssl'):
+ conn = TimeoutHTTPS(host, None, timeout)
+ else:
+ import M2Crypto
+ ctx = M2Crypto.SSL.Context()
+ ctx.set_session_timeout(timeout)
+ conn = M2Crypto.httpslib.HTTPSConnection(host, ssl_context=ctx)
+ #conn.set_debuglevel(1)
+ else:
+ conn = TimeoutHTTP(host, None, timeout)
+
+ conn.putrequest('GET', path)
+ conn.putheader('Connection', 'close')
+ conn.endheaders()
+ response = None
+ while 1:
+ response = conn.getresponse()
+ if response.status != 100:
+ break
+ conn._HTTPConnection__state = httplib._CS_REQ_SENT
+ conn._HTTPConnection__response = None
+
+ status = response.status
+
+ # If we get an HTTP redirect, we will follow it automatically.
+ if status >= 300 and status < 400:
+ location = response.msg.getheader('location')
+ if location is not None:
+ response.close()
+ if redirects is not None and redirects.has_key(location):
+ raise RecursionError(
+ 'Circular HTTP redirection detected.'
+ )
+ if redirects is None:
+ redirects = {}
+ redirects[location] = 1
+ return urlopen(location, timeout, redirects)
+ raise HTTPResponse(response)
+
+ if not (status >= 200 and status < 300):
+ raise HTTPResponse(response)
+
+ body = StringIO(response.read())
+ response.close()
+ return body
+
+class DOM:
+ """The DOM singleton defines a number of XML related constants and
+ provides a number of utility methods for DOM related tasks. It
+ also provides some basic abstractions so that the rest of the
+ package need not care about actual DOM implementation in use."""
+
+ # Namespace stuff related to the SOAP specification.
+
+ NS_SOAP_ENV_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/'
+ NS_SOAP_ENC_1_1 = 'http://schemas.xmlsoap.org/soap/encoding/'
+
+ NS_SOAP_ENV_1_2 = 'http://www.w3.org/2001/06/soap-envelope'
+ NS_SOAP_ENC_1_2 = 'http://www.w3.org/2001/06/soap-encoding'
+
+ NS_SOAP_ENV_ALL = (NS_SOAP_ENV_1_1, NS_SOAP_ENV_1_2)
+ NS_SOAP_ENC_ALL = (NS_SOAP_ENC_1_1, NS_SOAP_ENC_1_2)
+
+ NS_SOAP_ENV = NS_SOAP_ENV_1_1
+ NS_SOAP_ENC = NS_SOAP_ENC_1_1
+
+ _soap_uri_mapping = {
+ NS_SOAP_ENV_1_1 : '1.1',
+ NS_SOAP_ENV_1_2 : '1.2',
+ }
+
+ SOAP_ACTOR_NEXT_1_1 = 'http://schemas.xmlsoap.org/soap/actor/next'
+ SOAP_ACTOR_NEXT_1_2 = 'http://www.w3.org/2001/06/soap-envelope/actor/next'
+ SOAP_ACTOR_NEXT_ALL = (SOAP_ACTOR_NEXT_1_1, SOAP_ACTOR_NEXT_1_2)
+
+ def SOAPUriToVersion(self, uri):
+ """Return the SOAP version related to an envelope uri."""
+ value = self._soap_uri_mapping.get(uri)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP envelope uri: %s' % uri
+ )
+
+ def GetSOAPEnvUri(self, version):
+ """Return the appropriate SOAP envelope uri for a given
+ human-friendly SOAP version string (e.g. '1.1')."""
+ attrname = 'NS_SOAP_ENV_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attrname, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP version: %s' % version
+ )
+
+ def GetSOAPEncUri(self, version):
+ """Return the appropriate SOAP encoding uri for a given
+ human-friendly SOAP version string (e.g. '1.1')."""
+ attrname = 'NS_SOAP_ENC_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attrname, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP version: %s' % version
+ )
+
+ def GetSOAPActorNextUri(self, version):
+ """Return the right special next-actor uri for a given
+ human-friendly SOAP version string (e.g. '1.1')."""
+ attrname = 'SOAP_ACTOR_NEXT_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attrname, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP version: %s' % version
+ )
+
+
+ # Namespace stuff related to XML Schema.
+
+ NS_XSD_99 = 'http://www.w3.org/1999/XMLSchema'
+ NS_XSI_99 = 'http://www.w3.org/1999/XMLSchema-instance'
+
+ NS_XSD_00 = 'http://www.w3.org/2000/10/XMLSchema'
+ NS_XSI_00 = 'http://www.w3.org/2000/10/XMLSchema-instance'
+
+ NS_XSD_01 = 'http://www.w3.org/2001/XMLSchema'
+ NS_XSI_01 = 'http://www.w3.org/2001/XMLSchema-instance'
+
+ NS_XSD_ALL = (NS_XSD_99, NS_XSD_00, NS_XSD_01)
+ NS_XSI_ALL = (NS_XSI_99, NS_XSI_00, NS_XSI_01)
+
+ NS_XSD = NS_XSD_01
+ NS_XSI = NS_XSI_01
+
+ _xsd_uri_mapping = {
+ NS_XSD_99 : NS_XSI_99,
+ NS_XSD_00 : NS_XSI_00,
+ NS_XSD_01 : NS_XSI_01,
+ }
+
+ for key, value in _xsd_uri_mapping.items():
+ _xsd_uri_mapping[value] = key
+
+
+ def InstanceUriForSchemaUri(self, uri):
+ """Return the appropriate matching XML Schema instance uri for
+ the given XML Schema namespace uri."""
+ return self._xsd_uri_mapping.get(uri)
+
+ def SchemaUriForInstanceUri(self, uri):
+ """Return the appropriate matching XML Schema namespace uri for
+ the given XML Schema instance namespace uri."""
+ return self._xsd_uri_mapping.get(uri)
+
+
+ # Namespace stuff related to WSDL.
+
+ NS_WSDL_1_1 = 'http://schemas.xmlsoap.org/wsdl/'
+ NS_WSDL_ALL = (NS_WSDL_1_1,)
+ NS_WSDL = NS_WSDL_1_1
+
+ NS_SOAP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/'
+ NS_HTTP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/http/'
+ NS_MIME_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/mime/'
+
+ NS_SOAP_BINDING_ALL = (NS_SOAP_BINDING_1_1,)
+ NS_HTTP_BINDING_ALL = (NS_HTTP_BINDING_1_1,)
+ NS_MIME_BINDING_ALL = (NS_MIME_BINDING_1_1,)
+
+ NS_SOAP_BINDING = NS_SOAP_BINDING_1_1
+ NS_HTTP_BINDING = NS_HTTP_BINDING_1_1
+ NS_MIME_BINDING = NS_MIME_BINDING_1_1
+
+ NS_SOAP_HTTP_1_1 = 'http://schemas.xmlsoap.org/soap/http'
+ NS_SOAP_HTTP_ALL = (NS_SOAP_HTTP_1_1,)
+ NS_SOAP_HTTP = NS_SOAP_HTTP_1_1
+
+
+ _wsdl_uri_mapping = {
+ NS_WSDL_1_1 : '1.1',
+ }
+
+ def WSDLUriToVersion(self, uri):
+ """Return the WSDL version related to a WSDL namespace uri."""
+ value = self._wsdl_uri_mapping.get(uri)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported SOAP envelope uri: %s' % uri
+ )
+
+ def GetWSDLUri(self, version):
+ attr = 'NS_WSDL_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLSoapBindingUri(self, version):
+ attr = 'NS_SOAP_BINDING_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLHttpBindingUri(self, version):
+ attr = 'NS_HTTP_BINDING_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLMimeBindingUri(self, version):
+ attr = 'NS_MIME_BINDING_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+ def GetWSDLHttpTransportUri(self, version):
+ attr = 'NS_SOAP_HTTP_%s' % join(split(version, '.'), '_')
+ value = getattr(self, attr, None)
+ if value is not None:
+ return value
+ raise ValueError(
+ 'Unsupported WSDL version: %s' % version
+ )
+
+
+ # Other xml namespace constants.
+ NS_XMLNS = 'http://www.w3.org/2000/xmlns/'
+
+
+
+ def isElement(self, node, name, nsuri=None):
+ """Return true if the given node is an element with the given
+ name and optional namespace uri."""
+ if node.nodeType != node.ELEMENT_NODE:
+ return 0
+ return node.localName == name and \
+ (nsuri is None or self.nsUriMatch(node.namespaceURI, nsuri))
+
+ def getElement(self, node, name, nsuri=None, default=join):
+ """Return the first child of node with a matching name and
+ namespace uri, or the default if one is provided."""
+ nsmatch = self.nsUriMatch
+ ELEMENT_NODE = node.ELEMENT_NODE
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if ((child.localName == name or name is None) and
+ (nsuri is None or nsmatch(child.namespaceURI, nsuri))
+ ):
+ return child
+ if default is not join:
+ return default
+ raise KeyError, name
+
+ def getElementById(self, node, id, default=join):
+ """Return the first child of node matching an id reference."""
+ attrget = self.getAttr
+ ELEMENT_NODE = node.ELEMENT_NODE
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if attrget(child, 'id') == id:
+ return child
+ if default is not join:
+ return default
+ raise KeyError, name
+
+ def getMappingById(self, document, depth=None, element=None,
+ mapping=None, level=1):
+ """Create an id -> element mapping of those elements within a
+ document that define an id attribute. The depth of the search
+ may be controlled by using the (1-based) depth argument."""
+ if document is not None:
+ element = document.documentElement
+ mapping = {}
+ attr = element._attrs.get('id', None)
+ if attr is not None:
+ mapping[attr.value] = element
+ if depth is None or depth > level:
+ level = level + 1
+ ELEMENT_NODE = element.ELEMENT_NODE
+ for child in element.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ self.getMappingById(None, depth, child, mapping, level)
+ return mapping
+
+ def getElements(self, node, name, nsuri=None):
+ """Return a sequence of the child elements of the given node that
+ match the given name and optional namespace uri."""
+ nsmatch = self.nsUriMatch
+ result = []
+ ELEMENT_NODE = node.ELEMENT_NODE
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if ((child.localName == name or name is None) and (
+ (nsuri is None) or nsmatch(child.namespaceURI, nsuri))):
+ result.append(child)
+ return result
+
+ def hasAttr(self, node, name, nsuri=None):
+ """Return true if element has attribute with the given name and
+ optional nsuri. If nsuri is not specified, returns true if an
+ attribute exists with the given name with any namespace."""
+ if nsuri is None:
+ if node.hasAttribute(name):
+ return True
+ return False
+ return node.hasAttributeNS(nsuri, name)
+
+ def getAttr(self, node, name, nsuri=None, default=join):
+ """Return the value of the attribute named 'name' with the
+ optional nsuri, or the default if one is specified. If
+ nsuri is not specified, an attribute that matches the
+ given name will be returned regardless of namespace."""
+ if nsuri is None:
+ result = node._attrs.get(name, None)
+ if result is None:
+ for item in node._attrsNS.keys():
+ if item[1] == name:
+ result = node._attrsNS[item]
+ break
+ else:
+ result = node._attrsNS.get((nsuri, name), None)
+ if result is not None:
+ return result.value
+ if default is not join:
+ return default
+ return ''
+
+ def getAttrs(self, node):
+ """Return a Collection of all attributes
+ """
+ attrs = {}
+ for k,v in node._attrs.items():
+ attrs[k] = v.value
+ return attrs
+
+ def getElementText(self, node, preserve_ws=None):
+ """Return the text value of an xml element node. Leading and trailing
+ whitespace is stripped from the value unless the preserve_ws flag
+ is passed with a true value."""
+ result = []
+ for child in node.childNodes:
+ nodetype = child.nodeType
+ if nodetype == child.TEXT_NODE or \
+ nodetype == child.CDATA_SECTION_NODE:
+ result.append(child.nodeValue)
+ value = join(result, '')
+ if preserve_ws is None:
+ value = strip(value)
+ return value
+
+ def findNamespaceURI(self, prefix, node):
+ """Find a namespace uri given a prefix and a context node."""
+ attrkey = (self.NS_XMLNS, prefix)
+ DOCUMENT_NODE = node.DOCUMENT_NODE
+ ELEMENT_NODE = node.ELEMENT_NODE
+ while 1:
+ if node is None:
+ raise DOMException('Value for prefix %s not found.' % prefix)
+ if node.nodeType != ELEMENT_NODE:
+ node = node.parentNode
+ continue
+ result = node._attrsNS.get(attrkey, None)
+ if result is not None:
+ return result.value
+ if hasattr(node, '__imported__'):
+ raise DOMException('Value for prefix %s not found.' % prefix)
+ node = node.parentNode
+ if node.nodeType == DOCUMENT_NODE:
+ raise DOMException('Value for prefix %s not found.' % prefix)
+
+ def findDefaultNS(self, node):
+ """Return the current default namespace uri for the given node."""
+ attrkey = (self.NS_XMLNS, 'xmlns')
+ DOCUMENT_NODE = node.DOCUMENT_NODE
+ ELEMENT_NODE = node.ELEMENT_NODE
+ while 1:
+ if node.nodeType != ELEMENT_NODE:
+ node = node.parentNode
+ continue
+ result = node._attrsNS.get(attrkey, None)
+ if result is not None:
+ return result.value
+ if hasattr(node, '__imported__'):
+ raise DOMException('Cannot determine default namespace.')
+ node = node.parentNode
+ if node.nodeType == DOCUMENT_NODE:
+ raise DOMException('Cannot determine default namespace.')
+
+ def findTargetNS(self, node):
+ """Return the defined target namespace uri for the given node."""
+ attrget = self.getAttr
+ attrkey = (self.NS_XMLNS, 'xmlns')
+ DOCUMENT_NODE = node.DOCUMENT_NODE
+ ELEMENT_NODE = node.ELEMENT_NODE
+ while 1:
+ if node.nodeType != ELEMENT_NODE:
+ node = node.parentNode
+ continue
+ result = attrget(node, 'targetNamespace', default=None)
+ if result is not None:
+ return result
+ node = node.parentNode
+ if node.nodeType == DOCUMENT_NODE:
+ raise DOMException('Cannot determine target namespace.')
+
+ def getTypeRef(self, element):
+ """Return (namespaceURI, name) for a type attribue of the given
+ element, or None if the element does not have a type attribute."""
+ typeattr = self.getAttr(element, 'type', default=None)
+ if typeattr is None:
+ return None
+ parts = typeattr.split(':', 1)
+ if len(parts) == 2:
+ nsuri = self.findNamespaceURI(parts[0], element)
+ else:
+ nsuri = self.findDefaultNS(element)
+ return (nsuri, parts[1])
+
+ def importNode(self, document, node, deep=0):
+ """Implements (well enough for our purposes) DOM node import."""
+ nodetype = node.nodeType
+ if nodetype in (node.DOCUMENT_NODE, node.DOCUMENT_TYPE_NODE):
+ raise DOMException('Illegal node type for importNode')
+ if nodetype == node.ENTITY_REFERENCE_NODE:
+ deep = 0
+ clone = node.cloneNode(deep)
+ self._setOwnerDoc(document, clone)
+ clone.__imported__ = 1
+ return clone
+
+ def _setOwnerDoc(self, document, node):
+ node.ownerDocument = document
+ for child in node.childNodes:
+ self._setOwnerDoc(document, child)
+
+ def nsUriMatch(self, value, wanted, strict=0, tt=type(())):
+ """Return a true value if two namespace uri values match."""
+ if value == wanted or (type(wanted) is tt) and value in wanted:
+ return 1
+ if not strict and value is not None:
+ wanted = type(wanted) is tt and wanted or (wanted,)
+ value = value[-1:] != '/' and value or value[:-1]
+ for item in wanted:
+ if item == value or item[:-1] == value:
+ return 1
+ return 0
+
+ def createDocument(self, nsuri, qname, doctype=None):
+ """Create a new writable DOM document object."""
+ impl = xml.dom.minidom.getDOMImplementation()
+ return impl.createDocument(nsuri, qname, doctype)
+
+ def loadDocument(self, data):
+ """Load an xml file from a file-like object and return a DOM
+ document instance."""
+ return xml.dom.minidom.parse(data)
+
+ def loadFromURL(self, url):
+ """Load an xml file from a URL and return a DOM document."""
+ if isfile(url) is True:
+ file = open(url, 'r')
+ else:
+ file = urlopen(url)
+
+ try:
+ result = self.loadDocument(file)
+ except Exception, ex:
+ file.close()
+ raise ParseError(('Failed to load document %s' %url,) + ex.args)
+ else:
+ file.close()
+ return result
+
+DOM = DOM()
+
+
+class MessageInterface:
+ '''Higher Level Interface, delegates to DOM singleton, must
+ be subclassed and implement all methods that throw NotImplementedError.
+ '''
+ def __init__(self, sw):
+ '''Constructor, May be extended, do not override.
+ sw -- soapWriter instance
+ '''
+ self.sw = None
+ if type(sw) != weakref.ReferenceType and sw is not None:
+ self.sw = weakref.ref(sw)
+ else:
+ self.sw = sw
+
+ def AddCallback(self, func, *arglist):
+ self.sw().AddCallback(func, *arglist)
+
+ def Known(self, obj):
+ return self.sw().Known(obj)
+
+ def Forget(self, obj):
+ return self.sw().Forget(obj)
+
+ def canonicalize(self):
+ '''canonicalize the underlying DOM, and return as string.
+ '''
+ raise NotImplementedError, ''
+
+ def createDocument(self, namespaceURI=SOAP.ENV, localName='Envelope'):
+ '''create Document
+ '''
+ raise NotImplementedError, ''
+
+ def createAppendElement(self, namespaceURI, localName):
+ '''create and append element(namespaceURI,localName), and return
+ the node.
+ '''
+ raise NotImplementedError, ''
+
+ def findNamespaceURI(self, qualifiedName):
+ raise NotImplementedError, ''
+
+ def resolvePrefix(self, prefix):
+ raise NotImplementedError, ''
+
+ def setAttributeNS(self, namespaceURI, localName, value):
+ '''set attribute (namespaceURI, localName)=value
+ '''
+ raise NotImplementedError, ''
+
+ def setAttributeType(self, namespaceURI, localName):
+ '''set attribute xsi:type=(namespaceURI, localName)
+ '''
+ raise NotImplementedError, ''
+
+ def setNamespaceAttribute(self, namespaceURI, prefix):
+ '''set namespace attribute xmlns:prefix=namespaceURI
+ '''
+ raise NotImplementedError, ''
+
+
+class ElementProxy(Base, MessageInterface):
+ '''
+ '''
+ _soap_env_prefix = 'SOAP-ENV'
+ _soap_enc_prefix = 'SOAP-ENC'
+ _zsi_prefix = 'ZSI'
+ _xsd_prefix = 'xsd'
+ _xsi_prefix = 'xsi'
+ _xml_prefix = 'xml'
+ _xmlns_prefix = 'xmlns'
+
+ _soap_env_nsuri = SOAP.ENV
+ _soap_enc_nsuri = SOAP.ENC
+ _zsi_nsuri = ZSI_SCHEMA_URI
+ _xsd_nsuri = SCHEMA.XSD3
+ _xsi_nsuri = SCHEMA.XSI3
+ _xml_nsuri = XMLNS.XML
+ _xmlns_nsuri = XMLNS.BASE
+
+ standard_ns = {\
+ _xml_prefix:_xml_nsuri,
+ _xmlns_prefix:_xmlns_nsuri
+ }
+ reserved_ns = {\
+ _soap_env_prefix:_soap_env_nsuri,
+ _soap_enc_prefix:_soap_enc_nsuri,
+ _zsi_prefix:_zsi_nsuri,
+ _xsd_prefix:_xsd_nsuri,
+ _xsi_prefix:_xsi_nsuri,
+ }
+ name = None
+ namespaceURI = None
+
+ def __init__(self, sw, message=None):
+ '''Initialize.
+ sw -- SoapWriter
+ '''
+ self._indx = 0
+ MessageInterface.__init__(self, sw)
+ Base.__init__(self)
+ self._dom = DOM
+ self.node = None
+ if type(message) in (types.StringType,types.UnicodeType):
+ self.loadFromString(message)
+ elif isinstance(message, ElementProxy):
+ self.node = message._getNode()
+ else:
+ self.node = message
+ self.processorNss = self.standard_ns.copy()
+ self.processorNss.update(self.reserved_ns)
+
+ def __str__(self):
+ return self.toString()
+
+ def evaluate(self, expression, processorNss=None):
+ '''expression -- XPath compiled expression
+ '''
+ from Ft.Xml import XPath
+ if not processorNss:
+ context = XPath.Context.Context(self.node, processorNss=self.processorNss)
+ else:
+ context = XPath.Context.Context(self.node, processorNss=processorNss)
+ nodes = expression.evaluate(context)
+ return map(lambda node: ElementProxy(self.sw,node), nodes)
+
+ #############################################
+ # Methods for checking/setting the
+ # classes (namespaceURI,name) node.
+ #############################################
+ def checkNode(self, namespaceURI=None, localName=None):
+ '''
+ namespaceURI -- namespace of element
+ localName -- local name of element
+ '''
+ namespaceURI = namespaceURI or self.namespaceURI
+ localName = localName or self.name
+ check = False
+ if localName and self.node:
+ check = self._dom.isElement(self.node, localName, namespaceURI)
+ if not check:
+ raise NamespaceError, 'unexpected node type %s, expecting %s' %(self.node, localName)
+
+ def setNode(self, node=None):
+ if node:
+ if isinstance(node, ElementProxy):
+ self.node = node._getNode()
+ else:
+ self.node = node
+ elif self.node:
+ node = self._dom.getElement(self.node, self.name, self.namespaceURI, default=None)
+ if not node:
+ raise NamespaceError, 'cant find element (%s,%s)' %(self.namespaceURI,self.name)
+ self.node = node
+ else:
+ #self.node = self._dom.create(self.node, self.name, self.namespaceURI, default=None)
+ self.createDocument(self.namespaceURI, localName=self.name, doctype=None)
+
+ self.checkNode()
+
+ #############################################
+ # Wrapper Methods for direct DOM Element Node access
+ #############################################
+ def _getNode(self):
+ return self.node
+
+ def _getElements(self):
+ return self._dom.getElements(self.node, name=None)
+
+ def _getOwnerDocument(self):
+ return self.node.ownerDocument or self.node
+
+ def _getUniquePrefix(self):
+ '''I guess we need to resolve all potential prefixes
+ because when the current node is attached it copies the
+ namespaces into the parent node.
+ '''
+ while 1:
+ self._indx += 1
+ prefix = 'ns%d' %self._indx
+ try:
+ self._dom.findNamespaceURI(prefix, self._getNode())
+ except DOMException, ex:
+ break
+ return prefix
+
+ def _getPrefix(self, node, nsuri):
+ '''
+ Keyword arguments:
+ node -- DOM Element Node
+ nsuri -- namespace of attribute value
+ '''
+ try:
+ if node and (node.nodeType == node.ELEMENT_NODE) and \
+ (nsuri == self._dom.findDefaultNS(node)):
+ return None
+ except DOMException, ex:
+ pass
+ if nsuri == XMLNS.XML:
+ return self._xml_prefix
+ if node.nodeType == Node.ELEMENT_NODE:
+ for attr in node.attributes.values():
+ if attr.namespaceURI == XMLNS.BASE \
+ and nsuri == attr.value:
+ return attr.localName
+ else:
+ if node.parentNode:
+ return self._getPrefix(node.parentNode, nsuri)
+ raise NamespaceError, 'namespaceURI "%s" is not defined' %nsuri
+
+ def _appendChild(self, node):
+ '''
+ Keyword arguments:
+ node -- DOM Element Node
+ '''
+ if node is None:
+ raise TypeError, 'node is None'
+ self.node.appendChild(node)
+
+ def _insertBefore(self, newChild, refChild):
+ '''
+ Keyword arguments:
+ child -- DOM Element Node to insert
+ refChild -- DOM Element Node
+ '''
+ self.node.insertBefore(newChild, refChild)
+
+ def _setAttributeNS(self, namespaceURI, qualifiedName, value):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of attribute
+ qualifiedName -- qualified name of new attribute value
+ value -- value of attribute
+ '''
+ self.node.setAttributeNS(namespaceURI, qualifiedName, value)
+
+ #############################################
+ #General Methods
+ #############################################
+ def isFault(self):
+ '''check to see if this is a soap:fault message.
+ '''
+ return False
+
+ def getPrefix(self, namespaceURI):
+ try:
+ prefix = self._getPrefix(node=self.node, nsuri=namespaceURI)
+ except NamespaceError, ex:
+ prefix = self._getUniquePrefix()
+ self.setNamespaceAttribute(prefix, namespaceURI)
+ return prefix
+
+ def getDocument(self):
+ return self._getOwnerDocument()
+
+ def setDocument(self, document):
+ self.node = document
+
+ def importFromString(self, xmlString):
+ doc = self._dom.loadDocument(StringIO(xmlString))
+ node = self._dom.getElement(doc, name=None)
+ clone = self.importNode(node)
+ self._appendChild(clone)
+
+ def importNode(self, node):
+ if isinstance(node, ElementProxy):
+ node = node._getNode()
+ return self._dom.importNode(self._getOwnerDocument(), node, deep=1)
+
+ def loadFromString(self, data):
+ self.node = self._dom.loadDocument(StringIO(data))
+
+ def canonicalize(self):
+ return Canonicalize(self.node)
+
+ def toString(self):
+ return self.canonicalize()
+
+ def createDocument(self, namespaceURI, localName, doctype=None):
+ '''If specified must be a SOAP envelope, else may contruct an empty document.
+ '''
+ prefix = self._soap_env_prefix
+
+ if namespaceURI == self.reserved_ns[prefix]:
+ qualifiedName = '%s:%s' %(prefix,localName)
+ elif namespaceURI is localName is None:
+ self.node = self._dom.createDocument(None,None,None)
+ return
+ else:
+ raise KeyError, 'only support creation of document in %s' %self.reserved_ns[prefix]
+
+ document = self._dom.createDocument(nsuri=namespaceURI, qname=qualifiedName, doctype=doctype)
+ self.node = document.childNodes[0]
+
+ #set up reserved namespace attributes
+ for prefix,nsuri in self.reserved_ns.items():
+ self._setAttributeNS(namespaceURI=self._xmlns_nsuri,
+ qualifiedName='%s:%s' %(self._xmlns_prefix,prefix),
+ value=nsuri)
+
+ #############################################
+ #Methods for attributes
+ #############################################
+ def hasAttribute(self, namespaceURI, localName):
+ return self._dom.hasAttr(self._getNode(), name=localName, nsuri=namespaceURI)
+
+ def setAttributeType(self, namespaceURI, localName):
+ '''set xsi:type
+ Keyword arguments:
+ namespaceURI -- namespace of attribute value
+ localName -- name of new attribute value
+
+ '''
+ self.logger.debug('setAttributeType: (%s,%s)', namespaceURI, localName)
+ value = localName
+ if namespaceURI:
+ value = '%s:%s' %(self.getPrefix(namespaceURI),localName)
+
+ xsi_prefix = self.getPrefix(self._xsi_nsuri)
+ self._setAttributeNS(self._xsi_nsuri, '%s:type' %xsi_prefix, value)
+
+ def createAttributeNS(self, namespace, name, value):
+ document = self._getOwnerDocument()
+ attrNode = document.createAttributeNS(namespace, name, value)
+
+ def setAttributeNS(self, namespaceURI, localName, value):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of attribute to create, None is for
+ attributes in no namespace.
+ localName -- local name of new attribute
+ value -- value of new attribute
+ '''
+ prefix = None
+ if namespaceURI:
+ try:
+ prefix = self.getPrefix(namespaceURI)
+ except KeyError, ex:
+ prefix = 'ns2'
+ self.setNamespaceAttribute(prefix, namespaceURI)
+ qualifiedName = localName
+ if prefix:
+ qualifiedName = '%s:%s' %(prefix, localName)
+ self._setAttributeNS(namespaceURI, qualifiedName, value)
+
+ def setNamespaceAttribute(self, prefix, namespaceURI):
+ '''
+ Keyword arguments:
+ prefix -- xmlns prefix
+ namespaceURI -- value of prefix
+ '''
+ self._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI)
+
+ #############################################
+ #Methods for elements
+ #############################################
+ def createElementNS(self, namespace, qname):
+ '''
+ Keyword arguments:
+ namespace -- namespace of element to create
+ qname -- qualified name of new element
+ '''
+ document = self._getOwnerDocument()
+ node = document.createElementNS(namespace, qname)
+ return ElementProxy(self.sw, node)
+
+ def createAppendSetElement(self, namespaceURI, localName, prefix=None):
+ '''Create a new element (namespaceURI,name), append it
+ to current node, then set it to be the current node.
+ Keyword arguments:
+ namespaceURI -- namespace of element to create
+ localName -- local name of new element
+ prefix -- if namespaceURI is not defined, declare prefix. defaults
+ to 'ns1' if left unspecified.
+ '''
+ node = self.createAppendElement(namespaceURI, localName, prefix=None)
+ node=node._getNode()
+ self._setNode(node._getNode())
+
+ def createAppendElement(self, namespaceURI, localName, prefix=None):
+ '''Create a new element (namespaceURI,name), append it
+ to current node, and return the newly created node.
+ Keyword arguments:
+ namespaceURI -- namespace of element to create
+ localName -- local name of new element
+ prefix -- if namespaceURI is not defined, declare prefix. defaults
+ to 'ns1' if left unspecified.
+ '''
+ declare = False
+ qualifiedName = localName
+ if namespaceURI:
+ try:
+ prefix = self.getPrefix(namespaceURI)
+ except:
+ declare = True
+ prefix = prefix or self._getUniquePrefix()
+ if prefix:
+ qualifiedName = '%s:%s' %(prefix, localName)
+ node = self.createElementNS(namespaceURI, qualifiedName)
+ if declare:
+ node._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI)
+ self._appendChild(node=node._getNode())
+ return node
+
+ def createInsertBefore(self, namespaceURI, localName, refChild):
+ qualifiedName = localName
+ prefix = self.getPrefix(namespaceURI)
+ if prefix:
+ qualifiedName = '%s:%s' %(prefix, localName)
+ node = self.createElementNS(namespaceURI, qualifiedName)
+ self._insertBefore(newChild=node._getNode(), refChild=refChild._getNode())
+ return node
+
+ def getElement(self, namespaceURI, localName):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of element
+ localName -- local name of element
+ '''
+ node = self._dom.getElement(self.node, localName, namespaceURI, default=None)
+ if node:
+ return ElementProxy(self.sw, node)
+ return None
+
+ def getAttributeValue(self, namespaceURI, localName):
+ '''
+ Keyword arguments:
+ namespaceURI -- namespace of attribute
+ localName -- local name of attribute
+ '''
+ if self.hasAttribute(namespaceURI, localName):
+ attr = self.node.getAttributeNodeNS(namespaceURI,localName)
+ return attr.value
+ return None
+
+ def getValue(self):
+ return self._dom.getElementText(self.node, preserve_ws=True)
+
+ #############################################
+ #Methods for text nodes
+ #############################################
+ def createAppendTextNode(self, pyobj):
+ node = self.createTextNode(pyobj)
+ self._appendChild(node=node._getNode())
+ return node
+
+ def createTextNode(self, pyobj):
+ document = self._getOwnerDocument()
+ node = document.createTextNode(pyobj)
+ return ElementProxy(self.sw, node)
+
+ #############################################
+ #Methods for retrieving namespaceURI's
+ #############################################
+ def findNamespaceURI(self, qualifiedName):
+ parts = SplitQName(qualifiedName)
+ element = self._getNode()
+ if len(parts) == 1:
+ return (self._dom.findTargetNS(element), value)
+ return self._dom.findNamespaceURI(parts[0], element)
+
+ def resolvePrefix(self, prefix):
+ element = self._getNode()
+ return self._dom.findNamespaceURI(prefix, element)
+
+ def getSOAPEnvURI(self):
+ return self._soap_env_nsuri
+
+ def isEmpty(self):
+ return not self.node
+
+
+
+class Collection(UserDict):
+ """Helper class for maintaining ordered named collections."""
+ default = lambda self,k: k.name
+ def __init__(self, parent, key=None):
+ UserDict.__init__(self)
+ self.parent = weakref.ref(parent)
+ self.list = []
+ self._func = key or self.default
+
+ def __getitem__(self, key):
+ if type(key) is type(1):
+ return self.list[key]
+ return self.data[key]
+
+ def __setitem__(self, key, item):
+ item.parent = weakref.ref(self)
+ self.list.append(item)
+ self.data[key] = item
+
+ def keys(self):
+ return map(lambda i: self._func(i), self.list)
+
+ def items(self):
+ return map(lambda i: (self._func(i), i), self.list)
+
+ def values(self):
+ return self.list
+
+
+class CollectionNS(UserDict):
+ """Helper class for maintaining ordered named collections."""
+ default = lambda self,k: k.name
+ def __init__(self, parent, key=None):
+ UserDict.__init__(self)
+ self.parent = weakref.ref(parent)
+ self.targetNamespace = None
+ self.list = []
+ self._func = key or self.default
+
+ def __getitem__(self, key):
+ self.targetNamespace = self.parent().targetNamespace
+ if type(key) is types.IntType:
+ return self.list[key]
+ elif self.__isSequence(key):
+ nsuri,name = key
+ return self.data[nsuri][name]
+ return self.data[self.parent().targetNamespace][key]
+
+ def __setitem__(self, key, item):
+ item.parent = weakref.ref(self)
+ self.list.append(item)
+ targetNamespace = getattr(item, 'targetNamespace', self.parent().targetNamespace)
+ if not self.data.has_key(targetNamespace):
+ self.data[targetNamespace] = {}
+ self.data[targetNamespace][key] = item
+
+ def __isSequence(self, key):
+ return (type(key) in (types.TupleType,types.ListType) and len(key) == 2)
+
+ def keys(self):
+ keys = []
+ for tns in self.data.keys():
+ keys.append(map(lambda i: (tns,self._func(i)), self.data[tns].values()))
+ return keys
+
+ def items(self):
+ return map(lambda i: (self._func(i), i), self.list)
+
+ def values(self):
+ return self.list
+
+
+
+# This is a runtime guerilla patch for pulldom (used by minidom) so
+# that xml namespace declaration attributes are not lost in parsing.
+# We need them to do correct QName linking for XML Schema and WSDL.
+# The patch has been submitted to SF for the next Python version.
+
+from xml.dom.pulldom import PullDOM, START_ELEMENT
+if 1:
+ def startPrefixMapping(self, prefix, uri):
+ if not hasattr(self, '_xmlns_attrs'):
+ self._xmlns_attrs = []
+ self._xmlns_attrs.append((prefix or 'xmlns', uri))
+ self._ns_contexts.append(self._current_context.copy())
+ self._current_context[uri] = prefix or ''
+
+ PullDOM.startPrefixMapping = startPrefixMapping
+
+ def startElementNS(self, name, tagName , attrs):
+ # Retrieve xml namespace declaration attributes.
+ xmlns_uri = 'http://www.w3.org/2000/xmlns/'
+ xmlns_attrs = getattr(self, '_xmlns_attrs', None)
+ if xmlns_attrs is not None:
+ for aname, value in xmlns_attrs:
+ attrs._attrs[(xmlns_uri, aname)] = value
+ self._xmlns_attrs = []
+ uri, localname = name
+ if uri:
+ # When using namespaces, the reader may or may not
+ # provide us with the original name. If not, create
+ # *a* valid tagName from the current context.
+ if tagName is None:
+ prefix = self._current_context[uri]
+ if prefix:
+ tagName = prefix + ":" + localname
+ else:
+ tagName = localname
+ if self.document:
+ node = self.document.createElementNS(uri, tagName)
+ else:
+ node = self.buildDocument(uri, tagName)
+ else:
+ # When the tagname is not prefixed, it just appears as
+ # localname
+ if self.document:
+ node = self.document.createElement(localname)
+ else:
+ node = self.buildDocument(None, localname)
+
+ for aname,value in attrs.items():
+ a_uri, a_localname = aname
+ if a_uri == xmlns_uri:
+ if a_localname == 'xmlns':
+ qname = a_localname
+ else:
+ qname = 'xmlns:' + a_localname
+ attr = self.document.createAttributeNS(a_uri, qname)
+ node.setAttributeNodeNS(attr)
+ elif a_uri:
+ prefix = self._current_context[a_uri]
+ if prefix:
+ qname = prefix + ":" + a_localname
+ else:
+ qname = a_localname
+ attr = self.document.createAttributeNS(a_uri, qname)
+ node.setAttributeNodeNS(attr)
+ else:
+ attr = self.document.createAttribute(a_localname)
+ node.setAttributeNode(attr)
+ attr.value = value
+
+ self.lastEvent[1] = [(START_ELEMENT, node), None]
+ self.lastEvent = self.lastEvent[1]
+ self.push(node)
+
+ PullDOM.startElementNS = startElementNS
+
+#
+# This is a runtime guerilla patch for minidom so
+# that xmlns prefixed attributes dont raise AttributeErrors
+# during cloning.
+#
+# Namespace declarations can appear in any start-tag, must look for xmlns
+# prefixed attribute names during cloning.
+#
+# key (attr.namespaceURI, tag)
+# ('http://www.w3.org/2000/xmlns/', u'xsd') <xml.dom.minidom.Attr instance at 0x82227c4>
+# ('http://www.w3.org/2000/xmlns/', 'xmlns') <xml.dom.minidom.Attr instance at 0x8414b3c>
+#
+# xml.dom.minidom.Attr.nodeName = xmlns:xsd
+# xml.dom.minidom.Attr.value = = http://www.w3.org/2001/XMLSchema
+
+if 1:
+ def _clone_node(node, deep, newOwnerDocument):
+ """
+ Clone a node and give it the new owner document.
+ Called by Node.cloneNode and Document.importNode
+ """
+ if node.ownerDocument.isSameNode(newOwnerDocument):
+ operation = xml.dom.UserDataHandler.NODE_CLONED
+ else:
+ operation = xml.dom.UserDataHandler.NODE_IMPORTED
+ if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE:
+ clone = newOwnerDocument.createElementNS(node.namespaceURI,
+ node.nodeName)
+ for attr in node.attributes.values():
+ clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value)
+
+ prefix, tag = xml.dom.minidom._nssplit(attr.nodeName)
+ if prefix == 'xmlns':
+ a = clone.getAttributeNodeNS(attr.namespaceURI, tag)
+ elif prefix:
+ a = clone.getAttributeNodeNS(attr.namespaceURI, tag)
+ else:
+ a = clone.getAttributeNodeNS(attr.namespaceURI, attr.nodeName)
+ a.specified = attr.specified
+
+ if deep:
+ for child in node.childNodes:
+ c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument)
+ clone.appendChild(c)
+ elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_FRAGMENT_NODE:
+ clone = newOwnerDocument.createDocumentFragment()
+ if deep:
+ for child in node.childNodes:
+ c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument)
+ clone.appendChild(c)
+
+ elif node.nodeType == xml.dom.minidom.Node.TEXT_NODE:
+ clone = newOwnerDocument.createTextNode(node.data)
+ elif node.nodeType == xml.dom.minidom.Node.CDATA_SECTION_NODE:
+ clone = newOwnerDocument.createCDATASection(node.data)
+ elif node.nodeType == xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE:
+ clone = newOwnerDocument.createProcessingInstruction(node.target,
+ node.data)
+ elif node.nodeType == xml.dom.minidom.Node.COMMENT_NODE:
+ clone = newOwnerDocument.createComment(node.data)
+ elif node.nodeType == xml.dom.minidom.Node.ATTRIBUTE_NODE:
+ clone = newOwnerDocument.createAttributeNS(node.namespaceURI,
+ node.nodeName)
+ clone.specified = True
+ clone.value = node.value
+ elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_TYPE_NODE:
+ assert node.ownerDocument is not newOwnerDocument
+ operation = xml.dom.UserDataHandler.NODE_IMPORTED
+ clone = newOwnerDocument.implementation.createDocumentType(
+ node.name, node.publicId, node.systemId)
+ clone.ownerDocument = newOwnerDocument
+ if deep:
+ clone.entities._seq = []
+ clone.notations._seq = []
+ for n in node.notations._seq:
+ notation = xml.dom.minidom.Notation(n.nodeName, n.publicId, n.systemId)
+ notation.ownerDocument = newOwnerDocument
+ clone.notations._seq.append(notation)
+ if hasattr(n, '_call_user_data_handler'):
+ n._call_user_data_handler(operation, n, notation)
+ for e in node.entities._seq:
+ entity = xml.dom.minidom.Entity(e.nodeName, e.publicId, e.systemId,
+ e.notationName)
+ entity.actualEncoding = e.actualEncoding
+ entity.encoding = e.encoding
+ entity.version = e.version
+ entity.ownerDocument = newOwnerDocument
+ clone.entities._seq.append(entity)
+ if hasattr(e, '_call_user_data_handler'):
+ e._call_user_data_handler(operation, n, entity)
+ else:
+ # Note the cloning of Document and DocumentType nodes is
+ # implemenetation specific. minidom handles those cases
+ # directly in the cloneNode() methods.
+ raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node))
+
+ # Check for _call_user_data_handler() since this could conceivably
+ # used with other DOM implementations (one of the FourThought
+ # DOMs, perhaps?).
+ if hasattr(node, '_call_user_data_handler'):
+ node._call_user_data_handler(operation, node, clone)
+ return clone
+
+ xml.dom.minidom._clone_node = _clone_node
+
diff --git a/ZSI/zsi/ZSI/wstools/WSDLTools.py b/ZSI/zsi/ZSI/wstools/WSDLTools.py
new file mode 100755
index 0000000..c37d0d8
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/WSDLTools.py
@@ -0,0 +1,1668 @@
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+
+ident = "$Id: WSDLTools.py 1122 2006-02-04 01:24:50Z boverhof $"
+
+import weakref
+from cStringIO import StringIO
+from Namespaces import OASIS, XMLNS, WSA, WSA_LIST, WSRF_V1_2, WSRF
+from Utility import Collection, CollectionNS, DOM, ElementProxy, basejoin
+from XMLSchema import XMLSchema, SchemaReader, WSDLToolsAdapter
+
+
+class WSDLReader:
+ """A WSDLReader creates WSDL instances from urls and xml data."""
+
+ # Custom subclasses of WSDLReader may wish to implement a caching
+ # strategy or other optimizations. Because application needs vary
+ # so widely, we don't try to provide any caching by default.
+
+ def loadFromStream(self, stream, name=None):
+ """Return a WSDL instance loaded from a stream object."""
+ document = DOM.loadDocument(stream)
+ wsdl = WSDL()
+ if name:
+ wsdl.location = name
+ elif hasattr(stream, 'name'):
+ wsdl.location = stream.name
+ wsdl.load(document)
+ return wsdl
+
+ def loadFromURL(self, url):
+ """Return a WSDL instance loaded from the given url."""
+ document = DOM.loadFromURL(url)
+ wsdl = WSDL()
+ wsdl.location = url
+ wsdl.load(document)
+ return wsdl
+
+ def loadFromString(self, data):
+ """Return a WSDL instance loaded from an xml string."""
+ return self.loadFromStream(StringIO(data))
+
+ def loadFromFile(self, filename):
+ """Return a WSDL instance loaded from the given file."""
+ file = open(filename, 'rb')
+ try:
+ wsdl = self.loadFromStream(file)
+ finally:
+ file.close()
+ return wsdl
+
+class WSDL:
+ """A WSDL object models a WSDL service description. WSDL objects
+ may be created manually or loaded from an xml representation
+ using a WSDLReader instance."""
+
+ def __init__(self, targetNamespace=None, strict=1):
+ self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
+ self.documentation = ''
+ self.location = None
+ self.document = None
+ self.name = None
+ self.services = CollectionNS(self)
+ self.messages = CollectionNS(self)
+ self.portTypes = CollectionNS(self)
+ self.bindings = CollectionNS(self)
+ self.imports = Collection(self)
+ self.types = Types(self)
+ self.extensions = []
+ self.strict = strict
+
+ def __del__(self):
+ if self.document is not None:
+ self.document.unlink()
+
+ version = '1.1'
+
+ def addService(self, name, documentation='', targetNamespace=None):
+ if self.services.has_key(name):
+ raise WSDLError(
+ 'Duplicate service element: %s' % name
+ )
+ item = Service(name, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.services[name] = item
+ return item
+
+ def addMessage(self, name, documentation='', targetNamespace=None):
+ if self.messages.has_key(name):
+ raise WSDLError(
+ 'Duplicate message element: %s.' % name
+ )
+ item = Message(name, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.messages[name] = item
+ return item
+
+ def addPortType(self, name, documentation='', targetNamespace=None):
+ if self.portTypes.has_key(name):
+ raise WSDLError(
+ 'Duplicate portType element: name'
+ )
+ item = PortType(name, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.portTypes[name] = item
+ return item
+
+ def addBinding(self, name, type, documentation='', targetNamespace=None):
+ if self.bindings.has_key(name):
+ raise WSDLError(
+ 'Duplicate binding element: %s' % name
+ )
+ item = Binding(name, type, documentation)
+ if targetNamespace:
+ item.targetNamespace = targetNamespace
+ self.bindings[name] = item
+ return item
+
+ def addImport(self, namespace, location):
+ item = ImportElement(namespace, location)
+ self.imports[namespace] = item
+ return item
+
+ def toDom(self):
+ """ Generate a DOM representation of the WSDL instance.
+ Not dealing with generating XML Schema, thus the targetNamespace
+ of all XML Schema elements or types used by WSDL message parts
+ needs to be specified via import information items.
+ """
+ namespaceURI = DOM.GetWSDLUri(self.version)
+ self.document = DOM.createDocument(namespaceURI ,'wsdl:definitions')
+
+ # Set up a couple prefixes for easy reading.
+ child = DOM.getElement(self.document, None)
+ child.setAttributeNS(None, 'targetNamespace', self.targetNamespace)
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:wsdl', namespaceURI)
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:xsd', 'http://www.w3.org/1999/XMLSchema')
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:soap', 'http://schemas.xmlsoap.org/wsdl/soap/')
+ child.setAttributeNS(XMLNS.BASE, 'xmlns:tns', self.targetNamespace)
+
+ if self.name:
+ child.setAttributeNS(None, 'name', self.name)
+
+ # wsdl:import
+ for item in self.imports:
+ item.toDom()
+ # wsdl:message
+ for item in self.messages:
+ item.toDom()
+ # wsdl:portType
+ for item in self.portTypes:
+ item.toDom()
+ # wsdl:binding
+ for item in self.bindings:
+ item.toDom()
+ # wsdl:service
+ for item in self.services:
+ item.toDom()
+
+ def load(self, document):
+ # We save a reference to the DOM document to ensure that elements
+ # saved as "extensions" will continue to have a meaningful context
+ # for things like namespace references. The lifetime of the DOM
+ # document is bound to the lifetime of the WSDL instance.
+ self.document = document
+
+ definitions = DOM.getElement(document, 'definitions', None, None)
+ if definitions is None:
+ raise WSDLError(
+ 'Missing <definitions> element.'
+ )
+ self.version = DOM.WSDLUriToVersion(definitions.namespaceURI)
+ NS_WSDL = DOM.GetWSDLUri(self.version)
+
+ self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace',
+ None, None)
+ self.name = DOM.getAttr(definitions, 'name', None, None)
+ self.documentation = GetDocumentation(definitions)
+
+ #
+ # Retrieve all <wsdl:import>'s, append all children of imported
+ # document to main document. First iteration grab all original
+ # <wsdl:import>'s from document, second iteration grab all
+ # "imported" <wsdl:imports> from document, etc break out when
+ # no more <wsdl:import>'s.
+ #
+ imported = []
+ base_location = self.location
+ do_it = True
+ while do_it:
+ do_it = False
+ for element in DOM.getElements(definitions, 'import', NS_WSDL):
+ location = DOM.getAttr(element, 'location')
+
+ if base_location is not None:
+ location = basejoin(base_location, location)
+
+ if location not in imported:
+ do_it = True
+ self._import(document, element, base_location)
+ imported.append(location)
+ else:
+ definitions.removeChild(element)
+
+ base_location = None
+
+ #
+ # No more <wsdl:import>'s, now load up all other
+ # WSDL information items.
+ #
+ for element in DOM.getElements(definitions, None, None):
+ targetNamespace = DOM.getAttr(element, 'targetNamespace')
+ localName = element.localName
+
+ if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
+ if localName == 'schema':
+ tns = DOM.getAttr(element, 'targetNamespace')
+ reader = SchemaReader(base_url=self.imports[tns].location)
+ schema = reader.loadFromNode(WSDLToolsAdapter(self),
+ element)
+# schema.setBaseUrl(self.location)
+ self.types.addSchema(schema)
+ else:
+ self.extensions.append(element)
+ continue
+
+ elif localName == 'message':
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ message = self.addMessage(name, docs, targetNamespace)
+ parts = DOM.getElements(element, 'part', NS_WSDL)
+ message.load(parts)
+ continue
+
+ elif localName == 'portType':
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ ptype = self.addPortType(name, docs, targetNamespace)
+ #operations = DOM.getElements(element, 'operation', NS_WSDL)
+ #ptype.load(operations)
+ ptype.load(element)
+ continue
+
+ elif localName == 'binding':
+ name = DOM.getAttr(element, 'name')
+ type = DOM.getAttr(element, 'type', default=None)
+ if type is None:
+ raise WSDLError(
+ 'Missing type attribute for binding %s.' % name
+ )
+ type = ParseQName(type, element)
+ docs = GetDocumentation(element)
+ binding = self.addBinding(name, type, docs, targetNamespace)
+ operations = DOM.getElements(element, 'operation', NS_WSDL)
+ binding.load(operations)
+ binding.load_ex(GetExtensions(element))
+ continue
+
+ elif localName == 'service':
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ service = self.addService(name, docs, targetNamespace)
+ ports = DOM.getElements(element, 'port', NS_WSDL)
+ service.load(ports)
+ service.load_ex(GetExtensions(element))
+ continue
+
+ elif localName == 'types':
+ self.types.documentation = GetDocumentation(element)
+ base_location = DOM.getAttr(element, 'base-location')
+ if base_location:
+ element.removeAttribute('base-location')
+ base_location = base_location or self.location
+ reader = SchemaReader(base_url=base_location)
+ for item in DOM.getElements(element, None, None):
+ if item.localName == 'schema':
+ schema = reader.loadFromNode(WSDLToolsAdapter(self), item)
+ # XXX <types> could have been imported
+ #schema.setBaseUrl(self.location)
+ schema.setBaseUrl(base_location)
+ self.types.addSchema(schema)
+ else:
+ self.types.addExtension(item)
+ # XXX remove the attribute
+ # element.removeAttribute('base-location')
+ continue
+
+ def _import(self, document, element, base_location=None):
+ '''Algo take <import> element's children, clone them,
+ and add them to the main document. Support for relative
+ locations is a bit complicated. The orig document context
+ is lost, so we need to store base location in DOM elements
+ representing <types>, by creating a special temporary
+ "base-location" attribute, and <import>, by resolving
+ the relative "location" and storing it as "location".
+
+ document -- document we are loading
+ element -- DOM Element representing <import>
+ base_location -- location of document from which this
+ <import> was gleaned.
+ '''
+ namespace = DOM.getAttr(element, 'namespace', default=None)
+ location = DOM.getAttr(element, 'location', default=None)
+ if namespace is None or location is None:
+ raise WSDLError(
+ 'Invalid import element (missing namespace or location).'
+ )
+ if base_location:
+ location = basejoin(base_location, location)
+ element.setAttributeNS(None, 'location', location)
+
+ obimport = self.addImport(namespace, location)
+ obimport._loaded = 1
+
+ importdoc = DOM.loadFromURL(location)
+ try:
+ if location.find('#') > -1:
+ idref = location.split('#')[-1]
+ imported = DOM.getElementById(importdoc, idref)
+ else:
+ imported = importdoc.documentElement
+ if imported is None:
+ raise WSDLError(
+ 'Import target element not found for: %s' % location
+ )
+
+ imported_tns = DOM.findTargetNS(imported)
+ if imported_tns != namespace:
+ return
+
+ if imported.localName == 'definitions':
+ imported_nodes = imported.childNodes
+ else:
+ imported_nodes = [imported]
+ parent = element.parentNode
+
+ parent.removeChild(element)
+
+ for node in imported_nodes:
+ if node.nodeType != node.ELEMENT_NODE:
+ continue
+ child = DOM.importNode(document, node, 1)
+ parent.appendChild(child)
+ child.setAttribute('targetNamespace', namespace)
+ attrsNS = imported._attrsNS
+ for attrkey in attrsNS.keys():
+ if attrkey[0] == DOM.NS_XMLNS:
+ attr = attrsNS[attrkey].cloneNode(1)
+ child.setAttributeNode(attr)
+
+ #XXX Quick Hack, should be in WSDL Namespace.
+ if child.localName == 'import':
+ rlocation = child.getAttributeNS(None, 'location')
+ alocation = basejoin(location, rlocation)
+ child.setAttribute('location', alocation)
+ elif child.localName == 'types':
+ child.setAttribute('base-location', location)
+
+ finally:
+ importdoc.unlink()
+ return location
+
+class Element:
+ """A class that provides common functions for WSDL element classes."""
+ def __init__(self, name=None, documentation=''):
+ self.name = name
+ self.documentation = documentation
+ self.extensions = []
+
+ def addExtension(self, item):
+ item.parent = weakref.ref(self)
+ self.extensions.append(item)
+
+ def getWSDL(self):
+ """Return the WSDL object that contains this information item."""
+ parent = self
+ while 1:
+ # skip any collections
+ if isinstance(parent, WSDL):
+ return parent
+ try: parent = parent.parent()
+ except: break
+
+ return None
+
+
+class ImportElement(Element):
+ def __init__(self, namespace, location):
+ self.namespace = namespace
+ self.location = location
+
+# def getWSDL(self):
+# """Return the WSDL object that contains this Message Part."""
+# return self.parent().parent()
+
+ def toDom(self):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'import')
+ epc.setAttributeNS(None, 'namespace', self.namespace)
+ epc.setAttributeNS(None, 'location', self.location)
+
+ _loaded = None
+
+
+class Types(Collection):
+ default = lambda self,k: k.targetNamespace
+ def __init__(self, parent):
+ Collection.__init__(self, parent)
+ self.documentation = ''
+ self.extensions = []
+
+ def addSchema(self, schema):
+ name = schema.targetNamespace
+ self[name] = schema
+ return schema
+
+ def addExtension(self, item):
+ self.extensions.append(item)
+
+
+class Message(Element):
+ def __init__(self, name, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.parts = Collection(self)
+
+ def addPart(self, name, type=None, element=None):
+ if self.parts.has_key(name):
+ raise WSDLError(
+ 'Duplicate message part element: %s' % name
+ )
+ if type is None and element is None:
+ raise WSDLError(
+ 'Missing type or element attribute for part: %s' % name
+ )
+ item = MessagePart(name)
+ item.element = element
+ item.type = type
+ self.parts[name] = item
+ return item
+
+ def load(self, elements):
+ for element in elements:
+ name = DOM.getAttr(element, 'name')
+ part = MessagePart(name)
+ self.parts[name] = part
+ elemref = DOM.getAttr(element, 'element', default=None)
+ typeref = DOM.getAttr(element, 'type', default=None)
+ if typeref is None and elemref is None:
+ raise WSDLError(
+ 'No type or element attribute for part: %s' % name
+ )
+ if typeref is not None:
+ part.type = ParseTypeRef(typeref, element)
+ if elemref is not None:
+ part.element = ParseTypeRef(elemref, element)
+
+# def getElementDeclaration(self):
+# """Return the XMLSchema.ElementDeclaration instance or None"""
+# element = None
+# if self.element:
+# nsuri,name = self.element
+# wsdl = self.getWSDL()
+# if wsdl.types.has_key(nsuri) and wsdl.types[nsuri].elements.has_key(name):
+# element = wsdl.types[nsuri].elements[name]
+# return element
+#
+# def getTypeDefinition(self):
+# """Return the XMLSchema.TypeDefinition instance or None"""
+# type = None
+# if self.type:
+# nsuri,name = self.type
+# wsdl = self.getWSDL()
+# if wsdl.types.has_key(nsuri) and wsdl.types[nsuri].types.has_key(name):
+# type = wsdl.types[nsuri].types[name]
+# return type
+
+# def getWSDL(self):
+# """Return the WSDL object that contains this Message Part."""
+# return self.parent().parent()
+
+ def toDom(self):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'message')
+ epc.setAttributeNS(None, 'name', self.name)
+
+ for part in self.parts:
+ part.toDom(epc._getNode())
+
+
+class MessagePart(Element):
+ def __init__(self, name):
+ Element.__init__(self, name, '')
+ self.element = None
+ self.type = None
+
+# def getWSDL(self):
+# """Return the WSDL object that contains this Message Part."""
+# return self.parent().parent().parent().parent()
+
+ def getTypeDefinition(self):
+ wsdl = self.getWSDL()
+ nsuri,name = self.type
+ schema = wsdl.types.get(nsuri, {})
+ return schema.get(name)
+
+ def getElementDeclaration(self):
+ wsdl = self.getWSDL()
+ nsuri,name = self.element
+ schema = wsdl.types.get(nsuri, {})
+ return schema.get(name)
+
+ def toDom(self, node):
+ """node -- node representing message"""
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'part')
+ epc.setAttributeNS(None, 'name', self.name)
+
+ if self.element is not None:
+ ns,name = self.element
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(None, 'element', '%s:%s'%(prefix,name))
+ elif self.type is not None:
+ ns,name = self.type
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(None, 'type', '%s:%s'%(prefix,name))
+
+
+class PortType(Element):
+ '''PortType has a anyAttribute, thus must provide for an extensible
+ mechanism for supporting such attributes. ResourceProperties is
+ specified in WS-ResourceProperties. wsa:Action is specified in
+ WS-Address.
+
+ Instance Data:
+ name -- name attribute
+ resourceProperties -- optional. wsr:ResourceProperties attribute,
+ value is a QName this is Parsed into a (namespaceURI, name)
+ that represents a Global Element Declaration.
+ operations
+ '''
+
+ def __init__(self, name, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.operations = Collection(self)
+ self.resourceProperties = None
+
+# def getWSDL(self):
+# return self.parent().parent()
+
+ def getTargetNamespace(self):
+ return self.targetNamespace or self.getWSDL().targetNamespace
+
+ def getResourceProperties(self):
+ return self.resourceProperties
+
+ def addOperation(self, name, documentation='', parameterOrder=None):
+ item = Operation(name, documentation, parameterOrder)
+ self.operations[name] = item
+ return item
+
+ def load(self, element):
+ self.name = DOM.getAttr(element, 'name')
+ self.documentation = GetDocumentation(element)
+ self.targetNamespace = DOM.getAttr(element, 'targetNamespace')
+
+ for nsuri in WSRF_V1_2.PROPERTIES.XSD_LIST:
+ if DOM.hasAttr(element, 'ResourceProperties', nsuri):
+ rpref = DOM.getAttr(element, 'ResourceProperties', nsuri)
+ self.resourceProperties = ParseQName(rpref, element)
+
+ NS_WSDL = DOM.GetWSDLUri(self.getWSDL().version)
+ elements = DOM.getElements(element, 'operation', NS_WSDL)
+ for element in elements:
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ param_order = DOM.getAttr(element, 'parameterOrder', default=None)
+ if param_order is not None:
+ param_order = param_order.split(' ')
+ operation = self.addOperation(name, docs, param_order)
+
+ item = DOM.getElement(element, 'input', None, None)
+ if item is not None:
+ name = DOM.getAttr(item, 'name')
+ docs = GetDocumentation(item)
+ msgref = DOM.getAttr(item, 'message')
+ message = ParseQName(msgref, item)
+ for WSA in WSA_LIST:
+ action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
+ if action: break
+ operation.setInput(message, name, docs, action)
+
+ item = DOM.getElement(element, 'output', None, None)
+ if item is not None:
+ name = DOM.getAttr(item, 'name')
+ docs = GetDocumentation(item)
+ msgref = DOM.getAttr(item, 'message')
+ message = ParseQName(msgref, item)
+ for WSA in WSA_LIST:
+ action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
+ if action: break
+ operation.setOutput(message, name, docs, action)
+
+ for item in DOM.getElements(element, 'fault', None):
+ name = DOM.getAttr(item, 'name')
+ docs = GetDocumentation(item)
+ msgref = DOM.getAttr(item, 'message')
+ message = ParseQName(msgref, item)
+ for WSA in WSA_LIST:
+ action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
+ if action: break
+ operation.addFault(message, name, docs, action)
+
+ def toDom(self):
+ wsdl = self.getWSDL()
+
+ ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'portType')
+ epc.setAttributeNS(None, 'name', self.name)
+ if self.resourceProperties:
+ ns,name = self.resourceProperties
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(WSRF.PROPERTIES.LATEST, 'ResourceProperties',
+ '%s:%s'%(prefix,name))
+
+ for op in self.operations:
+ op.toDom(epc._getNode())
+
+
+
+class Operation(Element):
+ def __init__(self, name, documentation='', parameterOrder=None):
+ Element.__init__(self, name, documentation)
+ self.parameterOrder = parameterOrder
+ self.faults = Collection(self)
+ self.input = None
+ self.output = None
+
+ def getWSDL(self):
+ """Return the WSDL object that contains this Operation."""
+ return self.parent().parent().parent().parent()
+
+ def getPortType(self):
+ return self.parent().parent()
+
+ def getInputAction(self):
+ """wsa:Action attribute"""
+ return GetWSAActionInput(self)
+
+ def getInputMessage(self):
+ if self.input is None:
+ return None
+ wsdl = self.getPortType().getWSDL()
+ return wsdl.messages[self.input.message]
+
+ def getOutputAction(self):
+ """wsa:Action attribute"""
+ return GetWSAActionOutput(self)
+
+ def getOutputMessage(self):
+ if self.output is None:
+ return None
+ wsdl = self.getPortType().getWSDL()
+ return wsdl.messages[self.output.message]
+
+ def getFaultAction(self, name):
+ """wsa:Action attribute"""
+ return GetWSAActionFault(self, name)
+
+ def getFaultMessage(self, name):
+ wsdl = self.getPortType().getWSDL()
+ return wsdl.messages[self.faults[name].message]
+
+ def addFault(self, message, name, documentation='', action=None):
+ if self.faults.has_key(name):
+ raise WSDLError(
+ 'Duplicate fault element: %s' % name
+ )
+ item = MessageRole('fault', message, name, documentation, action)
+ self.faults[name] = item
+ return item
+
+ def setInput(self, message, name='', documentation='', action=None):
+ self.input = MessageRole('input', message, name, documentation, action)
+ self.input.parent = weakref.ref(self)
+ return self.input
+
+ def setOutput(self, message, name='', documentation='', action=None):
+ self.output = MessageRole('output', message, name, documentation, action)
+ self.output.parent = weakref.ref(self)
+ return self.output
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'operation')
+ epc.setAttributeNS(None, 'name', self.name)
+ node = epc._getNode()
+ if self.input:
+ self.input.toDom(node)
+ if self.output:
+ self.output.toDom(node)
+ for fault in self.faults:
+ fault.toDom(node)
+
+
+class MessageRole(Element):
+ def __init__(self, type, message, name='', documentation='', action=None):
+ Element.__init__(self, name, documentation)
+ self.message = message
+ self.type = type
+ self.action = action
+
+ def getWSDL(self):
+ """Return the WSDL object that contains this information item."""
+ parent = self
+ while 1:
+ # skip any collections
+ if isinstance(parent, WSDL):
+ return parent
+ try: parent = parent.parent()
+ except: break
+
+ return None
+
+ def getMessage(self):
+ """Return the WSDL object that represents the attribute message
+ (namespaceURI, name) tuple
+ """
+ wsdl = self.getWSDL()
+ return wsdl.messages[self.message]
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), self.type)
+ if not isinstance(self.message, basestring) and len(self.message) == 2:
+ ns,name = self.message
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(None, 'message', '%s:%s' %(prefix,name))
+ else:
+ epc.setAttributeNS(None, 'message', self.message)
+
+ if self.action:
+ epc.setAttributeNS(WSA.ADDRESS, 'Action', self.action)
+
+ if self.name:
+ epc.setAttributeNS(None, 'name', self.name)
+
+
+class Binding(Element):
+ def __init__(self, name, type, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.operations = Collection(self)
+ self.type = type
+
+# def getWSDL(self):
+# """Return the WSDL object that contains this binding."""
+# return self.parent().parent()
+
+ def getPortType(self):
+ """Return the PortType object associated with this binding."""
+ return self.getWSDL().portTypes[self.type]
+
+ def findBinding(self, kind):
+ for item in self.extensions:
+ if isinstance(item, kind):
+ return item
+ return None
+
+ def findBindings(self, kind):
+ return [ item for item in self.extensions if isinstance(item, kind) ]
+
+ def addOperationBinding(self, name, documentation=''):
+ item = OperationBinding(name, documentation)
+ self.operations[name] = item
+ return item
+
+ def load(self, elements):
+ for element in elements:
+ name = DOM.getAttr(element, 'name')
+ docs = GetDocumentation(element)
+ opbinding = self.addOperationBinding(name, docs)
+ opbinding.load_ex(GetExtensions(element))
+
+ item = DOM.getElement(element, 'input', None, None)
+ if item is not None:
+ #TODO: addInputBinding?
+ mbinding = MessageRoleBinding('input')
+ mbinding.documentation = GetDocumentation(item)
+ opbinding.input = mbinding
+ mbinding.load_ex(GetExtensions(item))
+ mbinding.parent = weakref.ref(opbinding)
+
+ item = DOM.getElement(element, 'output', None, None)
+ if item is not None:
+ mbinding = MessageRoleBinding('output')
+ mbinding.documentation = GetDocumentation(item)
+ opbinding.output = mbinding
+ mbinding.load_ex(GetExtensions(item))
+ mbinding.parent = weakref.ref(opbinding)
+
+ for item in DOM.getElements(element, 'fault', None):
+ name = DOM.getAttr(item, 'name')
+ mbinding = MessageRoleBinding('fault', name)
+ mbinding.documentation = GetDocumentation(item)
+ opbinding.faults[name] = mbinding
+ mbinding.load_ex(GetExtensions(item))
+ mbinding.parent = weakref.ref(opbinding)
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
+ transport = DOM.getAttr(e, 'transport', default=None)
+ style = DOM.getAttr(e, 'style', default='document')
+ ob = SoapBinding(transport, style)
+ self.addExtension(ob)
+ continue
+ elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
+ verb = DOM.getAttr(e, 'verb')
+ ob = HttpBinding(verb)
+ self.addExtension(ob)
+ continue
+ else:
+ self.addExtension(e)
+
+ def toDom(self):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'binding')
+ epc.setAttributeNS(None, 'name', self.name)
+
+ ns,name = self.type
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(None, 'type', '%s:%s' %(prefix,name))
+
+ node = epc._getNode()
+ for ext in self.extensions:
+ ext.toDom(node)
+ for op_binding in self.operations:
+ op_binding.toDom(node)
+
+
+class OperationBinding(Element):
+ def __init__(self, name, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.input = None
+ self.output = None
+ self.faults = Collection(self)
+
+# def getWSDL(self):
+# """Return the WSDL object that contains this binding."""
+# return self.parent().parent().parent().parent()
+
+
+ def getBinding(self):
+ """Return the parent Binding object of the operation binding."""
+ return self.parent().parent()
+
+ def getOperation(self):
+ """Return the abstract Operation associated with this binding."""
+ return self.getBinding().getPortType().operations[self.name]
+
+ def findBinding(self, kind):
+ for item in self.extensions:
+ if isinstance(item, kind):
+ return item
+ return None
+
+ def findBindings(self, kind):
+ return [ item for item in self.extensions if isinstance(item, kind) ]
+
+ def addInputBinding(self, binding):
+ if self.input is None:
+ self.input = MessageRoleBinding('input')
+ self.input.parent = weakref.ref(self)
+ self.input.addExtension(binding)
+ return binding
+
+ def addOutputBinding(self, binding):
+ if self.output is None:
+ self.output = MessageRoleBinding('output')
+ self.output.parent = weakref.ref(self)
+ self.output.addExtension(binding)
+ return binding
+
+ def addFaultBinding(self, name, binding):
+ fault = self.get(name, None)
+ if fault is None:
+ fault = MessageRoleBinding('fault', name)
+ fault.addExtension(binding)
+ return binding
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
+ soapaction = DOM.getAttr(e, 'soapAction', default=None)
+ style = DOM.getAttr(e, 'style', default=None)
+ ob = SoapOperationBinding(soapaction, style)
+ self.addExtension(ob)
+ continue
+ elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
+ location = DOM.getAttr(e, 'location')
+ ob = HttpOperationBinding(location)
+ self.addExtension(ob)
+ continue
+ else:
+ self.addExtension(e)
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'operation')
+ epc.setAttributeNS(None, 'name', self.name)
+
+ node = epc._getNode()
+ for ext in self.extensions:
+ ext.toDom(node)
+ if self.input:
+ self.input.toDom(node)
+ if self.output:
+ self.output.toDom(node)
+ for fault in self.faults:
+ fault.toDom(node)
+
+
+class MessageRoleBinding(Element):
+ def __init__(self, type, name='', documentation=''):
+ Element.__init__(self, name, documentation)
+ self.type = type
+
+ def findBinding(self, kind):
+ for item in self.extensions:
+ if isinstance(item, kind):
+ return item
+ return None
+
+ def findBindings(self, kind):
+ return [ item for item in self.extensions if isinstance(item, kind) ]
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
+ encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+ namespace = DOM.getAttr(e, 'namespace', default=None)
+ parts = DOM.getAttr(e, 'parts', default=None)
+ use = DOM.getAttr(e, 'use', default=None)
+ if use is None:
+ raise WSDLError(
+ 'Invalid soap:body binding element.'
+ )
+ ob = SoapBodyBinding(use, namespace, encstyle, parts)
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
+ encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+ namespace = DOM.getAttr(e, 'namespace', default=None)
+ name = DOM.getAttr(e, 'name', default=None)
+ use = DOM.getAttr(e, 'use', default=None)
+ if use is None or name is None:
+ raise WSDLError(
+ 'Invalid soap:fault binding element.'
+ )
+ ob = SoapFaultBinding(name, use, namespace, encstyle)
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
+ 'header', 'headerfault'
+ ):
+ encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+ namespace = DOM.getAttr(e, 'namespace', default=None)
+ message = DOM.getAttr(e, 'message')
+ part = DOM.getAttr(e, 'part')
+ use = DOM.getAttr(e, 'use')
+ if name == 'header':
+ _class = SoapHeaderBinding
+ else:
+ _class = SoapHeaderFaultBinding
+ message = ParseQName(message, e)
+ ob = _class(message, part, use, namespace, encstyle)
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
+ ob = HttpUrlReplacementBinding()
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
+ ob = HttpUrlEncodedBinding()
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
+ ob = MimeMultipartRelatedBinding()
+ self.addExtension(ob)
+ ob.load_ex(GetExtensions(e))
+ continue
+
+ elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
+ part = DOM.getAttr(e, 'part', default=None)
+ type = DOM.getAttr(e, 'type', default=None)
+ ob = MimeContentBinding(part, type)
+ self.addExtension(ob)
+ continue
+
+ elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
+ part = DOM.getAttr(e, 'part', default=None)
+ ob = MimeXmlBinding(part)
+ self.addExtension(ob)
+ continue
+
+ else:
+ self.addExtension(e)
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), self.type)
+
+ node = epc._getNode()
+ for item in self.extensions:
+ if item: item.toDom(node)
+
+
+class Service(Element):
+ def __init__(self, name, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.ports = Collection(self)
+
+ def getWSDL(self):
+ return self.parent().parent()
+
+ def addPort(self, name, binding, documentation=''):
+ item = Port(name, binding, documentation)
+ self.ports[name] = item
+ return item
+
+ def load(self, elements):
+ for element in elements:
+ name = DOM.getAttr(element, 'name', default=None)
+ docs = GetDocumentation(element)
+ binding = DOM.getAttr(element, 'binding', default=None)
+ if name is None or binding is None:
+ raise WSDLError(
+ 'Invalid port element.'
+ )
+ binding = ParseQName(binding, element)
+ port = self.addPort(name, binding, docs)
+ port.load_ex(GetExtensions(element))
+
+ def load_ex(self, elements):
+ for e in elements:
+ self.addExtension(e)
+
+ def toDom(self):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), "service")
+ epc.setAttributeNS(None, "name", self.name)
+
+ node = epc._getNode()
+ for port in self.ports:
+ port.toDom(node)
+
+
+class Port(Element):
+ def __init__(self, name, binding, documentation=''):
+ Element.__init__(self, name, documentation)
+ self.binding = binding
+
+# def getWSDL(self):
+# return self.parent().parent().getWSDL()
+
+ def getService(self):
+ """Return the Service object associated with this port."""
+ return self.parent().parent()
+
+ def getBinding(self):
+ """Return the Binding object that is referenced by this port."""
+ wsdl = self.getService().getWSDL()
+ return wsdl.bindings[self.binding]
+
+ def getPortType(self):
+ """Return the PortType object that is referenced by this port."""
+ wsdl = self.getService().getWSDL()
+ binding = wsdl.bindings[self.binding]
+ return wsdl.portTypes[binding.type]
+
+ def getAddressBinding(self):
+ """A convenience method to obtain the extension element used
+ as the address binding for the port."""
+ for item in self.extensions:
+ if isinstance(item, SoapAddressBinding) or \
+ isinstance(item, HttpAddressBinding):
+ return item
+ raise WSDLError(
+ 'No address binding found in port.'
+ )
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
+ location = DOM.getAttr(e, 'location', default=None)
+ ob = SoapAddressBinding(location)
+ self.addExtension(ob)
+ continue
+ elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
+ location = DOM.getAttr(e, 'location', default=None)
+ ob = HttpAddressBinding(location)
+ self.addExtension(ob)
+ continue
+ else:
+ self.addExtension(e)
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), "port")
+ epc.setAttributeNS(None, "name", self.name)
+
+ ns,name = self.binding
+ prefix = epc.getPrefix(ns)
+ epc.setAttributeNS(None, "binding", "%s:%s" %(prefix,name))
+
+ node = epc._getNode()
+ for ext in self.extensions:
+ ext.toDom(node)
+
+
+class SoapBinding:
+ def __init__(self, transport, style='rpc'):
+ self.transport = transport
+ self.style = style
+
+ def getWSDL(self):
+ return self.parent().getWSDL()
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'binding')
+ if self.transport:
+ epc.setAttributeNS(None, "transport", self.transport)
+ if self.style:
+ epc.setAttributeNS(None, "style", self.style)
+
+class SoapAddressBinding:
+ def __init__(self, location):
+ self.location = location
+
+ def getWSDL(self):
+ return self.parent().getWSDL()
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'address')
+ epc.setAttributeNS(None, "location", self.location)
+
+
+class SoapOperationBinding:
+ def __init__(self, soapAction=None, style=None):
+ self.soapAction = soapAction
+ self.style = style
+
+ def getWSDL(self):
+ return self.parent().getWSDL()
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'operation')
+ if self.soapAction:
+ epc.setAttributeNS(None, 'soapAction', self.soapAction)
+ if self.style:
+ epc.setAttributeNS(None, 'style', self.style)
+
+
+class SoapBodyBinding:
+ def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
+ if not use in ('literal', 'encoded'):
+ raise WSDLError(
+ 'Invalid use attribute value: %s' % use
+ )
+ self.encodingStyle = encodingStyle
+ self.namespace = namespace
+ if type(parts) in (type(''), type(u'')):
+ parts = parts.split()
+ self.parts = parts
+ self.use = use
+
+ def getWSDL(self):
+ return self.parent().getWSDL()
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'body')
+ epc.setAttributeNS(None, "use", self.use)
+ epc.setAttributeNS(None, "namespace", self.namespace)
+
+
+class SoapFaultBinding:
+ def __init__(self, name, use, namespace=None, encodingStyle=None):
+ if not use in ('literal', 'encoded'):
+ raise WSDLError(
+ 'Invalid use attribute value: %s' % use
+ )
+ self.encodingStyle = encodingStyle
+ self.namespace = namespace
+ self.name = name
+ self.use = use
+
+ def getWSDL(self):
+ return self.parent().getWSDL()
+
+ def toDom(self, node):
+ wsdl = self.getWSDL()
+ ep = ElementProxy(None, node)
+ epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'body')
+ epc.setAttributeNS(None, "use", self.use)
+ epc.setAttributeNS(None, "name", self.name)
+ if self.namespace is not None:
+ epc.setAttributeNS(None, "namespace", self.namespace)
+ if self.encodingStyle is not None:
+ epc.setAttributeNS(None, "encodingStyle", self.encodingStyle)
+
+
+class SoapHeaderBinding:
+ def __init__(self, message, part, use, namespace=None, encodingStyle=None):
+ if not use in ('literal', 'encoded'):
+ raise WSDLError(
+ 'Invalid use attribute value: %s' % use
+ )
+ self.encodingStyle = encodingStyle
+ self.namespace = namespace
+ self.message = message
+ self.part = part
+ self.use = use
+
+ tagname = 'header'
+
+class SoapHeaderFaultBinding(SoapHeaderBinding):
+ tagname = 'headerfault'
+
+
+class HttpBinding:
+ def __init__(self, verb):
+ self.verb = verb
+
+class HttpAddressBinding:
+ def __init__(self, location):
+ self.location = location
+
+
+class HttpOperationBinding:
+ def __init__(self, location):
+ self.location = location
+
+class HttpUrlReplacementBinding:
+ pass
+
+
+class HttpUrlEncodedBinding:
+ pass
+
+
+class MimeContentBinding:
+ def __init__(self, part=None, type=None):
+ self.part = part
+ self.type = type
+
+
+class MimeXmlBinding:
+ def __init__(self, part=None):
+ self.part = part
+
+
+class MimeMultipartRelatedBinding:
+ def __init__(self):
+ self.parts = []
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_MIME_BINDING_ALL and name == 'part':
+ self.parts.append(MimePartBinding())
+ continue
+
+
+class MimePartBinding:
+ def __init__(self):
+ self.items = []
+
+ def load_ex(self, elements):
+ for e in elements:
+ ns, name = e.namespaceURI, e.localName
+ if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
+ part = DOM.getAttr(e, 'part', default=None)
+ type = DOM.getAttr(e, 'type', default=None)
+ ob = MimeContentBinding(part, type)
+ self.items.append(ob)
+ continue
+
+ elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
+ part = DOM.getAttr(e, 'part', default=None)
+ ob = MimeXmlBinding(part)
+ self.items.append(ob)
+ continue
+
+ elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
+ encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+ namespace = DOM.getAttr(e, 'namespace', default=None)
+ parts = DOM.getAttr(e, 'parts', default=None)
+ use = DOM.getAttr(e, 'use', default=None)
+ if use is None:
+ raise WSDLError(
+ 'Invalid soap:body binding element.'
+ )
+ ob = SoapBodyBinding(use, namespace, encstyle, parts)
+ self.items.append(ob)
+ continue
+
+
+class WSDLError(Exception):
+ pass
+
+
+
+def DeclareNSPrefix(writer, prefix, nsuri):
+ if writer.hasNSPrefix(nsuri):
+ return
+ writer.declareNSPrefix(prefix, nsuri)
+
+def ParseTypeRef(value, element):
+ parts = value.split(':', 1)
+ if len(parts) == 1:
+ return (DOM.findTargetNS(element), value)
+ nsuri = DOM.findNamespaceURI(parts[0], element)
+ return (nsuri, parts[1])
+
+def ParseQName(value, element):
+ nameref = value.split(':', 1)
+ if len(nameref) == 2:
+ nsuri = DOM.findNamespaceURI(nameref[0], element)
+ name = nameref[-1]
+ else:
+ nsuri = DOM.findTargetNS(element)
+ name = nameref[-1]
+ return nsuri, name
+
+def GetDocumentation(element):
+ docnode = DOM.getElement(element, 'documentation', None, None)
+ if docnode is not None:
+ return DOM.getElementText(docnode)
+ return ''
+
+def GetExtensions(element):
+ return [ item for item in DOM.getElements(element, None, None)
+ if item.namespaceURI != DOM.NS_WSDL ]
+
+def GetWSAActionFault(operation, name):
+ """Find wsa:Action attribute, and return value or WSA.FAULT
+ for the default.
+ """
+ attr = operation.faults[name].action
+ if attr is not None:
+ return attr
+ return WSA.FAULT
+
+def GetWSAActionInput(operation):
+ """Find wsa:Action attribute, and return value or the default."""
+ attr = operation.input.action
+ if attr is not None:
+ return attr
+ portType = operation.getPortType()
+ targetNamespace = portType.getTargetNamespace()
+ ptName = portType.name
+ msgName = operation.input.name
+ if not msgName:
+ msgName = operation.name + 'Request'
+ if targetNamespace.endswith('/'):
+ return '%s%s/%s' %(targetNamespace, ptName, msgName)
+ return '%s/%s/%s' %(targetNamespace, ptName, msgName)
+
+def GetWSAActionOutput(operation):
+ """Find wsa:Action attribute, and return value or the default."""
+ attr = operation.output.action
+ if attr is not None:
+ return attr
+ targetNamespace = operation.getPortType().getTargetNamespace()
+ ptName = operation.getPortType().name
+ msgName = operation.output.name
+ if not msgName:
+ msgName = operation.name + 'Response'
+ if targetNamespace.endswith('/'):
+ return '%s%s/%s' %(targetNamespace, ptName, msgName)
+ return '%s/%s/%s' %(targetNamespace, ptName, msgName)
+
+def FindExtensions(object, kind, t_type=type(())):
+ if isinstance(kind, t_type):
+ result = []
+ namespaceURI, name = kind
+ return [ item for item in object.extensions
+ if hasattr(item, 'nodeType') \
+ and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
+ and item.name == name ]
+ return [ item for item in object.extensions if isinstance(item, kind) ]
+
+def FindExtension(object, kind, t_type=type(())):
+ if isinstance(kind, t_type):
+ namespaceURI, name = kind
+ for item in object.extensions:
+ if hasattr(item, 'nodeType') \
+ and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
+ and item.name == name:
+ return item
+ else:
+ for item in object.extensions:
+ if isinstance(item, kind):
+ return item
+ return None
+
+
+class SOAPCallInfo:
+ """SOAPCallInfo captures the important binding information about a
+ SOAP operation, in a structure that is easier to work with than
+ raw WSDL structures."""
+
+ def __init__(self, methodName):
+ self.methodName = methodName
+ self.inheaders = []
+ self.outheaders = []
+ self.inparams = []
+ self.outparams = []
+ self.retval = None
+
+ encodingStyle = DOM.NS_SOAP_ENC
+ documentation = ''
+ soapAction = None
+ transport = None
+ namespace = None
+ location = None
+ use = 'encoded'
+ style = 'rpc'
+
+ def addInParameter(self, name, type, namespace=None, element_type=0):
+ """Add an input parameter description to the call info."""
+ parameter = ParameterInfo(name, type, namespace, element_type)
+ self.inparams.append(parameter)
+ return parameter
+
+ def addOutParameter(self, name, type, namespace=None, element_type=0):
+ """Add an output parameter description to the call info."""
+ parameter = ParameterInfo(name, type, namespace, element_type)
+ self.outparams.append(parameter)
+ return parameter
+
+ def setReturnParameter(self, name, type, namespace=None, element_type=0):
+ """Set the return parameter description for the call info."""
+ parameter = ParameterInfo(name, type, namespace, element_type)
+ self.retval = parameter
+ return parameter
+
+ def addInHeaderInfo(self, name, type, namespace, element_type=0,
+ mustUnderstand=0):
+ """Add an input SOAP header description to the call info."""
+ headerinfo = HeaderInfo(name, type, namespace, element_type)
+ if mustUnderstand:
+ headerinfo.mustUnderstand = 1
+ self.inheaders.append(headerinfo)
+ return headerinfo
+
+ def addOutHeaderInfo(self, name, type, namespace, element_type=0,
+ mustUnderstand=0):
+ """Add an output SOAP header description to the call info."""
+ headerinfo = HeaderInfo(name, type, namespace, element_type)
+ if mustUnderstand:
+ headerinfo.mustUnderstand = 1
+ self.outheaders.append(headerinfo)
+ return headerinfo
+
+ def getInParameters(self):
+ """Return a sequence of the in parameters of the method."""
+ return self.inparams
+
+ def getOutParameters(self):
+ """Return a sequence of the out parameters of the method."""
+ return self.outparams
+
+ def getReturnParameter(self):
+ """Return param info about the return value of the method."""
+ return self.retval
+
+ def getInHeaders(self):
+ """Return a sequence of the in headers of the method."""
+ return self.inheaders
+
+ def getOutHeaders(self):
+ """Return a sequence of the out headers of the method."""
+ return self.outheaders
+
+
+class ParameterInfo:
+ """A ParameterInfo object captures parameter binding information."""
+ def __init__(self, name, type, namespace=None, element_type=0):
+ if element_type:
+ self.element_type = 1
+ if namespace is not None:
+ self.namespace = namespace
+ self.name = name
+ self.type = type
+
+ element_type = 0
+ namespace = None
+ default = None
+
+
+class HeaderInfo(ParameterInfo):
+ """A HeaderInfo object captures SOAP header binding information."""
+ def __init__(self, name, type, namespace, element_type=None):
+ ParameterInfo.__init__(self, name, type, namespace, element_type)
+
+ mustUnderstand = 0
+ actor = None
+
+
+def callInfoFromWSDL(port, name):
+ """Return a SOAPCallInfo given a WSDL port and operation name."""
+ wsdl = port.getService().getWSDL()
+ binding = port.getBinding()
+ portType = binding.getPortType()
+ operation = portType.operations[name]
+ opbinding = binding.operations[name]
+ messages = wsdl.messages
+ callinfo = SOAPCallInfo(name)
+
+ addrbinding = port.getAddressBinding()
+ if not isinstance(addrbinding, SoapAddressBinding):
+ raise ValueError, 'Unsupported binding type.'
+ callinfo.location = addrbinding.location
+
+ soapbinding = binding.findBinding(SoapBinding)
+ if soapbinding is None:
+ raise ValueError, 'Missing soap:binding element.'
+ callinfo.transport = soapbinding.transport
+ callinfo.style = soapbinding.style or 'document'
+
+ soap_op_binding = opbinding.findBinding(SoapOperationBinding)
+ if soap_op_binding is not None:
+ callinfo.soapAction = soap_op_binding.soapAction
+ callinfo.style = soap_op_binding.style or callinfo.style
+
+ parameterOrder = operation.parameterOrder
+
+ if operation.input is not None:
+ message = messages[operation.input.message]
+ msgrole = opbinding.input
+
+ mime = msgrole.findBinding(MimeMultipartRelatedBinding)
+ if mime is not None:
+ raise ValueError, 'Mime bindings are not supported.'
+ else:
+ for item in msgrole.findBindings(SoapHeaderBinding):
+ part = messages[item.message].parts[item.part]
+ header = callinfo.addInHeaderInfo(
+ part.name,
+ part.element or part.type,
+ item.namespace,
+ element_type = part.element and 1 or 0
+ )
+ header.encodingStyle = item.encodingStyle
+
+ body = msgrole.findBinding(SoapBodyBinding)
+ if body is None:
+ raise ValueError, 'Missing soap:body binding.'
+ callinfo.encodingStyle = body.encodingStyle
+ callinfo.namespace = body.namespace
+ callinfo.use = body.use
+
+ if body.parts is not None:
+ parts = []
+ for name in body.parts:
+ parts.append(message.parts[name])
+ else:
+ parts = message.parts.values()
+
+ for part in parts:
+ callinfo.addInParameter(
+ part.name,
+ part.element or part.type,
+ element_type = part.element and 1 or 0
+ )
+
+ if operation.output is not None:
+ try:
+ message = messages[operation.output.message]
+ except KeyError:
+ if self.strict:
+ raise RuntimeError(
+ "Recieved message not defined in the WSDL schema: %s" %
+ operation.output.message)
+ else:
+ message = wsdl.addMessage(operation.output.message)
+ print "Warning:", \
+ "Recieved message not defined in the WSDL schema.", \
+ "Adding it."
+ print "Message:", operation.output.message
+
+ msgrole = opbinding.output
+
+ mime = msgrole.findBinding(MimeMultipartRelatedBinding)
+ if mime is not None:
+ raise ValueError, 'Mime bindings are not supported.'
+ else:
+ for item in msgrole.findBindings(SoapHeaderBinding):
+ part = messages[item.message].parts[item.part]
+ header = callinfo.addOutHeaderInfo(
+ part.name,
+ part.element or part.type,
+ item.namespace,
+ element_type = part.element and 1 or 0
+ )
+ header.encodingStyle = item.encodingStyle
+
+ body = msgrole.findBinding(SoapBodyBinding)
+ if body is None:
+ raise ValueError, 'Missing soap:body binding.'
+ callinfo.encodingStyle = body.encodingStyle
+ callinfo.namespace = body.namespace
+ callinfo.use = body.use
+
+ if body.parts is not None:
+ parts = []
+ for name in body.parts:
+ parts.append(message.parts[name])
+ else:
+ parts = message.parts.values()
+
+ if parts:
+ for part in parts:
+ callinfo.addOutParameter(
+ part.name,
+ part.element or part.type,
+ element_type = part.element and 1 or 0
+ )
+
+ return callinfo
diff --git a/ZSI/zsi/ZSI/wstools/XMLSchema.py b/ZSI/zsi/ZSI/wstools/XMLSchema.py
new file mode 100755
index 0000000..1342835
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/XMLSchema.py
@@ -0,0 +1,3081 @@
+# Copyright (c) 2003, The Regents of the University of California,
+# through Lawrence Berkeley National Laboratory (subject to receipt of
+# any required approvals from the U.S. Dept. of Energy). All rights
+# reserved.
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+
+ident = "$Id: XMLSchema.py 1207 2006-05-04 18:34:01Z boverhof $"
+
+import types, weakref, sys, warnings
+from Namespaces import SCHEMA, XMLNS
+from Utility import DOM, DOMException, Collection, SplitQName, basejoin
+from StringIO import StringIO
+
+# If we have no threading, this should be a no-op
+try:
+ from threading import RLock
+except ImportError:
+ class RLock:
+ def acquire():
+ pass
+ def release():
+ pass
+
+#
+# Collections in XMLSchema class
+#
+TYPES = 'types'
+ATTRIBUTE_GROUPS = 'attr_groups'
+ATTRIBUTES = 'attr_decl'
+ELEMENTS = 'elements'
+MODEL_GROUPS = 'model_groups'
+
+
+def GetSchema(component):
+ """convience function for finding the parent XMLSchema instance.
+ """
+ parent = component
+ while not isinstance(parent, XMLSchema):
+ parent = parent._parent()
+ return parent
+
+class SchemaReader:
+ """A SchemaReader creates XMLSchema objects from urls and xml data.
+ """
+
+ namespaceToSchema = {}
+
+ def __init__(self, domReader=None, base_url=None):
+ """domReader -- class must implement DOMAdapterInterface
+ base_url -- base url string
+ """
+ self.__base_url = base_url
+ self.__readerClass = domReader
+ if not self.__readerClass:
+ self.__readerClass = DOMAdapter
+ self._includes = {}
+ self._imports = {}
+
+ def __setImports(self, schema):
+ """Add dictionary of imports to schema instance.
+ schema -- XMLSchema instance
+ """
+ for ns,val in schema.imports.items():
+ if self._imports.has_key(ns):
+ schema.addImportSchema(self._imports[ns])
+
+ def __setIncludes(self, schema):
+ """Add dictionary of includes to schema instance.
+ schema -- XMLSchema instance
+ """
+ for schemaLocation, val in schema.includes.items():
+ if self._includes.has_key(schemaLocation):
+ schema.addIncludeSchema(self._imports[schemaLocation])
+
+ def addSchemaByLocation(self, location, schema):
+ """provide reader with schema document for a location.
+ """
+ self._includes[location] = schema
+
+ def addSchemaByNamespace(self, schema):
+ """provide reader with schema document for a targetNamespace.
+ """
+ self._imports[schema.targetNamespace] = schema
+
+ def loadFromNode(self, parent, element):
+ """element -- DOM node or document
+ parent -- WSDLAdapter instance
+ """
+ reader = self.__readerClass(element)
+ schema = XMLSchema(parent)
+ #HACK to keep a reference
+ schema.wsdl = parent
+ schema.setBaseUrl(self.__base_url)
+ schema.load(reader)
+ return schema
+
+ def loadFromStream(self, file, url=None):
+ """Return an XMLSchema instance loaded from a file object.
+ file -- file object
+ url -- base location for resolving imports/includes.
+ """
+ reader = self.__readerClass()
+ reader.loadDocument(file)
+ schema = XMLSchema()
+ if url is not None:
+ schema.setBaseUrl(url)
+ schema.load(reader)
+ self.__setIncludes(schema)
+ self.__setImports(schema)
+ return schema
+
+ def loadFromString(self, data):
+ """Return an XMLSchema instance loaded from an XML string.
+ data -- XML string
+ """
+ return self.loadFromStream(StringIO(data))
+
+ def loadFromURL(self, url, schema=None):
+ """Return an XMLSchema instance loaded from the given url.
+ url -- URL to dereference
+ schema -- Optional XMLSchema instance.
+ """
+ reader = self.__readerClass()
+ if self.__base_url:
+ url = basejoin(self.__base_url,url)
+
+ reader.loadFromURL(url)
+ schema = schema or XMLSchema()
+ schema.setBaseUrl(url)
+ schema.load(reader)
+ self.__setIncludes(schema)
+ self.__setImports(schema)
+ return schema
+
+ def loadFromFile(self, filename):
+ """Return an XMLSchema instance loaded from the given file.
+ filename -- name of file to open
+ """
+ if self.__base_url:
+ filename = basejoin(self.__base_url,filename)
+ file = open(filename, 'rb')
+ try:
+ schema = self.loadFromStream(file, filename)
+ finally:
+ file.close()
+
+ return schema
+
+
+class SchemaError(Exception):
+ pass
+
+
+###########################
+# DOM Utility Adapters
+##########################
+class DOMAdapterInterface:
+ def hasattr(self, attr, ns=None):
+ """return true if node has attribute
+ attr -- attribute to check for
+ ns -- namespace of attribute, by default None
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def getContentList(self, *contents):
+ """returns an ordered list of child nodes
+ *contents -- list of node names to return
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def setAttributeDictionary(self, attributes):
+ """set attribute dictionary
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def getAttributeDictionary(self):
+ """returns a dict of node's attributes
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def getNamespace(self, prefix):
+ """returns namespace referenced by prefix.
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def getTagName(self):
+ """returns tagName of node
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+
+ def getParentNode(self):
+ """returns parent element in DOMAdapter or None
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def loadDocument(self, file):
+ """load a Document from a file object
+ file --
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+ def loadFromURL(self, url):
+ """load a Document from an url
+ url -- URL to dereference
+ """
+ raise NotImplementedError, 'adapter method not implemented'
+
+
+class DOMAdapter(DOMAdapterInterface):
+ """Adapter for ZSI.Utility.DOM
+ """
+ def __init__(self, node=None):
+ """Reset all instance variables.
+ element -- DOM document, node, or None
+ """
+ if hasattr(node, 'documentElement'):
+ self.__node = node.documentElement
+ else:
+ self.__node = node
+ self.__attributes = None
+
+ def getNode(self):
+ return self.__node
+
+ def hasattr(self, attr, ns=None):
+ """attr -- attribute
+ ns -- optional namespace, None means unprefixed attribute.
+ """
+ if not self.__attributes:
+ self.setAttributeDictionary()
+ if ns:
+ return self.__attributes.get(ns,{}).has_key(attr)
+ return self.__attributes.has_key(attr)
+
+ def getContentList(self, *contents):
+ nodes = []
+ ELEMENT_NODE = self.__node.ELEMENT_NODE
+ for child in DOM.getElements(self.__node, None):
+ if child.nodeType == ELEMENT_NODE and\
+ SplitQName(child.tagName)[1] in contents:
+ nodes.append(child)
+ return map(self.__class__, nodes)
+
+ def setAttributeDictionary(self):
+ self.__attributes = {}
+ for v in self.__node._attrs.values():
+ self.__attributes[v.nodeName] = v.nodeValue
+
+ def getAttributeDictionary(self):
+ if not self.__attributes:
+ self.setAttributeDictionary()
+ return self.__attributes
+
+ def getTagName(self):
+ return self.__node.tagName
+
+ def getParentNode(self):
+ if self.__node.parentNode.nodeType == self.__node.ELEMENT_NODE:
+ return DOMAdapter(self.__node.parentNode)
+ return None
+
+ def getNamespace(self, prefix):
+ """prefix -- deference namespace prefix in node's context.
+ Ascends parent nodes until found.
+ """
+ namespace = None
+ if prefix == 'xmlns':
+ namespace = DOM.findDefaultNS(prefix, self.__node)
+ else:
+ try:
+ namespace = DOM.findNamespaceURI(prefix, self.__node)
+ except DOMException, ex:
+ if prefix != 'xml':
+ raise SchemaError, '%s namespace not declared for %s'\
+ %(prefix, self.__node._get_tagName())
+ namespace = XMLNS.XML
+ return namespace
+
+ def loadDocument(self, file):
+ self.__node = DOM.loadDocument(file)
+ if hasattr(self.__node, 'documentElement'):
+ self.__node = self.__node.documentElement
+
+ def loadFromURL(self, url):
+ self.__node = DOM.loadFromURL(url)
+ if hasattr(self.__node, 'documentElement'):
+ self.__node = self.__node.documentElement
+
+
+class XMLBase:
+ """ These class variables are for string indentation.
+ """
+ tag = None
+ __indent = 0
+ __rlock = RLock()
+
+ def __str__(self):
+ XMLBase.__rlock.acquire()
+ XMLBase.__indent += 1
+ tmp = "<" + str(self.__class__) + '>\n'
+ for k,v in self.__dict__.items():
+ tmp += "%s* %s = %s\n" %(XMLBase.__indent*' ', k, v)
+ XMLBase.__indent -= 1
+ XMLBase.__rlock.release()
+ return tmp
+
+
+"""Marker Interface: can determine something about an instances properties by using
+ the provided convenience functions.
+
+"""
+class DefinitionMarker:
+ """marker for definitions
+ """
+ pass
+
+class DeclarationMarker:
+ """marker for declarations
+ """
+ pass
+
+class AttributeMarker:
+ """marker for attributes
+ """
+ pass
+
+class AttributeGroupMarker:
+ """marker for attribute groups
+ """
+ pass
+
+class WildCardMarker:
+ """marker for wildcards
+ """
+ pass
+
+class ElementMarker:
+ """marker for wildcards
+ """
+ pass
+
+class ReferenceMarker:
+ """marker for references
+ """
+ pass
+
+class ModelGroupMarker:
+ """marker for model groups
+ """
+ pass
+
+class AllMarker(ModelGroupMarker):
+ """marker for all model group
+ """
+ pass
+
+class ChoiceMarker(ModelGroupMarker):
+ """marker for choice model group
+ """
+ pass
+
+class SequenceMarker(ModelGroupMarker):
+ """marker for sequence model group
+ """
+ pass
+
+class ExtensionMarker:
+ """marker for extensions
+ """
+ pass
+
+class RestrictionMarker:
+ """marker for restrictions
+ """
+ facets = ['enumeration', 'length', 'maxExclusive', 'maxInclusive',\
+ 'maxLength', 'minExclusive', 'minInclusive', 'minLength',\
+ 'pattern', 'fractionDigits', 'totalDigits', 'whiteSpace']
+
+class SimpleMarker:
+ """marker for simple type information
+ """
+ pass
+
+class ListMarker:
+ """marker for simple type list
+ """
+ pass
+
+class UnionMarker:
+ """marker for simple type Union
+ """
+ pass
+
+
+class ComplexMarker:
+ """marker for complex type information
+ """
+ pass
+
+class LocalMarker:
+ """marker for complex type information
+ """
+ pass
+
+
+class MarkerInterface:
+ def isDefinition(self):
+ return isinstance(self, DefinitionMarker)
+
+ def isDeclaration(self):
+ return isinstance(self, DeclarationMarker)
+
+ def isAttribute(self):
+ return isinstance(self, AttributeMarker)
+
+ def isAttributeGroup(self):
+ return isinstance(self, AttributeGroupMarker)
+
+ def isElement(self):
+ return isinstance(self, ElementMarker)
+
+ def isReference(self):
+ return isinstance(self, ReferenceMarker)
+
+ def isWildCard(self):
+ return isinstance(self, WildCardMarker)
+
+ def isModelGroup(self):
+ return isinstance(self, ModelGroupMarker)
+
+ def isAll(self):
+ return isinstance(self, AllMarker)
+
+ def isChoice(self):
+ return isinstance(self, ChoiceMarker)
+
+ def isSequence(self):
+ return isinstance(self, SequenceMarker)
+
+ def isExtension(self):
+ return isinstance(self, ExtensionMarker)
+
+ def isRestriction(self):
+ return isinstance(self, RestrictionMarker)
+
+ def isSimple(self):
+ return isinstance(self, SimpleMarker)
+
+ def isComplex(self):
+ return isinstance(self, ComplexMarker)
+
+ def isLocal(self):
+ return isinstance(self, LocalMarker)
+
+ def isList(self):
+ return isinstance(self, ListMarker)
+
+ def isUnion(self):
+ return isinstance(self, UnionMarker)
+
+
+##########################################################
+# Schema Components
+#########################################################
+class XMLSchemaComponent(XMLBase, MarkerInterface):
+ """
+ class variables:
+ required -- list of required attributes
+ attributes -- dict of default attribute values, including None.
+ Value can be a function for runtime dependencies.
+ contents -- dict of namespace keyed content lists.
+ 'xsd' content of xsd namespace.
+ xmlns_key -- key for declared xmlns namespace.
+ xmlns -- xmlns is special prefix for namespace dictionary
+ xml -- special xml prefix for xml namespace.
+ """
+ required = []
+ attributes = {}
+ contents = {}
+ xmlns_key = ''
+ xmlns = 'xmlns'
+ xml = 'xml'
+
+ def __init__(self, parent=None):
+ """parent -- parent instance
+ instance variables:
+ attributes -- dictionary of node's attributes
+ """
+ self.attributes = None
+ self._parent = parent
+ if self._parent:
+ self._parent = weakref.ref(parent)
+
+ if not self.__class__ == XMLSchemaComponent\
+ and not (type(self.__class__.required) == type(XMLSchemaComponent.required)\
+ and type(self.__class__.attributes) == type(XMLSchemaComponent.attributes)\
+ and type(self.__class__.contents) == type(XMLSchemaComponent.contents)):
+ raise RuntimeError, 'Bad type for a class variable in %s' %self.__class__
+
+ def getItemTrace(self):
+ """Returns a node trace up to the <schema> item.
+ """
+ item, path, name, ref = self, [], 'name', 'ref'
+ while not isinstance(item,XMLSchema) and not isinstance(item,WSDLToolsAdapter):
+ attr = item.getAttribute(name)
+ if attr is None:
+ attr = item.getAttribute(ref)
+ if attr is None: path.append('<%s>' %(item.tag))
+ else: path.append('<%s ref="%s">' %(item.tag, attr))
+ else:
+ path.append('<%s name="%s">' %(item.tag,attr))
+ item = item._parent()
+ try:
+ tns = item.getTargetNamespace()
+ except:
+ tns = ''
+ path.append('<%s targetNamespace="%s">' %(item.tag, tns))
+ path.reverse()
+ return ''.join(path)
+
+ def getTargetNamespace(self):
+ """return targetNamespace
+ """
+ parent = self
+ targetNamespace = 'targetNamespace'
+ tns = self.attributes.get(targetNamespace)
+ while not tns:
+ parent = parent._parent()
+ tns = parent.attributes.get(targetNamespace)
+ return tns
+
+ def getAttributeDeclaration(self, attribute):
+ """attribute -- attribute with a QName value (eg. type).
+ collection -- check types collection in parent Schema instance
+ """
+ return self.getQNameAttribute(ATTRIBUTES, attribute)
+
+ def getAttributeGroup(self, attribute):
+ """attribute -- attribute with a QName value (eg. type).
+ collection -- check types collection in parent Schema instance
+ """
+ return self.getQNameAttribute(ATTRIBUTE_GROUPS, attribute)
+
+ def getTypeDefinition(self, attribute):
+ """attribute -- attribute with a QName value (eg. type).
+ collection -- check types collection in parent Schema instance
+ """
+ return self.getQNameAttribute(TYPES, attribute)
+
+ def getElementDeclaration(self, attribute):
+ """attribute -- attribute with a QName value (eg. element).
+ collection -- check elements collection in parent Schema instance.
+ """
+ return self.getQNameAttribute(ELEMENTS, attribute)
+
+ def getModelGroup(self, attribute):
+ """attribute -- attribute with a QName value (eg. ref).
+ collection -- check model_group collection in parent Schema instance.
+ """
+ return self.getQNameAttribute(MODEL_GROUPS, attribute)
+
+ def getQNameAttribute(self, collection, attribute):
+ """returns object instance representing QName --> (namespace,name),
+ or if does not exist return None.
+ attribute -- an information item attribute, with a QName value.
+ collection -- collection in parent Schema instance to search.
+ """
+ obj = None
+ tdc = self.getAttributeQName(attribute)
+ if tdc:
+ obj = self.getSchemaItem(collection, tdc.getTargetNamespace(), tdc.getName())
+
+ return obj
+
+ def getSchemaItem(self, collection, namespace, name):
+ """returns object instance representing namespace, name,
+ or if does not exist return None.
+ namespace -- namespace item defined in.
+ name -- name of item.
+ collection -- collection in parent Schema instance to search.
+ """
+ parent = GetSchema(self)
+ if parent.targetNamespace == namespace:
+ try:
+ obj = getattr(parent, collection)[name]
+ except KeyError, ex:
+ raise KeyError, 'targetNamespace(%s) collection(%s) has no item(%s)'\
+ %(namespace, collection, name)
+
+ return obj
+
+ if not parent.imports.has_key(namespace):
+ return None
+
+ # Lazy Eval
+ schema = parent.imports[namespace]
+ if not isinstance(schema, XMLSchema):
+ schema = schema.getSchema()
+ if schema is not None:
+ parent.imports[namespace] = schema
+
+ if schema is None:
+ raise SchemaError, 'no schema instance for imported namespace (%s).'\
+ %(namespace)
+
+ if not isinstance(schema, XMLSchema):
+ raise TypeError, 'expecting XMLSchema instance not "%r"' %schema
+
+ try:
+ obj = getattr(schema, collection)[name]
+ except KeyError, ex:
+ raise KeyError, 'targetNamespace(%s) collection(%s) has no item(%s)'\
+ %(namespace, collection, name)
+
+ return obj
+
+ def getXMLNS(self, prefix=None):
+ """deference prefix or by default xmlns, returns namespace.
+ """
+ if prefix == XMLSchemaComponent.xml:
+ return XMLNS.XML
+ parent = self
+ ns = self.attributes[XMLSchemaComponent.xmlns].get(prefix or\
+ XMLSchemaComponent.xmlns_key)
+ while not ns:
+ parent = parent._parent()
+ ns = parent.attributes[XMLSchemaComponent.xmlns].get(prefix or\
+ XMLSchemaComponent.xmlns_key)
+ if not ns and isinstance(parent, WSDLToolsAdapter):
+ if prefix is None:
+ return ''
+ raise SchemaError, 'unknown prefix %s' %prefix
+ return ns
+
+ def getAttribute(self, attribute):
+ """return requested attribute value or None
+ """
+ if type(attribute) in (list, tuple):
+ if len(attribute) != 2:
+ raise LookupError, 'To access attributes must use name or (namespace,name)'
+
+ return self.attributes.get(attribute[0]).get(attribute[1])
+
+ return self.attributes.get(attribute)
+
+ def getAttributeQName(self, attribute):
+ """return requested attribute value as (namespace,name) or None
+ """
+ qname = self.getAttribute(attribute)
+ if isinstance(qname, TypeDescriptionComponent) is True:
+ return qname
+ if qname is None:
+ return None
+
+ prefix,ncname = SplitQName(qname)
+ namespace = self.getXMLNS(prefix)
+ return TypeDescriptionComponent((namespace,ncname))
+
+ def getAttributeName(self):
+ """return attribute name or None
+ """
+ return self.getAttribute('name')
+
+ def setAttributes(self, node):
+ """Sets up attribute dictionary, checks for required attributes and
+ sets default attribute values. attr is for default attribute values
+ determined at runtime.
+
+ structure of attributes dictionary
+ ['xmlns'][xmlns_key] -- xmlns namespace
+ ['xmlns'][prefix] -- declared namespace prefix
+ [namespace][prefix] -- attributes declared in a namespace
+ [attribute] -- attributes w/o prefix, default namespaces do
+ not directly apply to attributes, ie Name can't collide
+ with QName.
+ """
+ self.attributes = {XMLSchemaComponent.xmlns:{}}
+ for k,v in node.getAttributeDictionary().items():
+ prefix,value = SplitQName(k)
+ if value == XMLSchemaComponent.xmlns:
+ self.attributes[value][prefix or XMLSchemaComponent.xmlns_key] = v
+ elif prefix:
+ ns = node.getNamespace(prefix)
+ if not ns:
+ raise SchemaError, 'no namespace for attribute prefix %s'\
+ %prefix
+ if not self.attributes.has_key(ns):
+ self.attributes[ns] = {}
+ elif self.attributes[ns].has_key(value):
+ raise SchemaError, 'attribute %s declared multiple times in %s'\
+ %(value, ns)
+ self.attributes[ns][value] = v
+ elif not self.attributes.has_key(value):
+ self.attributes[value] = v
+ else:
+ raise SchemaError, 'attribute %s declared multiple times' %value
+
+ if not isinstance(self, WSDLToolsAdapter):
+ self.__checkAttributes()
+ self.__setAttributeDefaults()
+
+ #set QNames
+ for k in ['type', 'element', 'base', 'ref', 'substitutionGroup', 'itemType']:
+ if self.attributes.has_key(k):
+ prefix, value = SplitQName(self.attributes.get(k))
+ self.attributes[k] = \
+ TypeDescriptionComponent((self.getXMLNS(prefix), value))
+
+ #Union, memberTypes is a whitespace separated list of QNames
+ for k in ['memberTypes']:
+ if self.attributes.has_key(k):
+ qnames = self.attributes[k]
+ self.attributes[k] = []
+ for qname in qnames.split():
+ prefix, value = SplitQName(qname)
+ self.attributes['memberTypes'].append(\
+ TypeDescriptionComponent(\
+ (self.getXMLNS(prefix), value)))
+
+ def getContents(self, node):
+ """retrieve xsd contents
+ """
+ return node.getContentList(*self.__class__.contents['xsd'])
+
+ def __setAttributeDefaults(self):
+ """Looks for default values for unset attributes. If
+ class variable representing attribute is None, then
+ it must be defined as an instance variable.
+ """
+ for k,v in self.__class__.attributes.items():
+ if v is not None and self.attributes.has_key(k) is False:
+ if isinstance(v, types.FunctionType):
+ self.attributes[k] = v(self)
+ else:
+ self.attributes[k] = v
+
+ def __checkAttributes(self):
+ """Checks that required attributes have been defined,
+ attributes w/default cannot be required. Checks
+ all defined attributes are legal, attribute
+ references are not subject to this test.
+ """
+ for a in self.__class__.required:
+ if not self.attributes.has_key(a):
+ raise SchemaError,\
+ 'class instance %s, missing required attribute %s'\
+ %(self.__class__, a)
+ for a,v in self.attributes.items():
+ # attribute #other, ie. not in empty namespace
+ if type(v) is dict:
+ continue
+
+ # predefined prefixes xmlns, xml
+ if a in (XMLSchemaComponent.xmlns, XMLNS.XML):
+ continue
+
+ if (a not in self.__class__.attributes.keys()) and not\
+ (self.isAttribute() and self.isReference()):
+ raise SchemaError, '%s, unknown attribute(%s,%s)' \
+ %(self.getItemTrace(), a, self.attributes[a])
+
+
+class WSDLToolsAdapter(XMLSchemaComponent):
+ """WSDL Adapter to grab the attributes from the wsdl document node.
+ """
+ attributes = {'name':None, 'targetNamespace':None}
+ tag = 'definitions'
+
+ def __init__(self, wsdl):
+ XMLSchemaComponent.__init__(self, parent=wsdl)
+ self.setAttributes(DOMAdapter(wsdl.document))
+
+ def getImportSchemas(self):
+ """returns WSDLTools.WSDL types Collection
+ """
+ return self._parent().types
+
+
+class Notation(XMLSchemaComponent):
+ """<notation>
+ parent:
+ schema
+ attributes:
+ id -- ID
+ name -- NCName, Required
+ public -- token, Required
+ system -- anyURI
+ contents:
+ annotation?
+ """
+ required = ['name', 'public']
+ attributes = {'id':None, 'name':None, 'public':None, 'system':None}
+ contents = {'xsd':('annotation')}
+ tag = 'notation'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class Annotation(XMLSchemaComponent):
+ """<annotation>
+ parent:
+ all,any,anyAttribute,attribute,attributeGroup,choice,complexContent,
+ complexType,element,extension,field,group,import,include,key,keyref,
+ list,notation,redefine,restriction,schema,selector,simpleContent,
+ simpleType,union,unique
+ attributes:
+ id -- ID
+ contents:
+ (documentation | appinfo)*
+ """
+ attributes = {'id':None}
+ contents = {'xsd':('documentation', 'appinfo')}
+ tag = 'annotation'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'documentation':
+ #print_debug('class %s, documentation skipped' %self.__class__, 5)
+ continue
+ elif component == 'appinfo':
+ #print_debug('class %s, appinfo skipped' %self.__class__, 5)
+ continue
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+ class Documentation(XMLSchemaComponent):
+ """<documentation>
+ parent:
+ annotation
+ attributes:
+ source, anyURI
+ xml:lang, language
+ contents:
+ mixed, any
+ """
+ attributes = {'source':None, 'xml:lang':None}
+ contents = {'xsd':('mixed', 'any')}
+ tag = 'documentation'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'mixed':
+ #print_debug('class %s, mixed skipped' %self.__class__, 5)
+ continue
+ elif component == 'any':
+ #print_debug('class %s, any skipped' %self.__class__, 5)
+ continue
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+ class Appinfo(XMLSchemaComponent):
+ """<appinfo>
+ parent:
+ annotation
+ attributes:
+ source, anyURI
+ contents:
+ mixed, any
+ """
+ attributes = {'source':None, 'anyURI':None}
+ contents = {'xsd':('mixed', 'any')}
+ tag = 'appinfo'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'mixed':
+ #print_debug('class %s, mixed skipped' %self.__class__, 5)
+ continue
+ elif component == 'any':
+ #print_debug('class %s, any skipped' %self.__class__, 5)
+ continue
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+class XMLSchemaFake:
+ # This is temporary, for the benefit of WSDL until the real thing works.
+ def __init__(self, element):
+ self.targetNamespace = DOM.getAttr(element, 'targetNamespace')
+ self.element = element
+
+class XMLSchema(XMLSchemaComponent):
+ """A schema is a collection of schema components derived from one
+ or more schema documents, that is, one or more <schema> element
+ information items. It represents the abstract notion of a schema
+ rather than a single schema document (or other representation).
+
+ <schema>
+ parent:
+ ROOT
+ attributes:
+ id -- ID
+ version -- token
+ xml:lang -- language
+ targetNamespace -- anyURI
+ attributeFormDefault -- 'qualified' | 'unqualified', 'unqualified'
+ elementFormDefault -- 'qualified' | 'unqualified', 'unqualified'
+ blockDefault -- '#all' | list of
+ ('substitution | 'extension' | 'restriction')
+ finalDefault -- '#all' | list of
+ ('extension' | 'restriction' | 'list' | 'union')
+
+ contents:
+ ((include | import | redefine | annotation)*,
+ (attribute, attributeGroup, complexType, element, group,
+ notation, simpleType)*, annotation*)*
+
+
+ attributes -- schema attributes
+ imports -- import statements
+ includes -- include statements
+ redefines --
+ types -- global simpleType, complexType definitions
+ elements -- global element declarations
+ attr_decl -- global attribute declarations
+ attr_groups -- attribute Groups
+ model_groups -- model Groups
+ notations -- global notations
+ """
+ attributes = {'id':None,
+ 'version':None,
+ 'xml:lang':None,
+ 'targetNamespace':None,
+ 'attributeFormDefault':'unqualified',
+ 'elementFormDefault':'unqualified',
+ 'blockDefault':None,
+ 'finalDefault':None}
+ contents = {'xsd':('include', 'import', 'redefine', 'annotation',
+ 'attribute', 'attributeGroup', 'complexType',
+ 'element', 'group', 'notation', 'simpleType',
+ 'annotation')}
+ empty_namespace = ''
+ tag = 'schema'
+
+ def __init__(self, parent=None):
+ """parent --
+ instance variables:
+ targetNamespace -- schema's declared targetNamespace, or empty string.
+ _imported_schemas -- namespace keyed dict of schema dependencies, if
+ a schema is provided instance will not resolve import statement.
+ _included_schemas -- schemaLocation keyed dict of component schemas,
+ if schema is provided instance will not resolve include statement.
+ _base_url -- needed for relative URLs support, only works with URLs
+ relative to initial document.
+ includes -- collection of include statements
+ imports -- collection of import statements
+ elements -- collection of global element declarations
+ types -- collection of global type definitions
+ attr_decl -- collection of global attribute declarations
+ attr_groups -- collection of global attribute group definitions
+ model_groups -- collection of model group definitions
+ notations -- collection of notations
+
+ """
+ self.__node = None
+ self.targetNamespace = None
+ XMLSchemaComponent.__init__(self, parent)
+ f = lambda k: k.attributes['name']
+ ns = lambda k: k.attributes['namespace']
+ sl = lambda k: k.attributes['schemaLocation']
+ self.includes = Collection(self, key=sl)
+ self.imports = Collection(self, key=ns)
+ self.elements = Collection(self, key=f)
+ self.types = Collection(self, key=f)
+ self.attr_decl = Collection(self, key=f)
+ self.attr_groups = Collection(self, key=f)
+ self.model_groups = Collection(self, key=f)
+ self.notations = Collection(self, key=f)
+
+ self._imported_schemas = {}
+ self._included_schemas = {}
+ self._base_url = None
+
+ def getNode(self):
+ """
+ Interacting with the underlying DOM tree.
+ """
+ return self.__node
+
+ def addImportSchema(self, schema):
+ """for resolving import statements in Schema instance
+ schema -- schema instance
+ _imported_schemas
+ """
+ if not isinstance(schema, XMLSchema):
+ raise TypeError, 'expecting a Schema instance'
+ if schema.targetNamespace != self.targetNamespace:
+ self._imported_schemas[schema.targetNamespace] = schema
+ else:
+ raise SchemaError, 'import schema bad targetNamespace'
+
+ def addIncludeSchema(self, schemaLocation, schema):
+ """for resolving include statements in Schema instance
+ schemaLocation -- schema location
+ schema -- schema instance
+ _included_schemas
+ """
+ if not isinstance(schema, XMLSchema):
+ raise TypeError, 'expecting a Schema instance'
+ if not schema.targetNamespace or\
+ schema.targetNamespace == self.targetNamespace:
+ self._included_schemas[schemaLocation] = schema
+ else:
+ raise SchemaError, 'include schema bad targetNamespace'
+
+ def setImportSchemas(self, schema_dict):
+ """set the import schema dictionary, which is used to
+ reference depedent schemas.
+ """
+ self._imported_schemas = schema_dict
+
+ def getImportSchemas(self):
+ """get the import schema dictionary, which is used to
+ reference depedent schemas.
+ """
+ return self._imported_schemas
+
+ def getSchemaNamespacesToImport(self):
+ """returns tuple of namespaces the schema instance has declared
+ itself to be depedent upon.
+ """
+ return tuple(self.includes.keys())
+
+ def setIncludeSchemas(self, schema_dict):
+ """set the include schema dictionary, which is keyed with
+ schemaLocation (uri).
+ This is a means of providing
+ schemas to the current schema for content inclusion.
+ """
+ self._included_schemas = schema_dict
+
+ def getIncludeSchemas(self):
+ """get the include schema dictionary, which is keyed with
+ schemaLocation (uri).
+ """
+ return self._included_schemas
+
+ def getBaseUrl(self):
+ """get base url, used for normalizing all relative uri's
+ """
+ return self._base_url
+
+ def setBaseUrl(self, url):
+ """set base url, used for normalizing all relative uri's
+ """
+ self._base_url = url
+
+ def getElementFormDefault(self):
+ """return elementFormDefault attribute
+ """
+ return self.attributes.get('elementFormDefault')
+
+ def isElementFormDefaultQualified(self):
+ return self.attributes.get('elementFormDefault') == 'qualified'
+
+ def getAttributeFormDefault(self):
+ """return attributeFormDefault attribute
+ """
+ return self.attributes.get('attributeFormDefault')
+
+ def getBlockDefault(self):
+ """return blockDefault attribute
+ """
+ return self.attributes.get('blockDefault')
+
+ def getFinalDefault(self):
+ """return finalDefault attribute
+ """
+ return self.attributes.get('finalDefault')
+
+ def load(self, node, location=None):
+ self.__node = node
+
+ pnode = node.getParentNode()
+ if pnode:
+ pname = SplitQName(pnode.getTagName())[1]
+ if pname == 'types':
+ attributes = {}
+ self.setAttributes(pnode)
+ attributes.update(self.attributes)
+ self.setAttributes(node)
+ for k,v in attributes['xmlns'].items():
+ if not self.attributes['xmlns'].has_key(k):
+ self.attributes['xmlns'][k] = v
+ else:
+ self.setAttributes(node)
+ else:
+ self.setAttributes(node)
+
+ self.targetNamespace = self.getTargetNamespace()
+ for childNode in self.getContents(node):
+ component = SplitQName(childNode.getTagName())[1]
+
+ if component == 'include':
+ tp = self.__class__.Include(self)
+ tp.fromDom(childNode)
+
+ sl = tp.attributes['schemaLocation']
+ schema = tp.getSchema()
+
+ if not self.getIncludeSchemas().has_key(sl):
+ self.addIncludeSchema(sl, schema)
+
+ self.includes[sl] = tp
+
+ pn = childNode.getParentNode().getNode()
+ pn.removeChild(childNode.getNode())
+ for child in schema.getNode().getNode().childNodes:
+ pn.appendChild(child.cloneNode(1))
+
+ for collection in ['imports','elements','types',
+ 'attr_decl','attr_groups','model_groups',
+ 'notations']:
+ for k,v in getattr(schema,collection).items():
+ if not getattr(self,collection).has_key(k):
+ v._parent = weakref.ref(self)
+ getattr(self,collection)[k] = v
+ else:
+ warnings.warn("Not keeping schema component.")
+
+ elif component == 'import':
+ slocd = SchemaReader.namespaceToSchema
+ tp = self.__class__.Import(self)
+ tp.fromDom(childNode)
+ import_ns = tp.getAttribute('namespace') or\
+ self.__class__.empty_namespace
+ schema = slocd.get(import_ns)
+ if schema is None:
+ schema = XMLSchema()
+ slocd[import_ns] = schema
+ try:
+ tp.loadSchema(schema)
+ except SchemaError:
+ # Dependency declaration, hopefully implementation
+ # is aware of this namespace (eg. SOAP,WSDL,?)
+ #warnings.warn(\
+ # '<import namespace="%s" schemaLocation=?>, %s'\
+ # %(import_ns, 'failed to load schema instance')
+ #)
+ del slocd[import_ns]
+ class LazyEval(str):
+ '''Lazy evaluation of import, replace entry in self.imports.'''
+ def getSchema(namespace):
+ schema = slocd.get(namespace)
+ if schema is None:
+ parent = self._parent()
+ wstypes = parent
+ if isinstance(parent, WSDLToolsAdapter):
+ wstypes = parent.getImportSchemas()
+ schema = wstypes.get(namespace)
+ if isinstance(schema, XMLSchema):
+ self.imports[namespace] = schema
+ return schema
+
+ return None
+
+ self.imports[import_ns] = LazyEval(import_ns)
+ continue
+ else:
+ tp._schema = schema
+
+ if self.getImportSchemas().has_key(import_ns):
+ warnings.warn(\
+ 'Detected multiple imports of the namespace "%s" '\
+ %import_ns)
+
+ self.addImportSchema(schema)
+ # spec says can have multiple imports of same namespace
+ # but purpose of import is just dependency declaration.
+ self.imports[import_ns] = tp
+
+ elif component == 'redefine':
+ warnings.warn('redefine is ignored')
+ elif component == 'annotation':
+ warnings.warn('annotation is ignored')
+ elif component == 'attribute':
+ tp = AttributeDeclaration(self)
+ tp.fromDom(childNode)
+ self.attr_decl[tp.getAttribute('name')] = tp
+ elif component == 'attributeGroup':
+ tp = AttributeGroupDefinition(self)
+ tp.fromDom(childNode)
+ self.attr_groups[tp.getAttribute('name')] = tp
+ elif component == 'element':
+ tp = ElementDeclaration(self)
+ tp.fromDom(childNode)
+ self.elements[tp.getAttribute('name')] = tp
+ elif component == 'group':
+ tp = ModelGroupDefinition(self)
+ tp.fromDom(childNode)
+ self.model_groups[tp.getAttribute('name')] = tp
+ elif component == 'notation':
+ tp = Notation(self)
+ tp.fromDom(childNode)
+ self.notations[tp.getAttribute('name')] = tp
+ elif component == 'complexType':
+ tp = ComplexType(self)
+ tp.fromDom(childNode)
+ self.types[tp.getAttribute('name')] = tp
+ elif component == 'simpleType':
+ tp = SimpleType(self)
+ tp.fromDom(childNode)
+ self.types[tp.getAttribute('name')] = tp
+ else:
+ break
+
+ class Import(XMLSchemaComponent):
+ """<import>
+ parent:
+ schema
+ attributes:
+ id -- ID
+ namespace -- anyURI
+ schemaLocation -- anyURI
+ contents:
+ annotation?
+ """
+ attributes = {'id':None,
+ 'namespace':None,
+ 'schemaLocation':None}
+ contents = {'xsd':['annotation']}
+ tag = 'import'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self._schema = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ if self.attributes['namespace'] == self.getTargetNamespace():
+ raise SchemaError, 'namespace of schema and import match'
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+ def getSchema(self):
+ """if schema is not defined, first look for a Schema class instance
+ in parent Schema. Else if not defined resolve schemaLocation
+ and create a new Schema class instance, and keep a hard reference.
+ """
+ if not self._schema:
+ ns = self.attributes['namespace']
+ schema = self._parent().getImportSchemas().get(ns)
+ if not schema and self._parent()._parent:
+ schema = self._parent()._parent().getImportSchemas().get(ns)
+
+ if not schema:
+ url = self.attributes.get('schemaLocation')
+ if not url:
+ raise SchemaError, 'namespace(%s) is unknown' %ns
+ base_url = self._parent().getBaseUrl()
+ reader = SchemaReader(base_url=base_url)
+ reader._imports = self._parent().getImportSchemas()
+ reader._includes = self._parent().getIncludeSchemas()
+ self._schema = reader.loadFromURL(url)
+ return self._schema or schema
+
+ def loadSchema(self, schema):
+ """
+ """
+ base_url = self._parent().getBaseUrl()
+ reader = SchemaReader(base_url=base_url)
+ reader._imports = self._parent().getImportSchemas()
+ reader._includes = self._parent().getIncludeSchemas()
+ self._schema = schema
+
+ if not self.attributes.has_key('schemaLocation'):
+ raise SchemaError, 'no schemaLocation'
+ reader.loadFromURL(self.attributes.get('schemaLocation'), schema)
+
+
+ class Include(XMLSchemaComponent):
+ """<include schemaLocation>
+ parent:
+ schema
+ attributes:
+ id -- ID
+ schemaLocation -- anyURI, required
+ contents:
+ annotation?
+ """
+ required = ['schemaLocation']
+ attributes = {'id':None,
+ 'schemaLocation':None}
+ contents = {'xsd':['annotation']}
+ tag = 'include'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self._schema = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+ def getSchema(self):
+ """if schema is not defined, first look for a Schema class instance
+ in parent Schema. Else if not defined resolve schemaLocation
+ and create a new Schema class instance.
+ """
+ if not self._schema:
+ schema = self._parent()
+ self._schema = schema.getIncludeSchemas().get(\
+ self.attributes['schemaLocation']
+ )
+ if not self._schema:
+ url = self.attributes['schemaLocation']
+ reader = SchemaReader(base_url=schema.getBaseUrl())
+ reader._imports = schema.getImportSchemas()
+ reader._includes = schema.getIncludeSchemas()
+
+ # create schema before loading so chameleon include
+ # will evalute targetNamespace correctly.
+ self._schema = XMLSchema(schema)
+ reader.loadFromURL(url, self._schema)
+
+ return self._schema
+
+
+class AttributeDeclaration(XMLSchemaComponent,\
+ AttributeMarker,\
+ DeclarationMarker):
+ """<attribute name>
+ parent:
+ schema
+ attributes:
+ id -- ID
+ name -- NCName, required
+ type -- QName
+ default -- string
+ fixed -- string
+ contents:
+ annotation?, simpleType?
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'type':None,
+ 'default':None,
+ 'fixed':None}
+ contents = {'xsd':['annotation','simpleType']}
+ tag = 'attribute'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ """ No list or union support
+ """
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ elif component == 'simpleType':
+ self.content = AnonymousSimpleType(self)
+ self.content.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class LocalAttributeDeclaration(AttributeDeclaration,\
+ AttributeMarker,\
+ LocalMarker,\
+ DeclarationMarker):
+ """<attribute name>
+ parent:
+ complexType, restriction, extension, attributeGroup
+ attributes:
+ id -- ID
+ name -- NCName, required
+ type -- QName
+ form -- ('qualified' | 'unqualified'), schema.attributeFormDefault
+ use -- ('optional' | 'prohibited' | 'required'), optional
+ default -- string
+ fixed -- string
+ contents:
+ annotation?, simpleType?
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'type':None,
+ 'form':lambda self: GetSchema(self).getAttributeFormDefault(),
+ 'use':'optional',
+ 'default':None,
+ 'fixed':None}
+ contents = {'xsd':['annotation','simpleType']}
+
+ def __init__(self, parent):
+ AttributeDeclaration.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ elif component == 'simpleType':
+ self.content = AnonymousSimpleType(self)
+ self.content.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class AttributeWildCard(XMLSchemaComponent,\
+ AttributeMarker,\
+ DeclarationMarker,\
+ WildCardMarker):
+ """<anyAttribute>
+ parents:
+ complexType, restriction, extension, attributeGroup
+ attributes:
+ id -- ID
+ namespace -- '##any' | '##other' |
+ (anyURI* | '##targetNamespace' | '##local'), ##any
+ processContents -- 'lax' | 'skip' | 'strict', strict
+ contents:
+ annotation?
+ """
+ attributes = {'id':None,
+ 'namespace':'##any',
+ 'processContents':'strict'}
+ contents = {'xsd':['annotation']}
+ tag = 'anyAttribute'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class AttributeReference(XMLSchemaComponent,\
+ AttributeMarker,\
+ ReferenceMarker):
+ """<attribute ref>
+ parents:
+ complexType, restriction, extension, attributeGroup
+ attributes:
+ id -- ID
+ ref -- QName, required
+ use -- ('optional' | 'prohibited' | 'required'), optional
+ default -- string
+ fixed -- string
+ contents:
+ annotation?
+ """
+ required = ['ref']
+ attributes = {'id':None,
+ 'ref':None,
+ 'use':'optional',
+ 'default':None,
+ 'fixed':None}
+ contents = {'xsd':['annotation']}
+ tag = 'attribute'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def getAttributeDeclaration(self, attribute='ref'):
+ return XMLSchemaComponent.getAttributeDeclaration(self, attribute)
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class AttributeGroupDefinition(XMLSchemaComponent,\
+ AttributeGroupMarker,\
+ DefinitionMarker):
+ """<attributeGroup name>
+ parents:
+ schema, redefine
+ attributes:
+ id -- ID
+ name -- NCName, required
+ contents:
+ annotation?, (attribute | attributeGroup)*, anyAttribute?
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None}
+ contents = {'xsd':['annotation', 'attribute', 'attributeGroup', 'anyAttribute']}
+ tag = 'attributeGroup'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.attr_content = None
+
+ def getAttributeContent(self):
+ return self.attr_content
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for indx in range(len(contents)):
+ component = SplitQName(contents[indx].getTagName())[1]
+ if (component == 'annotation') and (not indx):
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ elif component == 'attribute':
+ if contents[indx].hasattr('name'):
+ content.append(LocalAttributeDeclaration(self))
+ elif contents[indx].hasattr('ref'):
+ content.append(AttributeReference(self))
+ else:
+ raise SchemaError, 'Unknown attribute type'
+ content[-1].fromDom(contents[indx])
+ elif component == 'attributeGroup':
+ content.append(AttributeGroupReference(self))
+ content[-1].fromDom(contents[indx])
+ elif component == 'anyAttribute':
+ if len(contents) != indx+1:
+ raise SchemaError, 'anyAttribute is out of order in %s' %self.getItemTrace()
+ content.append(AttributeWildCard(self))
+ content[-1].fromDom(contents[indx])
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(contents[indx].getTagName())
+
+ self.attr_content = tuple(content)
+
+class AttributeGroupReference(XMLSchemaComponent,\
+ AttributeGroupMarker,\
+ ReferenceMarker):
+ """<attributeGroup ref>
+ parents:
+ complexType, restriction, extension, attributeGroup
+ attributes:
+ id -- ID
+ ref -- QName, required
+ contents:
+ annotation?
+ """
+ required = ['ref']
+ attributes = {'id':None,
+ 'ref':None}
+ contents = {'xsd':['annotation']}
+ tag = 'attributeGroup'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def getAttributeGroup(self, attribute='ref'):
+ """attribute -- attribute with a QName value (eg. type).
+ collection -- check types collection in parent Schema instance
+ """
+ return XMLSchemaComponent.getAttributeGroup(self, attribute)
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+
+######################################################
+# Elements
+#####################################################
+class IdentityConstrants(XMLSchemaComponent):
+ """Allow one to uniquely identify nodes in a document and ensure the
+ integrity of references between them.
+
+ attributes -- dictionary of attributes
+ selector -- XPath to selected nodes
+ fields -- list of XPath to key field
+ """
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.selector = None
+ self.fields = None
+ self.annotation = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ fields = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ elif component == 'selector':
+ self.selector = self.Selector(self)
+ self.selector.fromDom(i)
+ continue
+ elif component == 'field':
+ fields.append(self.Field(self))
+ fields[-1].fromDom(i)
+ continue
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.fields = tuple(fields)
+
+
+ class Constraint(XMLSchemaComponent):
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+ class Selector(Constraint):
+ """<selector xpath>
+ parent:
+ unique, key, keyref
+ attributes:
+ id -- ID
+ xpath -- XPath subset, required
+ contents:
+ annotation?
+ """
+ required = ['xpath']
+ attributes = {'id':None,
+ 'xpath':None}
+ contents = {'xsd':['annotation']}
+ tag = 'selector'
+
+ class Field(Constraint):
+ """<field xpath>
+ parent:
+ unique, key, keyref
+ attributes:
+ id -- ID
+ xpath -- XPath subset, required
+ contents:
+ annotation?
+ """
+ required = ['xpath']
+ attributes = {'id':None,
+ 'xpath':None}
+ contents = {'xsd':['annotation']}
+ tag = 'field'
+
+
+class Unique(IdentityConstrants):
+ """<unique name> Enforce fields are unique w/i a specified scope.
+
+ parent:
+ element
+ attributes:
+ id -- ID
+ name -- NCName, required
+ contents:
+ annotation?, selector, field+
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None}
+ contents = {'xsd':['annotation', 'selector', 'field']}
+ tag = 'unique'
+
+
+class Key(IdentityConstrants):
+ """<key name> Enforce fields are unique w/i a specified scope, and all
+ field values are present w/i document. Fields cannot
+ be nillable.
+
+ parent:
+ element
+ attributes:
+ id -- ID
+ name -- NCName, required
+ contents:
+ annotation?, selector, field+
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None}
+ contents = {'xsd':['annotation', 'selector', 'field']}
+ tag = 'key'
+
+
+class KeyRef(IdentityConstrants):
+ """<keyref name refer> Ensure a match between two sets of values in an
+ instance.
+ parent:
+ element
+ attributes:
+ id -- ID
+ name -- NCName, required
+ refer -- QName, required
+ contents:
+ annotation?, selector, field+
+ """
+ required = ['name', 'refer']
+ attributes = {'id':None,
+ 'name':None,
+ 'refer':None}
+ contents = {'xsd':['annotation', 'selector', 'field']}
+ tag = 'keyref'
+
+
+class ElementDeclaration(XMLSchemaComponent,\
+ ElementMarker,\
+ DeclarationMarker):
+ """<element name>
+ parents:
+ schema
+ attributes:
+ id -- ID
+ name -- NCName, required
+ type -- QName
+ default -- string
+ fixed -- string
+ nillable -- boolean, false
+ abstract -- boolean, false
+ substitutionGroup -- QName
+ block -- ('#all' | ('substition' | 'extension' | 'restriction')*),
+ schema.blockDefault
+ final -- ('#all' | ('extension' | 'restriction')*),
+ schema.finalDefault
+ contents:
+ annotation?, (simpleType,complexType)?, (key | keyref | unique)*
+
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'type':None,
+ 'default':None,
+ 'fixed':None,
+ 'nillable':0,
+ 'abstract':0,
+ 'substitutionGroup':None,
+ 'block':lambda self: self._parent().getBlockDefault(),
+ 'final':lambda self: self._parent().getFinalDefault()}
+ contents = {'xsd':['annotation', 'simpleType', 'complexType', 'key',\
+ 'keyref', 'unique']}
+ tag = 'element'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+ self.constraints = ()
+
+ def isQualified(self):
+ """Global elements are always qualified.
+ """
+ return True
+
+ def getAttribute(self, attribute):
+ """return attribute.
+ If attribute is type and it's None, and no simple or complex content,
+ return the default type "xsd:anyType"
+ """
+ value = XMLSchemaComponent.getAttribute(self, attribute)
+ if attribute != 'type' or value is not None:
+ return value
+
+ if self.content is not None:
+ return None
+
+ parent = self
+ while 1:
+ nsdict = parent.attributes[XMLSchemaComponent.xmlns]
+ for k,v in nsdict.items():
+ if v not in SCHEMA.XSD_LIST: continue
+ return TypeDescriptionComponent((v, 'anyType'))
+
+ if isinstance(parent, WSDLToolsAdapter)\
+ or not hasattr(parent, '_parent'):
+ break
+
+ parent = parent._parent()
+
+ raise SchemaError, 'failed to locate the XSD namespace'
+
+ def getElementDeclaration(self, attribute):
+ raise Warning, 'invalid operation for <%s>' %self.tag
+
+ def getTypeDefinition(self, attribute=None):
+ """If attribute is None, "type" is assumed, return the corresponding
+ representation of the global type definition (TypeDefinition),
+ or the local definition if don't find "type". To maintain backwards
+ compat, if attribute is provided call base class method.
+ """
+ if attribute:
+ return XMLSchemaComponent.getTypeDefinition(self, attribute)
+ gt = XMLSchemaComponent.getTypeDefinition(self, 'type')
+ if gt:
+ return gt
+ return self.content
+
+ def getConstraints(self):
+ return self._constraints
+ def setConstraints(self, constraints):
+ self._constraints = tuple(constraints)
+ constraints = property(getConstraints, setConstraints, None, "tuple of key, keyref, unique constraints")
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ constraints = []
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ elif component == 'simpleType' and not self.content:
+ self.content = AnonymousSimpleType(self)
+ self.content.fromDom(i)
+ elif component == 'complexType' and not self.content:
+ self.content = LocalComplexType(self)
+ self.content.fromDom(i)
+ elif component == 'key':
+ constraints.append(Key(self))
+ constraints[-1].fromDom(i)
+ elif component == 'keyref':
+ constraints.append(KeyRef(self))
+ constraints[-1].fromDom(i)
+ elif component == 'unique':
+ constraints.append(Unique(self))
+ constraints[-1].fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+ self.constraints = constraints
+
+
+class LocalElementDeclaration(ElementDeclaration,\
+ LocalMarker):
+ """<element>
+ parents:
+ all, choice, sequence
+ attributes:
+ id -- ID
+ name -- NCName, required
+ form -- ('qualified' | 'unqualified'), schema.elementFormDefault
+ type -- QName
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+ default -- string
+ fixed -- string
+ nillable -- boolean, false
+ block -- ('#all' | ('extension' | 'restriction')*), schema.blockDefault
+ contents:
+ annotation?, (simpleType,complexType)?, (key | keyref | unique)*
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'form':lambda self: GetSchema(self).getElementFormDefault(),
+ 'type':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1',
+ 'default':None,
+ 'fixed':None,
+ 'nillable':0,
+ 'abstract':0,
+ 'block':lambda self: GetSchema(self).getBlockDefault()}
+ contents = {'xsd':['annotation', 'simpleType', 'complexType', 'key',\
+ 'keyref', 'unique']}
+
+ def isQualified(self):
+ """
+Local elements can be qualified or unqualifed according
+ to the attribute form, or the elementFormDefault. By default
+ local elements are unqualified.
+ """
+ form = self.getAttribute('form')
+ if form == 'qualified':
+ return True
+ if form == 'unqualified':
+ return False
+ raise SchemaError, 'Bad form (%s) for element: %s' %(form, self.getItemTrace())
+
+
+class ElementReference(XMLSchemaComponent,\
+ ElementMarker,\
+ ReferenceMarker):
+ """<element ref>
+ parents:
+ all, choice, sequence
+ attributes:
+ id -- ID
+ ref -- QName, required
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+ contents:
+ annotation?
+ """
+ required = ['ref']
+ attributes = {'id':None,
+ 'ref':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1'}
+ contents = {'xsd':['annotation']}
+ tag = 'element'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def getElementDeclaration(self, attribute=None):
+ """If attribute is None, "ref" is assumed, return the corresponding
+ representation of the global element declaration (ElementDeclaration),
+ To maintain backwards compat, if attribute is provided call base class method.
+ """
+ if attribute:
+ return XMLSchemaComponent.getElementDeclaration(self, attribute)
+ return XMLSchemaComponent.getElementDeclaration(self, 'ref')
+
+ def fromDom(self, node):
+ self.annotation = None
+ self.setAttributes(node)
+ for i in self.getContents(node):
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class ElementWildCard(LocalElementDeclaration, WildCardMarker):
+ """<any>
+ parents:
+ choice, sequence
+ attributes:
+ id -- ID
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+ namespace -- '##any' | '##other' |
+ (anyURI* | '##targetNamespace' | '##local'), ##any
+ processContents -- 'lax' | 'skip' | 'strict', strict
+ contents:
+ annotation?
+ """
+ required = []
+ attributes = {'id':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1',
+ 'namespace':'##any',
+ 'processContents':'strict'}
+ contents = {'xsd':['annotation']}
+ tag = 'any'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def isQualified(self):
+ """
+ Global elements are always qualified, but if processContents
+ are not strict could have dynamically generated local elements.
+ """
+ return GetSchema(self).isElementFormDefaultQualified()
+
+ def getAttribute(self, attribute):
+ """return attribute.
+ """
+ return XMLSchemaComponent.getAttribute(self, attribute)
+
+ def getTypeDefinition(self, attribute):
+ raise Warning, 'invalid operation for <%s>' % self.tag
+
+ def fromDom(self, node):
+ self.annotation = None
+ self.setAttributes(node)
+ for i in self.getContents(node):
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+######################################################
+# Model Groups
+#####################################################
+class Sequence(XMLSchemaComponent,\
+ SequenceMarker):
+ """<sequence>
+ parents:
+ complexType, extension, restriction, group, choice, sequence
+ attributes:
+ id -- ID
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+
+ contents:
+ annotation?, (element | group | choice | sequence | any)*
+ """
+ attributes = {'id':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1'}
+ contents = {'xsd':['annotation', 'element', 'group', 'choice', 'sequence',\
+ 'any']}
+ tag = 'sequence'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ continue
+ elif component == 'element':
+ if i.hasattr('ref'):
+ content.append(ElementReference(self))
+ else:
+ content.append(LocalElementDeclaration(self))
+ elif component == 'group':
+ content.append(ModelGroupReference(self))
+ elif component == 'choice':
+ content.append(Choice(self))
+ elif component == 'sequence':
+ content.append(Sequence(self))
+ elif component == 'any':
+ content.append(ElementWildCard(self))
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ content[-1].fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+class All(XMLSchemaComponent,\
+ AllMarker):
+ """<all>
+ parents:
+ complexType, extension, restriction, group
+ attributes:
+ id -- ID
+ minOccurs -- '0' | '1', 1
+ maxOccurs -- '1', 1
+
+ contents:
+ annotation?, element*
+ """
+ attributes = {'id':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1'}
+ contents = {'xsd':['annotation', 'element']}
+ tag = 'all'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ continue
+ elif component == 'element':
+ if i.hasattr('ref'):
+ content.append(ElementReference(self))
+ else:
+ content.append(LocalElementDeclaration(self))
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ content[-1].fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+class Choice(XMLSchemaComponent,\
+ ChoiceMarker):
+ """<choice>
+ parents:
+ complexType, extension, restriction, group, choice, sequence
+ attributes:
+ id -- ID
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+
+ contents:
+ annotation?, (element | group | choice | sequence | any)*
+ """
+ attributes = {'id':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1'}
+ contents = {'xsd':['annotation', 'element', 'group', 'choice', 'sequence',\
+ 'any']}
+ tag = 'choice'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ continue
+ elif component == 'element':
+ if i.hasattr('ref'):
+ content.append(ElementReference(self))
+ else:
+ content.append(LocalElementDeclaration(self))
+ elif component == 'group':
+ content.append(ModelGroupReference(self))
+ elif component == 'choice':
+ content.append(Choice(self))
+ elif component == 'sequence':
+ content.append(Sequence(self))
+ elif component == 'any':
+ content.append(ElementWildCard(self))
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ content[-1].fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+class ModelGroupDefinition(XMLSchemaComponent,\
+ ModelGroupMarker,\
+ DefinitionMarker):
+ """<group name>
+ parents:
+ redefine, schema
+ attributes:
+ id -- ID
+ name -- NCName, required
+
+ contents:
+ annotation?, (all | choice | sequence)?
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None}
+ contents = {'xsd':['annotation', 'all', 'choice', 'sequence']}
+ tag = 'group'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ continue
+ elif component == 'all' and not self.content:
+ self.content = All(self)
+ elif component == 'choice' and not self.content:
+ self.content = Choice(self)
+ elif component == 'sequence' and not self.content:
+ self.content = Sequence(self)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class ModelGroupReference(XMLSchemaComponent,\
+ ModelGroupMarker,\
+ ReferenceMarker):
+ """<group ref>
+ parents:
+ choice, complexType, extension, restriction, sequence
+ attributes:
+ id -- ID
+ ref -- NCName, required
+ minOccurs -- Whole Number, 1
+ maxOccurs -- (Whole Number | 'unbounded'), 1
+
+ contents:
+ annotation?
+ """
+ required = ['ref']
+ attributes = {'id':None,
+ 'ref':None,
+ 'minOccurs':'1',
+ 'maxOccurs':'1'}
+ contents = {'xsd':['annotation']}
+ tag = 'group'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+
+ def getModelGroupReference(self):
+ return self.getModelGroup('ref')
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+
+class ComplexType(XMLSchemaComponent,\
+ DefinitionMarker,\
+ ComplexMarker):
+ """<complexType name>
+ parents:
+ redefine, schema
+ attributes:
+ id -- ID
+ name -- NCName, required
+ mixed -- boolean, false
+ abstract -- boolean, false
+ block -- ('#all' | ('extension' | 'restriction')*), schema.blockDefault
+ final -- ('#all' | ('extension' | 'restriction')*), schema.finalDefault
+
+ contents:
+ annotation?, (simpleContent | complexContent |
+ ((group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute?))
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'mixed':0,
+ 'abstract':0,
+ 'block':lambda self: self._parent().getBlockDefault(),
+ 'final':lambda self: self._parent().getFinalDefault()}
+ contents = {'xsd':['annotation', 'simpleContent', 'complexContent',\
+ 'group', 'all', 'choice', 'sequence', 'attribute', 'attributeGroup',\
+ 'anyAttribute', 'any']}
+ tag = 'complexType'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+ self.attr_content = None
+
+ def isMixed(self):
+ m = self.getAttribute('mixed')
+ if m == 0 or m == False:
+ return False
+ if isinstance(m, basestring) is True:
+ if m in ('false', '0'):
+ return False
+ if m in ('true', '1'):
+ return True
+
+ raise SchemaError, 'invalid value for attribute mixed(%s): %s'\
+ %(m, self.getItemTrace())
+
+ def getAttributeContent(self):
+ return self.attr_content
+
+ def getElementDeclaration(self, attribute):
+ raise Warning, 'invalid operation for <%s>' %self.tag
+
+ def getTypeDefinition(self, attribute):
+ raise Warning, 'invalid operation for <%s>' %self.tag
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ indx = 0
+ num = len(contents)
+ if not num:
+ return
+
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'annotation':
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ indx += 1
+ component = SplitQName(contents[indx].getTagName())[1]
+
+ self.content = None
+ if component == 'simpleContent':
+ self.content = self.__class__.SimpleContent(self)
+ self.content.fromDom(contents[indx])
+ elif component == 'complexContent':
+ self.content = self.__class__.ComplexContent(self)
+ self.content.fromDom(contents[indx])
+ else:
+ if component == 'all':
+ self.content = All(self)
+ elif component == 'choice':
+ self.content = Choice(self)
+ elif component == 'sequence':
+ self.content = Sequence(self)
+ elif component == 'group':
+ self.content = ModelGroupReference(self)
+
+ if self.content:
+ self.content.fromDom(contents[indx])
+ indx += 1
+
+ self.attr_content = []
+ while indx < num:
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'attribute':
+ if contents[indx].hasattr('ref'):
+ self.attr_content.append(AttributeReference(self))
+ else:
+ self.attr_content.append(LocalAttributeDeclaration(self))
+ elif component == 'attributeGroup':
+ self.attr_content.append(AttributeGroupReference(self))
+ elif component == 'anyAttribute':
+ self.attr_content.append(AttributeWildCard(self))
+ else:
+ raise SchemaError, 'Unknown component (%s): %s' \
+ %(contents[indx].getTagName(),self.getItemTrace())
+ self.attr_content[-1].fromDom(contents[indx])
+ indx += 1
+
+ class _DerivedType(XMLSchemaComponent):
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ # XXX remove attribute derivation, inconsistent
+ self.derivation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ for i in contents:
+ component = SplitQName(i.getTagName())[1]
+ if component in self.__class__.contents['xsd']:
+ if component == 'annotation' and not self.annotation:
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(i)
+ continue
+ elif component == 'restriction' and not self.derivation:
+ self.derivation = self.__class__.Restriction(self)
+ elif component == 'extension' and not self.derivation:
+ self.derivation = self.__class__.Extension(self)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.derivation.fromDom(i)
+ self.content = self.derivation
+
+ class ComplexContent(_DerivedType,\
+ ComplexMarker):
+ """<complexContent>
+ parents:
+ complexType
+ attributes:
+ id -- ID
+ mixed -- boolean, false
+
+ contents:
+ annotation?, (restriction | extension)
+ """
+ attributes = {'id':None,
+ 'mixed':0}
+ contents = {'xsd':['annotation', 'restriction', 'extension']}
+ tag = 'complexContent'
+
+ def isMixed(self):
+ m = self.getAttribute('mixed')
+ if m == 0 or m == False:
+ return False
+ if isinstance(m, basestring) is True:
+ if m in ('false', '0'):
+ return False
+ if m in ('true', '1'):
+ return True
+ raise SchemaError, 'invalid value for attribute mixed(%s): %s'\
+ %(m, self.getItemTrace())
+
+ class _DerivationBase(XMLSchemaComponent):
+ """<extension>,<restriction>
+ parents:
+ complexContent
+ attributes:
+ id -- ID
+ base -- QName, required
+
+ contents:
+ annotation?, (group | all | choice | sequence)?,
+ (attribute | attributeGroup)*, anyAttribute?
+ """
+ required = ['base']
+ attributes = {'id':None,
+ 'base':None }
+ contents = {'xsd':['annotation', 'group', 'all', 'choice',\
+ 'sequence', 'attribute', 'attributeGroup', 'anyAttribute']}
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+ self.attr_content = None
+
+ def getAttributeContent(self):
+ return self.attr_content
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ indx = 0
+ num = len(contents)
+ #XXX ugly
+ if not num:
+ return
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'annotation':
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ indx += 1
+ component = SplitQName(contents[indx].getTagName())[1]
+
+ if component == 'all':
+ self.content = All(self)
+ self.content.fromDom(contents[indx])
+ indx += 1
+ elif component == 'choice':
+ self.content = Choice(self)
+ self.content.fromDom(contents[indx])
+ indx += 1
+ elif component == 'sequence':
+ self.content = Sequence(self)
+ self.content.fromDom(contents[indx])
+ indx += 1
+ elif component == 'group':
+ self.content = ModelGroupReference(self)
+ self.content.fromDom(contents[indx])
+ indx += 1
+ else:
+ self.content = None
+
+ self.attr_content = []
+ while indx < num:
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'attribute':
+ if contents[indx].hasattr('ref'):
+ self.attr_content.append(AttributeReference(self))
+ else:
+ self.attr_content.append(LocalAttributeDeclaration(self))
+ elif component == 'attributeGroup':
+ if contents[indx].hasattr('ref'):
+ self.attr_content.append(AttributeGroupReference(self))
+ else:
+ self.attr_content.append(AttributeGroupDefinition(self))
+ elif component == 'anyAttribute':
+ self.attr_content.append(AttributeWildCard(self))
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(contents[indx].getTagName())
+ self.attr_content[-1].fromDom(contents[indx])
+ indx += 1
+
+ class Extension(_DerivationBase,
+ ExtensionMarker):
+ """<extension base>
+ parents:
+ complexContent
+ attributes:
+ id -- ID
+ base -- QName, required
+
+ contents:
+ annotation?, (group | all | choice | sequence)?,
+ (attribute | attributeGroup)*, anyAttribute?
+ """
+ tag = 'extension'
+
+ class Restriction(_DerivationBase,\
+ RestrictionMarker):
+ """<restriction base>
+ parents:
+ complexContent
+ attributes:
+ id -- ID
+ base -- QName, required
+
+ contents:
+ annotation?, (group | all | choice | sequence)?,
+ (attribute | attributeGroup)*, anyAttribute?
+ """
+ tag = 'restriction'
+
+
+ class SimpleContent(_DerivedType,\
+ SimpleMarker):
+ """<simpleContent>
+ parents:
+ complexType
+ attributes:
+ id -- ID
+
+ contents:
+ annotation?, (restriction | extension)
+ """
+ attributes = {'id':None}
+ contents = {'xsd':['annotation', 'restriction', 'extension']}
+ tag = 'simpleContent'
+
+ class Extension(XMLSchemaComponent,\
+ ExtensionMarker):
+ """<extension base>
+ parents:
+ simpleContent
+ attributes:
+ id -- ID
+ base -- QName, required
+
+ contents:
+ annotation?, (attribute | attributeGroup)*, anyAttribute?
+ """
+ required = ['base']
+ attributes = {'id':None,
+ 'base':None }
+ contents = {'xsd':['annotation', 'attribute', 'attributeGroup',
+ 'anyAttribute']}
+ tag = 'extension'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.attr_content = None
+
+ def getAttributeContent(self):
+ return self.attr_content
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ indx = 0
+ num = len(contents)
+
+ if num:
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'annotation':
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ indx += 1
+ component = SplitQName(contents[indx].getTagName())[1]
+
+ content = []
+ while indx < num:
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'attribute':
+ if contents[indx].hasattr('ref'):
+ content.append(AttributeReference(self))
+ else:
+ content.append(LocalAttributeDeclaration(self))
+ elif component == 'attributeGroup':
+ content.append(AttributeGroupReference(self))
+ elif component == 'anyAttribute':
+ content.append(AttributeWildCard(self))
+ else:
+ raise SchemaError, 'Unknown component (%s)'\
+ %(contents[indx].getTagName())
+ content[-1].fromDom(contents[indx])
+ indx += 1
+ self.attr_content = tuple(content)
+
+
+ class Restriction(XMLSchemaComponent,\
+ RestrictionMarker):
+ """<restriction base>
+ parents:
+ simpleContent
+ attributes:
+ id -- ID
+ base -- QName, required
+
+ contents:
+ annotation?, simpleType?, (enumeration | length |
+ maxExclusive | maxInclusive | maxLength | minExclusive |
+ minInclusive | minLength | pattern | fractionDigits |
+ totalDigits | whiteSpace)*, (attribute | attributeGroup)*,
+ anyAttribute?
+ """
+ required = ['base']
+ attributes = {'id':None,
+ 'base':None }
+ contents = {'xsd':['annotation', 'simpleType', 'attribute',\
+ 'attributeGroup', 'anyAttribute'] + RestrictionMarker.facets}
+ tag = 'restriction'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+ self.attr_content = None
+
+ def getAttributeContent(self):
+ return self.attr_content
+
+ def fromDom(self, node):
+ self.content = []
+ self.setAttributes(node)
+ contents = self.getContents(node)
+
+ indx = 0
+ num = len(contents)
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'annotation':
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ indx += 1
+ component = SplitQName(contents[indx].getTagName())[1]
+
+ content = []
+ while indx < num:
+ component = SplitQName(contents[indx].getTagName())[1]
+ if component == 'attribute':
+ if contents[indx].hasattr('ref'):
+ content.append(AttributeReference(self))
+ else:
+ content.append(LocalAttributeDeclaration(self))
+ elif component == 'attributeGroup':
+ content.append(AttributeGroupReference(self))
+ elif component == 'anyAttribute':
+ content.append(AttributeWildCard(self))
+ elif component == 'simpleType':
+ self.content.append(AnonymousSimpleType(self))
+ self.content[-1].fromDom(contents[indx])
+ else:
+ raise SchemaError, 'Unknown component (%s)'\
+ %(contents[indx].getTagName())
+ content[-1].fromDom(contents[indx])
+ indx += 1
+ self.attr_content = tuple(content)
+
+
+class LocalComplexType(ComplexType,\
+ LocalMarker):
+ """<complexType>
+ parents:
+ element
+ attributes:
+ id -- ID
+ mixed -- boolean, false
+
+ contents:
+ annotation?, (simpleContent | complexContent |
+ ((group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute?))
+ """
+ required = []
+ attributes = {'id':None,
+ 'mixed':0}
+ tag = 'complexType'
+
+
+class SimpleType(XMLSchemaComponent,\
+ DefinitionMarker,\
+ SimpleMarker):
+ """<simpleType name>
+ parents:
+ redefine, schema
+ attributes:
+ id -- ID
+ name -- NCName, required
+ final -- ('#all' | ('extension' | 'restriction' | 'list' | 'union')*),
+ schema.finalDefault
+
+ contents:
+ annotation?, (restriction | list | union)
+ """
+ required = ['name']
+ attributes = {'id':None,
+ 'name':None,
+ 'final':lambda self: self._parent().getFinalDefault()}
+ contents = {'xsd':['annotation', 'restriction', 'list', 'union']}
+ tag = 'simpleType'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def getElementDeclaration(self, attribute):
+ raise Warning, 'invalid operation for <%s>' %self.tag
+
+ def getTypeDefinition(self, attribute):
+ raise Warning, 'invalid operation for <%s>' %self.tag
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ for child in contents:
+ component = SplitQName(child.getTagName())[1]
+ if component == 'annotation':
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(child)
+ continue
+ break
+ else:
+ return
+ if component == 'restriction':
+ self.content = self.__class__.Restriction(self)
+ elif component == 'list':
+ self.content = self.__class__.List(self)
+ elif component == 'union':
+ self.content = self.__class__.Union(self)
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(component)
+ self.content.fromDom(child)
+
+ class Restriction(XMLSchemaComponent,\
+ RestrictionMarker):
+ """<restriction base>
+ parents:
+ simpleType
+ attributes:
+ id -- ID
+ base -- QName, required or simpleType child
+
+ contents:
+ annotation?, simpleType?, (enumeration | length |
+ maxExclusive | maxInclusive | maxLength | minExclusive |
+ minInclusive | minLength | pattern | fractionDigits |
+ totalDigits | whiteSpace)*
+ """
+ attributes = {'id':None,
+ 'base':None }
+ contents = {'xsd':['annotation', 'simpleType']+RestrictionMarker.facets}
+ tag = 'restriction'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+ self.facets = None
+
+ def getAttributeBase(self):
+ return XMLSchemaComponent.getAttribute(self, 'base')
+
+ def getTypeDefinition(self, attribute='base'):
+ return XMLSchemaComponent.getTypeDefinition(self, attribute)
+
+ def getSimpleTypeContent(self):
+ for el in self.content:
+ if el.isSimple(): return el
+ return None
+
+ def fromDom(self, node):
+ self.facets = []
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for indx in range(len(contents)):
+ component = SplitQName(contents[indx].getTagName())[1]
+ if (component == 'annotation') and (not indx):
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ continue
+ elif (component == 'simpleType') and (not indx or indx == 1):
+ content.append(AnonymousSimpleType(self))
+ content[-1].fromDom(contents[indx])
+ elif component in RestrictionMarker.facets:
+ self.facets.append(contents[indx])
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+
+ class Union(XMLSchemaComponent,
+ UnionMarker):
+ """<union>
+ parents:
+ simpleType
+ attributes:
+ id -- ID
+ memberTypes -- list of QNames, required or simpleType child.
+
+ contents:
+ annotation?, simpleType*
+ """
+ attributes = {'id':None,
+ 'memberTypes':None }
+ contents = {'xsd':['annotation', 'simpleType']}
+ tag = 'union'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def fromDom(self, node):
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ content = []
+
+ for indx in range(len(contents)):
+ component = SplitQName(contents[indx].getTagName())[1]
+ if (component == 'annotation') and (not indx):
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ elif (component == 'simpleType'):
+ content.append(AnonymousSimpleType(self))
+ content[-1].fromDom(contents[indx])
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+ self.content = tuple(content)
+
+ class List(XMLSchemaComponent,
+ ListMarker):
+ """<list>
+ parents:
+ simpleType
+ attributes:
+ id -- ID
+ itemType -- QName, required or simpleType child.
+
+ contents:
+ annotation?, simpleType?
+ """
+ attributes = {'id':None,
+ 'itemType':None }
+ contents = {'xsd':['annotation', 'simpleType']}
+ tag = 'list'
+
+ def __init__(self, parent):
+ XMLSchemaComponent.__init__(self, parent)
+ self.annotation = None
+ self.content = None
+
+ def getItemType(self):
+ return self.attributes.get('itemType')
+
+ def getTypeDefinition(self, attribute='itemType'):
+ """
+ return the type refered to by itemType attribute or
+ the simpleType content. If returns None, then the
+ type refered to by itemType is primitive.
+ """
+ tp = XMLSchemaComponent.getTypeDefinition(self, attribute)
+ return tp or self.content
+
+ def fromDom(self, node):
+ self.annotation = None
+ self.content = None
+ self.setAttributes(node)
+ contents = self.getContents(node)
+ for indx in range(len(contents)):
+ component = SplitQName(contents[indx].getTagName())[1]
+ if (component == 'annotation') and (not indx):
+ self.annotation = Annotation(self)
+ self.annotation.fromDom(contents[indx])
+ elif (component == 'simpleType'):
+ self.content = AnonymousSimpleType(self)
+ self.content.fromDom(contents[indx])
+ break
+ else:
+ raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+
+
+class AnonymousSimpleType(SimpleType,\
+ SimpleMarker,\
+ LocalMarker):
+ """<simpleType>
+ parents:
+ attribute, element, list, restriction, union
+ attributes:
+ id -- ID
+
+ contents:
+ annotation?, (restriction | list | union)
+ """
+ required = []
+ attributes = {'id':None}
+ tag = 'simpleType'
+
+
+class Redefine:
+ """<redefine>
+ parents:
+ attributes:
+
+ contents:
+ """
+ tag = 'redefine'
+
+
+###########################
+###########################
+
+
+if sys.version_info[:2] >= (2, 2):
+ tupleClass = tuple
+else:
+ import UserTuple
+ tupleClass = UserTuple.UserTuple
+
+class TypeDescriptionComponent(tupleClass):
+ """Tuple of length 2, consisting of
+ a namespace and unprefixed name.
+ """
+ def __init__(self, args):
+ """args -- (namespace, name)
+ Remove the name's prefix, irrelevant.
+ """
+ if len(args) != 2:
+ raise TypeError, 'expecting tuple (namespace, name), got %s' %args
+ elif args[1].find(':') >= 0:
+ args = (args[0], SplitQName(args[1])[1])
+ tuple.__init__(self, args)
+ return
+
+ def getTargetNamespace(self):
+ return self[0]
+
+ def getName(self):
+ return self[1]
+
+
diff --git a/ZSI/zsi/ZSI/wstools/XMLname.py b/ZSI/zsi/ZSI/wstools/XMLname.py
new file mode 100644
index 0000000..a26a02a
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/XMLname.py
@@ -0,0 +1,90 @@
+"""Translate strings to and from SOAP 1.2 XML name encoding
+
+Implements rules for mapping application defined name to XML names
+specified by the w3 SOAP working group for SOAP version 1.2 in
+Appendix A of "SOAP Version 1.2 Part 2: Adjuncts", W3C Working Draft
+17, December 2001, <http://www.w3.org/TR/soap12-part2/#namemap>
+
+Also see <http://www.w3.org/2000/xp/Group/xmlp-issues>.
+
+Author: Gregory R. Warnes <Gregory.R.Warnes@Pfizer.com>
+Date:: 2002-04-25
+Version 0.9.0
+
+"""
+
+ident = "$Id: XMLname.py 954 2005-02-16 14:45:37Z warnes $"
+
+from re import *
+
+
+def _NCNameChar(x):
+ return x.isalpha() or x.isdigit() or x=="." or x=='-' or x=="_"
+
+
+def _NCNameStartChar(x):
+ return x.isalpha() or x=="_"
+
+
+def _toUnicodeHex(x):
+ hexval = hex(ord(x[0]))[2:]
+ hexlen = len(hexval)
+ # Make hexval have either 4 or 8 digits by prepending 0's
+ if (hexlen==1): hexval = "000" + hexval
+ elif (hexlen==2): hexval = "00" + hexval
+ elif (hexlen==3): hexval = "0" + hexval
+ elif (hexlen==4): hexval = "" + hexval
+ elif (hexlen==5): hexval = "000" + hexval
+ elif (hexlen==6): hexval = "00" + hexval
+ elif (hexlen==7): hexval = "0" + hexval
+ elif (hexlen==8): hexval = "" + hexval
+ else: raise Exception, "Illegal Value returned from hex(ord(x))"
+
+ return "_x"+ hexval + "_"
+
+
+def _fromUnicodeHex(x):
+ return eval( r'u"\u'+x[2:-1]+'"' )
+
+
+def toXMLname(string):
+ """Convert string to a XML name."""
+ if string.find(':') != -1 :
+ (prefix, localname) = string.split(':',1)
+ else:
+ prefix = None
+ localname = string
+
+ T = unicode(localname)
+
+ N = len(localname)
+ X = [];
+ for i in range(N) :
+ if i< N-1 and T[i]==u'_' and T[i+1]==u'x':
+ X.append(u'_x005F_')
+ elif i==0 and N >= 3 and \
+ ( T[0]==u'x' or T[0]==u'X' ) and \
+ ( T[1]==u'm' or T[1]==u'M' ) and \
+ ( T[2]==u'l' or T[2]==u'L' ):
+ X.append(u'_xFFFF_' + T[0])
+ elif (not _NCNameChar(T[i])) or (i==0 and not _NCNameStartChar(T[i])):
+ X.append(_toUnicodeHex(T[i]))
+ else:
+ X.append(T[i])
+
+ if prefix:
+ return "%s:%s" % (prefix, u''.join(X))
+ return u''.join(X)
+
+
+def fromXMLname(string):
+ """Convert XML name to unicode string."""
+
+ retval = sub(r'_xFFFF_','', string )
+
+ def fun( matchobj ):
+ return _fromUnicodeHex( matchobj.group(0) )
+
+ retval = sub(r'_x[0-9A-Za-z]+_', fun, retval )
+
+ return retval
diff --git a/ZSI/zsi/ZSI/wstools/__init__.py b/ZSI/zsi/ZSI/wstools/__init__.py
new file mode 100644
index 0000000..2ca5ced
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/__init__.py
@@ -0,0 +1,9 @@
+#! /usr/bin/env python
+"""WSDL parsing services package for Web Services for Python."""
+
+ident = "$Id: __init__.py 840 2004-12-07 15:54:53Z blunck2 $"
+
+import WSDLTools
+import XMLname
+import logging
+
diff --git a/ZSI/zsi/ZSI/wstools/c14n.py b/ZSI/zsi/ZSI/wstools/c14n.py
new file mode 100755
index 0000000..ecc9b01
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/c14n.py
@@ -0,0 +1,433 @@
+#! /usr/bin/env python
+'''XML Canonicalization
+
+Patches Applied to xml.dom.ext.c14n:
+ http://sourceforge.net/projects/pyxml/
+
+ [ 1444526 ] c14n.py: http://www.w3.org/TR/xml-exc-c14n/ fix
+ -- includes [ 829905 ] c14n.py fix for bug #825115,
+ Date Submitted: 2003-10-24 23:43
+ -- include dependent namespace declarations declared in ancestor nodes
+ (checking attributes and tags),
+ -- handle InclusiveNamespaces PrefixList parameter
+
+This module generates canonical XML of a document or element.
+ http://www.w3.org/TR/2001/REC-xml-c14n-20010315
+and includes a prototype of exclusive canonicalization
+ http://www.w3.org/Signature/Drafts/xml-exc-c14n
+
+Requires PyXML 0.7.0 or later.
+
+Known issues if using Ft.Lib.pDomlette:
+ 1. Unicode
+ 2. does not white space normalize attributes of type NMTOKEN and ID?
+ 3. seems to be include "\n" after importing external entities?
+
+Note, this version processes a DOM tree, and consequently it processes
+namespace nodes as attributes, not from a node's namespace axis. This
+permits simple document and element canonicalization without
+XPath. When XPath is used, the XPath result node list is passed and used to
+determine if the node is in the XPath result list, but little else.
+
+Authors:
+ "Joseph M. Reagle Jr." <reagle@w3.org>
+ "Rich Salz" <rsalz@zolera.com>
+
+$Date: 2006-03-30 15:47:16 -0800 (Thu, 30 Mar 2006) $ by $Author: boverhof $
+'''
+
+_copyright = '''Copyright 2001, Zolera Systems Inc. All Rights Reserved.
+Copyright 2001, MIT. All Rights Reserved.
+
+Distributed under the terms of:
+ Python 2.0 License or later.
+ http://www.python.org/2.0.1/license.html
+or
+ W3C Software License
+ http://www.w3.org/Consortium/Legal/copyright-software-19980720
+'''
+
+import string
+from xml.dom import Node
+try:
+ from xml.ns import XMLNS
+except:
+ class XMLNS:
+ BASE = "http://www.w3.org/2000/xmlns/"
+ XML = "http://www.w3.org/XML/1998/namespace"
+try:
+ import cStringIO
+ StringIO = cStringIO
+except ImportError:
+ import StringIO
+
+_attrs = lambda E: (E.attributes and E.attributes.values()) or []
+_children = lambda E: E.childNodes or []
+_IN_XML_NS = lambda n: n.name.startswith("xmlns")
+_inclusive = lambda n: n.unsuppressedPrefixes == None
+
+
+# Does a document/PI has lesser/greater document order than the
+# first element?
+_LesserElement, _Element, _GreaterElement = range(3)
+
+def _sorter(n1,n2):
+ '''_sorter(n1,n2) -> int
+ Sorting predicate for non-NS attributes.'''
+
+ i = cmp(n1.namespaceURI, n2.namespaceURI)
+ if i: return i
+ return cmp(n1.localName, n2.localName)
+
+
+def _sorter_ns(n1,n2):
+ '''_sorter_ns((n,v),(n,v)) -> int
+ "(an empty namespace URI is lexicographically least)."'''
+
+ if n1[0] == 'xmlns': return -1
+ if n2[0] == 'xmlns': return 1
+ return cmp(n1[0], n2[0])
+
+def _utilized(n, node, other_attrs, unsuppressedPrefixes):
+ '''_utilized(n, node, other_attrs, unsuppressedPrefixes) -> boolean
+ Return true if that nodespace is utilized within the node'''
+ if n.startswith('xmlns:'):
+ n = n[6:]
+ elif n.startswith('xmlns'):
+ n = n[5:]
+ if (n=="" and node.prefix in ["#default", None]) or \
+ n == node.prefix or n in unsuppressedPrefixes:
+ return 1
+ for attr in other_attrs:
+ if n == attr.prefix: return 1
+ # For exclusive need to look at attributes
+ if unsuppressedPrefixes is not None:
+ for attr in _attrs(node):
+ if n == attr.prefix: return 1
+
+ return 0
+
+
+def _inclusiveNamespacePrefixes(node, context, unsuppressedPrefixes):
+ '''http://www.w3.org/TR/xml-exc-c14n/
+ InclusiveNamespaces PrefixList parameter, which lists namespace prefixes that
+ are handled in the manner described by the Canonical XML Recommendation'''
+ inclusive = []
+ if node.prefix:
+ usedPrefixes = ['xmlns:%s' %node.prefix]
+ else:
+ usedPrefixes = ['xmlns']
+
+ for a in _attrs(node):
+ if a.nodeName.startswith('xmlns') or not a.prefix: continue
+ usedPrefixes.append('xmlns:%s' %a.prefix)
+
+ unused_namespace_dict = {}
+ for attr in context:
+ n = attr.nodeName
+ if n in unsuppressedPrefixes:
+ inclusive.append(attr)
+ elif n.startswith('xmlns:') and n[6:] in unsuppressedPrefixes:
+ inclusive.append(attr)
+ elif n.startswith('xmlns') and n[5:] in unsuppressedPrefixes:
+ inclusive.append(attr)
+ elif attr.nodeName in usedPrefixes:
+ inclusive.append(attr)
+ elif n.startswith('xmlns:'):
+ unused_namespace_dict[n] = attr.value
+
+ return inclusive, unused_namespace_dict
+
+#_in_subset = lambda subset, node: not subset or node in subset
+_in_subset = lambda subset, node: subset is None or node in subset # rich's tweak
+
+
+class _implementation:
+ '''Implementation class for C14N. This accompanies a node during it's
+ processing and includes the parameters and processing state.'''
+
+ # Handler for each node type; populated during module instantiation.
+ handlers = {}
+
+ def __init__(self, node, write, **kw):
+ '''Create and run the implementation.'''
+ self.write = write
+ self.subset = kw.get('subset')
+ self.comments = kw.get('comments', 0)
+ self.unsuppressedPrefixes = kw.get('unsuppressedPrefixes')
+ nsdict = kw.get('nsdict', { 'xml': XMLNS.XML, 'xmlns': XMLNS.BASE })
+
+ # Processing state.
+ self.state = (nsdict, {'xml':''}, {}, {}) #0422
+
+ if node.nodeType == Node.DOCUMENT_NODE:
+ self._do_document(node)
+ elif node.nodeType == Node.ELEMENT_NODE:
+ self.documentOrder = _Element # At document element
+ if not _inclusive(self):
+ inherited,unused = _inclusiveNamespacePrefixes(node, self._inherit_context(node),
+ self.unsuppressedPrefixes)
+ self._do_element(node, inherited, unused=unused)
+ else:
+ inherited = self._inherit_context(node)
+ self._do_element(node, inherited)
+ elif node.nodeType == Node.DOCUMENT_TYPE_NODE:
+ pass
+ else:
+ raise TypeError, str(node)
+
+
+ def _inherit_context(self, node):
+ '''_inherit_context(self, node) -> list
+ Scan ancestors of attribute and namespace context. Used only
+ for single element node canonicalization, not for subset
+ canonicalization.'''
+
+ # Collect the initial list of xml:foo attributes.
+ xmlattrs = filter(_IN_XML_NS, _attrs(node))
+
+ # Walk up and get all xml:XXX attributes we inherit.
+ inherited, parent = [], node.parentNode
+ while parent and parent.nodeType == Node.ELEMENT_NODE:
+ for a in filter(_IN_XML_NS, _attrs(parent)):
+ n = a.localName
+ if n not in xmlattrs:
+ xmlattrs.append(n)
+ inherited.append(a)
+ parent = parent.parentNode
+ return inherited
+
+
+ def _do_document(self, node):
+ '''_do_document(self, node) -> None
+ Process a document node. documentOrder holds whether the document
+ element has been encountered such that PIs/comments can be written
+ as specified.'''
+
+ self.documentOrder = _LesserElement
+ for child in node.childNodes:
+ if child.nodeType == Node.ELEMENT_NODE:
+ self.documentOrder = _Element # At document element
+ self._do_element(child)
+ self.documentOrder = _GreaterElement # After document element
+ elif child.nodeType == Node.PROCESSING_INSTRUCTION_NODE:
+ self._do_pi(child)
+ elif child.nodeType == Node.COMMENT_NODE:
+ self._do_comment(child)
+ elif child.nodeType == Node.DOCUMENT_TYPE_NODE:
+ pass
+ else:
+ raise TypeError, str(child)
+ handlers[Node.DOCUMENT_NODE] = _do_document
+
+
+ def _do_text(self, node):
+ '''_do_text(self, node) -> None
+ Process a text or CDATA node. Render various special characters
+ as their C14N entity representations.'''
+ if not _in_subset(self.subset, node): return
+ s = string.replace(node.data, "&", "&amp;")
+ s = string.replace(s, "<", "&lt;")
+ s = string.replace(s, ">", "&gt;")
+ s = string.replace(s, "\015", "&#xD;")
+ if s: self.write(s)
+ handlers[Node.TEXT_NODE] = _do_text
+ handlers[Node.CDATA_SECTION_NODE] = _do_text
+
+
+ def _do_pi(self, node):
+ '''_do_pi(self, node) -> None
+ Process a PI node. Render a leading or trailing #xA if the
+ document order of the PI is greater or lesser (respectively)
+ than the document element.
+ '''
+ if not _in_subset(self.subset, node): return
+ W = self.write
+ if self.documentOrder == _GreaterElement: W('\n')
+ W('<?')
+ W(node.nodeName)
+ s = node.data
+ if s:
+ W(' ')
+ W(s)
+ W('?>')
+ if self.documentOrder == _LesserElement: W('\n')
+ handlers[Node.PROCESSING_INSTRUCTION_NODE] = _do_pi
+
+
+ def _do_comment(self, node):
+ '''_do_comment(self, node) -> None
+ Process a comment node. Render a leading or trailing #xA if the
+ document order of the comment is greater or lesser (respectively)
+ than the document element.
+ '''
+ if not _in_subset(self.subset, node): return
+ if self.comments:
+ W = self.write
+ if self.documentOrder == _GreaterElement: W('\n')
+ W('<!--')
+ W(node.data)
+ W('-->')
+ if self.documentOrder == _LesserElement: W('\n')
+ handlers[Node.COMMENT_NODE] = _do_comment
+
+
+ def _do_attr(self, n, value):
+ ''''_do_attr(self, node) -> None
+ Process an attribute.'''
+
+ W = self.write
+ W(' ')
+ W(n)
+ W('="')
+ s = string.replace(value, "&", "&amp;")
+ s = string.replace(s, "<", "&lt;")
+ s = string.replace(s, '"', '&quot;')
+ s = string.replace(s, '\011', '&#x9')
+ s = string.replace(s, '\012', '&#xA')
+ s = string.replace(s, '\015', '&#xD')
+ W(s)
+ W('"')
+
+
+ def _do_element(self, node, initial_other_attrs = [], unused = None):
+ '''_do_element(self, node, initial_other_attrs = [], unused = {}) -> None
+ Process an element (and its children).'''
+
+ # Get state (from the stack) make local copies.
+ # ns_parent -- NS declarations in parent
+ # ns_rendered -- NS nodes rendered by ancestors
+ # ns_local -- NS declarations relevant to this element
+ # xml_attrs -- Attributes in XML namespace from parent
+ # xml_attrs_local -- Local attributes in XML namespace.
+ # ns_unused_inherited -- not rendered namespaces, used for exclusive
+ ns_parent, ns_rendered, xml_attrs = \
+ self.state[0], self.state[1].copy(), self.state[2].copy() #0422
+
+ ns_unused_inherited = unused
+ if unused is None:
+ ns_unused_inherited = self.state[3].copy()
+
+ ns_local = ns_parent.copy()
+ inclusive = _inclusive(self)
+ xml_attrs_local = {}
+
+ # Divide attributes into NS, XML, and others.
+ other_attrs = []
+ in_subset = _in_subset(self.subset, node)
+ for a in initial_other_attrs + _attrs(node):
+ if a.namespaceURI == XMLNS.BASE:
+ n = a.nodeName
+ if n == "xmlns:": n = "xmlns" # DOM bug workaround
+ ns_local[n] = a.nodeValue
+ elif a.namespaceURI == XMLNS.XML:
+ if inclusive or (in_subset and _in_subset(self.subset, a)): #020925 Test to see if attribute node in subset
+ xml_attrs_local[a.nodeName] = a #0426
+ else:
+ if _in_subset(self.subset, a): #020925 Test to see if attribute node in subset
+ other_attrs.append(a)
+
+# # TODO: exclusive, might need to define xmlns:prefix here
+# if not inclusive and a.prefix is not None and not ns_rendered.has_key('xmlns:%s' %a.prefix):
+# ns_local['xmlns:%s' %a.prefix] = ??
+
+ #add local xml:foo attributes to ancestor's xml:foo attributes
+ xml_attrs.update(xml_attrs_local)
+
+ # Render the node
+ W, name = self.write, None
+ if in_subset:
+ name = node.nodeName
+ if not inclusive:
+ if node.prefix is not None:
+ prefix = 'xmlns:%s' %node.prefix
+ else:
+ prefix = 'xmlns'
+
+ if not ns_rendered.has_key(prefix) and not ns_local.has_key(prefix):
+ if not ns_unused_inherited.has_key(prefix):
+ raise RuntimeError,\
+ 'For exclusive c14n, unable to map prefix "%s" in %s' %(
+ prefix, node)
+
+ ns_local[prefix] = ns_unused_inherited[prefix]
+ del ns_unused_inherited[prefix]
+
+ W('<')
+ W(name)
+
+ # Create list of NS attributes to render.
+ ns_to_render = []
+ for n,v in ns_local.items():
+
+ # If default namespace is XMLNS.BASE or empty,
+ # and if an ancestor was the same
+ if n == "xmlns" and v in [ XMLNS.BASE, '' ] \
+ and ns_rendered.get('xmlns') in [ XMLNS.BASE, '', None ]:
+ continue
+
+ # "omit namespace node with local name xml, which defines
+ # the xml prefix, if its string value is
+ # http://www.w3.org/XML/1998/namespace."
+ if n in ["xmlns:xml", "xml"] \
+ and v in [ 'http://www.w3.org/XML/1998/namespace' ]:
+ continue
+
+
+ # If not previously rendered
+ # and it's inclusive or utilized
+ if (n,v) not in ns_rendered.items():
+ if inclusive or _utilized(n, node, other_attrs, self.unsuppressedPrefixes):
+ ns_to_render.append((n, v))
+ elif not inclusive:
+ ns_unused_inherited[n] = v
+
+ # Sort and render the ns, marking what was rendered.
+ ns_to_render.sort(_sorter_ns)
+ for n,v in ns_to_render:
+ self._do_attr(n, v)
+ ns_rendered[n]=v #0417
+
+ # If exclusive or the parent is in the subset, add the local xml attributes
+ # Else, add all local and ancestor xml attributes
+ # Sort and render the attributes.
+ if not inclusive or _in_subset(self.subset,node.parentNode): #0426
+ other_attrs.extend(xml_attrs_local.values())
+ else:
+ other_attrs.extend(xml_attrs.values())
+ other_attrs.sort(_sorter)
+ for a in other_attrs:
+ self._do_attr(a.nodeName, a.value)
+ W('>')
+
+ # Push state, recurse, pop state.
+ state, self.state = self.state, (ns_local, ns_rendered, xml_attrs, ns_unused_inherited)
+ for c in _children(node):
+ _implementation.handlers[c.nodeType](self, c)
+ self.state = state
+
+ if name: W('</%s>' % name)
+ handlers[Node.ELEMENT_NODE] = _do_element
+
+
+def Canonicalize(node, output=None, **kw):
+ '''Canonicalize(node, output=None, **kw) -> UTF-8
+
+ Canonicalize a DOM document/element node and all descendents.
+ Return the text; if output is specified then output.write will
+ be called to output the text and None will be returned
+ Keyword parameters:
+ nsdict: a dictionary of prefix:uri namespace entries
+ assumed to exist in the surrounding context
+ comments: keep comments if non-zero (default is 0)
+ subset: Canonical XML subsetting resulting from XPath
+ (default is [])
+ unsuppressedPrefixes: do exclusive C14N, and this specifies the
+ prefixes that should be inherited.
+ '''
+ if output:
+ apply(_implementation, (node, output.write), kw)
+ else:
+ s = StringIO.StringIO()
+ apply(_implementation, (node, s.write), kw)
+ return s.getvalue()
diff --git a/ZSI/zsi/ZSI/wstools/logging.py b/ZSI/zsi/ZSI/wstools/logging.py
new file mode 100644
index 0000000..240c134
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/logging.py
@@ -0,0 +1,110 @@
+# Copyright (c) 2003, The Regents of the University of California,
+# through Lawrence Berkeley National Laboratory (subject to receipt of
+# any required approvals from the U.S. Dept. of Energy). All rights
+# reserved.
+#
+"""Logging"""
+ident = "$Id: logging.py 1204 2006-05-03 00:13:47Z boverhof $"
+import sys
+
+WARN = 1
+DEBUG = 2
+
+
+class ILogger:
+ '''Logger interface, by default this class
+ will be used and logging calls are no-ops.
+ '''
+ level = 0
+ def __init__(self, msg):
+ return
+ def warning(self, *args):
+ return
+ def debug(self, *args):
+ return
+ def error(self, *args):
+ return
+ def setLevel(cls, level):
+ cls.level = level
+ setLevel = classmethod(setLevel)
+
+ debugOn = lambda self: self.level >= DEBUG
+ warnOn = lambda self: self.level >= WARN
+
+
+class BasicLogger(ILogger):
+ last = ''
+
+ def __init__(self, msg, out=sys.stdout):
+ self.msg, self.out = msg, out
+
+ def warning(self, msg, *args):
+ if self.warnOn() is False: return
+ if BasicLogger.last != self.msg:
+ BasicLogger.last = self.msg
+ print >>self, "---- ", self.msg, " ----"
+ print >>self, " %s " %BasicLogger.WARN,
+ print >>self, msg %args
+ WARN = '[WARN]'
+ def debug(self, msg, *args):
+ if self.debugOn() is False: return
+ if BasicLogger.last != self.msg:
+ BasicLogger.last = self.msg
+ print >>self, "---- ", self.msg, " ----"
+ print >>self, " %s " %BasicLogger.DEBUG,
+ print >>self, msg %args
+ DEBUG = '[DEBUG]'
+ def error(self, msg, *args):
+ if BasicLogger.last != self.msg:
+ BasicLogger.last = self.msg
+ print >>self, "---- ", self.msg, " ----"
+ print >>self, " %s " %BasicLogger.ERROR,
+ print >>self, msg %args
+ ERROR = '[ERROR]'
+
+ def write(self, *args):
+ '''Write convenience function; writes strings.
+ '''
+ for s in args: self.out.write(s)
+
+_LoggerClass = BasicLogger
+
+def setBasicLogger():
+ '''Use Basic Logger.
+ '''
+ setLoggerClass(BasicLogger)
+ BasicLogger.setLevel(0)
+
+def setBasicLoggerWARN():
+ '''Use Basic Logger.
+ '''
+ setLoggerClass(BasicLogger)
+ BasicLogger.setLevel(WARN)
+
+def setBasicLoggerDEBUG():
+ '''Use Basic Logger.
+ '''
+ setLoggerClass(BasicLogger)
+ BasicLogger.setLevel(DEBUG)
+
+def setLoggerClass(loggingClass):
+ '''Set Logging Class.
+ '''
+ assert issubclass(loggingClass, ILogger), 'loggingClass must subclass ILogger'
+ global _LoggerClass
+ _LoggerClass = loggingClass
+
+def setLevel(level=0):
+ '''Set Global Logging Level.
+ '''
+ ILogger.level = level
+
+def getLevel():
+ return ILogger.level
+
+def getLogger(msg):
+ '''Return instance of Logging class.
+ '''
+ return _LoggerClass(msg)
+
+
diff --git a/ZSI/zsi/ZSI/wstools/test/__init__.py b/ZSI/zsi/ZSI/wstools/test/__init__.py
new file mode 100644
index 0000000..d5a5350
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/test/__init__.py
@@ -0,0 +1,5 @@
+#! /usr/bin/env python
+"""wstools.WSDLTools.WSDLReader tests directory."""
+
+import utils
+
diff --git a/ZSI/zsi/ZSI/wstools/test/test_t1.py b/ZSI/zsi/ZSI/wstools/test/test_t1.py
new file mode 100644
index 0000000..5c33899
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/test/test_t1.py
@@ -0,0 +1,20 @@
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import unittest
+import test_wsdl
+import utils
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(test_wsdl.makeTestSuite("services_by_file"))
+ return suite
+
+def main():
+ loader = utils.MatchTestLoader(True, None, "makeTestSuite")
+ unittest.main(defaultTest="makeTestSuite", testLoader=loader)
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/ZSI/wstools/test/test_wsdl.py b/ZSI/zsi/ZSI/wstools/test/test_wsdl.py
new file mode 100644
index 0000000..90b0c4d
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/test/test_wsdl.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+import sys, unittest
+import ConfigParser
+from ZSI.wstools.Utility import DOM
+from ZSI.wstools.WSDLTools import WSDLReader
+from ZSI.wstools.TimeoutSocket import TimeoutError
+
+class WSDLToolsTestCase(unittest.TestCase):
+
+ def __init__(self, methodName='runTest'):
+ unittest.TestCase.__init__(self, methodName)
+
+ def setUp(self):
+ self.path = nameGenerator.next()
+ print self.path
+ sys.stdout.flush()
+
+ def __str__(self):
+ teststr = unittest.TestCase.__str__(self)
+ if hasattr(self, "path"):
+ return "%s: %s" % (teststr, self.path )
+ else:
+ return "%s" % (teststr)
+
+ def checkWSDLCollection(self, tag_name, component, key='name'):
+ if self.wsdl is None:
+ return
+ definition = self.wsdl.document.documentElement
+ version = DOM.WSDLUriToVersion(definition.namespaceURI)
+ nspname = DOM.GetWSDLUri(version)
+ for node in DOM.getElements(definition, tag_name, nspname):
+ name = DOM.getAttr(node, key)
+ comp = component[name]
+ self.failUnlessEqual(eval('comp.%s' %key), name)
+
+ def checkXSDCollection(self, tag_name, component, node, key='name'):
+ for cnode in DOM.getElements(node, tag_name):
+ name = DOM.getAttr(cnode, key)
+ component[name]
+
+ def test_all(self):
+ try:
+ if self.path[:7] == 'http://':
+ self.wsdl = WSDLReader().loadFromURL(self.path)
+ else:
+ self.wsdl = WSDLReader().loadFromFile(self.path)
+
+ except TimeoutError:
+ print "connection timed out"
+ sys.stdout.flush()
+ return
+ except:
+ self.path = self.path + ": load failed, unable to start"
+ raise
+
+ try:
+ self.checkWSDLCollection('service', self.wsdl.services)
+ except:
+ self.path = self.path + ": wsdl.services"
+ raise
+
+ try:
+ self.checkWSDLCollection('message', self.wsdl.messages)
+ except:
+ self.path = self.path + ": wsdl.messages"
+ raise
+
+ try:
+ self.checkWSDLCollection('portType', self.wsdl.portTypes)
+ except:
+ self.path = self.path + ": wsdl.portTypes"
+ raise
+
+ try:
+ self.checkWSDLCollection('binding', self.wsdl.bindings)
+ except:
+ self.path = self.path + ": wsdl.bindings"
+ raise
+
+ try:
+ self.checkWSDLCollection('import', self.wsdl.imports, key='namespace')
+ except:
+ self.path = self.path + ": wsdl.imports"
+ raise
+
+ try:
+ for key in self.wsdl.types.keys():
+ schema = self.wsdl.types[key]
+ self.failUnlessEqual(key, schema.getTargetNamespace())
+
+ definition = self.wsdl.document.documentElement
+ version = DOM.WSDLUriToVersion(definition.namespaceURI)
+ nspname = DOM.GetWSDLUri(version)
+ for node in DOM.getElements(definition, 'types', nspname):
+ for snode in DOM.getElements(node, 'schema'):
+ tns = DOM.findTargetNS(snode)
+ schema = self.wsdl.types[tns]
+ self.schemaAttributesDeclarations(schema, snode)
+ self.schemaAttributeGroupDeclarations(schema, snode)
+ self.schemaElementDeclarations(schema, snode)
+ self.schemaTypeDefinitions(schema, snode)
+ except:
+ self.path = self.path + ": wsdl.types"
+ raise
+
+ if self.wsdl.extensions:
+ print 'No check for WSDLTools(%s) Extensions:' %(self.wsdl.name)
+ for ext in self.wsdl.extensions: print '\t', ext
+
+ def schemaAttributesDeclarations(self, schema, node):
+ self.checkXSDCollection('attribute', schema.attr_decl, node)
+
+ def schemaAttributeGroupDeclarations(self, schema, node):
+ self.checkXSDCollection('group', schema.attr_groups, node)
+
+ def schemaElementDeclarations(self, schema, node):
+ self.checkXSDCollection('element', schema.elements, node)
+
+ def schemaTypeDefinitions(self, schema, node):
+ self.checkXSDCollection('complexType', schema.types, node)
+ self.checkXSDCollection('simpleType', schema.types, node)
+
+
+def setUpOptions(section):
+ cp = ConfigParser.ConfigParser()
+ cp.read('config.txt')
+ if not cp.sections():
+ print 'fatal error: configuration file config.txt not present'
+ sys.exit(0)
+ if not cp.has_section(section):
+ print '%s section not present in configuration file, exiting' % section
+ sys.exit(0)
+ return cp, len(cp.options(section))
+
+def getOption(cp, section):
+ for name, value in cp.items(section):
+ yield value
+
+def makeTestSuite(section='services_by_file'):
+ global nameGenerator
+
+ cp, numTests = setUpOptions(section)
+ nameGenerator = getOption(cp, section)
+ suite = unittest.TestSuite()
+ for i in range(0, numTests):
+ suite.addTest(unittest.makeSuite(WSDLToolsTestCase, 'test_'))
+ return suite
+
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+
+if __name__ == "__main__" : main()
diff --git a/ZSI/zsi/ZSI/wstools/test/test_wstools.py b/ZSI/zsi/ZSI/wstools/test/test_wstools.py
new file mode 100644
index 0000000..0e0f958
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/test/test_wstools.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+import unittest, tarfile, os, ConfigParser
+import test_wsdl
+
+
+SECTION='files'
+CONFIG_FILE = 'config.txt'
+
+def extractFiles(section, option):
+ config = ConfigParser.ConfigParser()
+ config.read(CONFIG_FILE)
+ archives = config.get(section, option)
+ archives = eval(archives)
+ for file in archives:
+ tar = tarfile.open(file)
+ if not os.access(tar.membernames[0], os.R_OK):
+ for i in tar.getnames():
+ tar.extract(i)
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(test_wsdl.makeTestSuite("services_by_file"))
+ return suite
+
+def main():
+ extractFiles(SECTION, 'archives')
+ unittest.main(defaultTest="makeTestSuite")
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/ZSI/wstools/test/test_wstools_net.py b/ZSI/zsi/ZSI/wstools/test/test_wstools_net.py
new file mode 100644
index 0000000..880cff3
--- /dev/null
+++ b/ZSI/zsi/ZSI/wstools/test/test_wstools_net.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import unittest
+import test_wsdl
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(test_wsdl.makeTestSuite("services_by_http"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/doc/examples/client/receive_response/complex/manual/ComplexTypes.py b/ZSI/zsi/doc/examples/client/receive_response/complex/manual/ComplexTypes.py
new file mode 100644
index 0000000..00918f7
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/receive_response/complex/manual/ComplexTypes.py
@@ -0,0 +1,13 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'User')
diff --git a/ZSI/zsi/doc/examples/client/receive_response/complex/manual/client.py b/ZSI/zsi/doc/examples/client/receive_response/complex/manual/client.py
new file mode 100755
index 0000000..d1737b5
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/receive_response/complex/manual/client.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+
+import ComplexTypes as MyComplexTypes
+
+import sys
+
+def main():
+ server = ServiceProxy('../binding.wsdl',
+ typesmodule=MyComplexTypes,
+ tracefile=sys.stdout)
+
+ user = server.GetUser('john_doe')
+ print ' Age: %d' % user.Age
+ print ' Name: %s' % user.Name
+ print 'UserId: %s' % user.UserId
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/client/receive_response/complex/server/server.py b/ZSI/zsi/doc/examples/client/receive_response/complex/server/server.py
new file mode 100755
index 0000000..3379339
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/receive_response/complex/server/server.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from Registration_services import GetUserResponseWrapper
+from Registration_services_types import ns1
+
+
+def GetUser(user_id):
+ user = ns1.User_Def()
+ user._UserId = user_id
+ user._Name = "John Doe"
+ user._Age = 32
+
+ response = GetUserResponseWrapper()
+ response._User = user
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/ZSI/zsi/doc/examples/client/receive_response/complex/wsdl2py/client.py b/ZSI/zsi/doc/examples/client/receive_response/complex/wsdl2py/client.py
new file mode 100755
index 0000000..904bb51
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/receive_response/complex/wsdl2py/client.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+import sys
+
+from Registration_services import *
+
+def main():
+ locator = RegistrationServiceLocator()
+ service = locator.getRegistration(tracefile=sys.stdout)
+
+ request = GetUserRequestWrapper()
+ request._UserId = "john_doe"
+
+ response = service.GetUser(request)
+ print ' Age: %d' % response._User._Age
+ print ' Name: %s' % response._User._Name
+ print 'UserId: %s' % response._User._UserId
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/client/receive_response/simple/ServiceProxy/client.py b/ZSI/zsi/doc/examples/client/receive_response/simple/ServiceProxy/client.py
new file mode 100755
index 0000000..de19790
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/receive_response/simple/ServiceProxy/client.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ server = ServiceProxy('../binding.wsdl', use_wsdl=True)
+
+ print ' Sending: %s' % MESSAGE
+ response = server.echo(Message=MESSAGE)
+ print 'Response: %s' % response['Message']
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/client/receive_response/simple/server/server.py b/ZSI/zsi/doc/examples/client/receive_response/simple/server/server.py
new file mode 100755
index 0000000..c878cd8
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/receive_response/simple/server/server.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from Example_services import EchoResponseWrapper
+
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/ZSI/zsi/doc/examples/client/receive_response/simple/wsdl2py/client.py b/ZSI/zsi/doc/examples/client/receive_response/simple/wsdl2py/client.py
new file mode 100755
index 0000000..cff9713
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/receive_response/simple/wsdl2py/client.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+import Example_services
+
+MESSAGE = "Hello from Python!"
+
+
+def main():
+ locator = Example_services.ExampleServiceLocator()
+ port = locator.getExample()
+ request = Example_services.EchoRequestWrapper()
+ request._Message = MESSAGE
+
+ try:
+ print ' Sending: %s' % MESSAGE
+ response = port.echo(request)
+ print 'Response: %s' % response._Message
+ except Exception, e:
+ print e
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/client/send_request/complex/manual/ComplexTypes.py b/ZSI/zsi/doc/examples/client/send_request/complex/manual/ComplexTypes.py
new file mode 100644
index 0000000..fa62530
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/send_request/complex/manual/ComplexTypes.py
@@ -0,0 +1,13 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'types:User')
diff --git a/ZSI/zsi/doc/examples/client/send_request/complex/manual/client.py b/ZSI/zsi/doc/examples/client/send_request/complex/manual/client.py
new file mode 100755
index 0000000..05cca14
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/send_request/complex/manual/client.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+from ComplexTypes import User
+
+from ZSI import ServiceProxy
+
+import sys
+
+
+def main():
+ user = User('john_doe', 'John Doe', 25)
+ nsdict = { 'types' : 'http://pycon.org/types' }
+ registration = ServiceProxy('../binding.wsdl',
+ nsdict=nsdict,
+ tracefile=sys.stdout)
+ response = registration.RegisterUser(user)
+ print response
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/client/send_request/complex/server/ComplexTypes.py b/ZSI/zsi/doc/examples/client/send_request/complex/server/ComplexTypes.py
new file mode 100644
index 0000000..00918f7
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/send_request/complex/server/ComplexTypes.py
@@ -0,0 +1,13 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'User')
diff --git a/ZSI/zsi/doc/examples/client/send_request/complex/server/server.py b/ZSI/zsi/doc/examples/client/send_request/complex/server/server.py
new file mode 100755
index 0000000..0459540
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/send_request/complex/server/server.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+import ComplexTypes as MyComplexTypes
+
+from Registration_services import RegisterUserResponseWrapper
+
+
+def RegisterUser(user):
+ response = RegisterUserResponseWrapper()
+ response._Message = "OK"
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080, typesmodule=(MyComplexTypes,))
diff --git a/ZSI/zsi/doc/examples/client/send_request/complex/wsdl2py/client.py b/ZSI/zsi/doc/examples/client/send_request/complex/wsdl2py/client.py
new file mode 100755
index 0000000..c58903e
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/send_request/complex/wsdl2py/client.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+from Registration_services import *
+
+import sys
+
+def main():
+ user = ns1.User_Def()
+ user._UserId = 'john_doe'
+ user._Name = 'John Doe'
+ user._Age = 25
+
+ locator = RegistrationServiceLocator()
+ registration = locator.getRegistration(tracefile=sys.stdout)
+
+ request = RegisterUserRequestWrapper()
+ request._User = user
+ response = registration.RegisterUser(request)
+ print response._Message
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/client/send_request/simple/Binding/client.py b/ZSI/zsi/doc/examples/client/send_request/simple/Binding/client.py
new file mode 100755
index 0000000..57a409f
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/send_request/simple/Binding/client.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+from ZSI import Binding
+
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ binding = Binding(url='http://localhost:8080/server.py')
+ print ' Sending: %s' % MESSAGE
+ response = binding.echo(MESSAGE)
+ print 'Response: %s' % MESSAGE
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/client/send_request/simple/ServiceProxy/client.py b/ZSI/zsi/doc/examples/client/send_request/simple/ServiceProxy/client.py
new file mode 100755
index 0000000..de19790
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/send_request/simple/ServiceProxy/client.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ server = ServiceProxy('../binding.wsdl', use_wsdl=True)
+
+ print ' Sending: %s' % MESSAGE
+ response = server.echo(Message=MESSAGE)
+ print 'Response: %s' % response['Message']
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/client/send_request/simple/server/server.py b/ZSI/zsi/doc/examples/client/send_request/simple/server/server.py
new file mode 100755
index 0000000..97c5c38
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/send_request/simple/server/server.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from Example_services import *
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
+
diff --git a/ZSI/zsi/doc/examples/client/send_request/simple/wsdl2py/client.py b/ZSI/zsi/doc/examples/client/send_request/simple/wsdl2py/client.py
new file mode 100755
index 0000000..cff9713
--- /dev/null
+++ b/ZSI/zsi/doc/examples/client/send_request/simple/wsdl2py/client.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+import Example_services
+
+MESSAGE = "Hello from Python!"
+
+
+def main():
+ locator = Example_services.ExampleServiceLocator()
+ port = locator.getExample()
+ request = Example_services.EchoRequestWrapper()
+ request._Message = MESSAGE
+
+ try:
+ print ' Sending: %s' % MESSAGE
+ response = port.echo(request)
+ print 'Response: %s' % response._Message
+ except Exception, e:
+ print e
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/server/receive_request/complex/client/ComplexTypes.py b/ZSI/zsi/doc/examples/server/receive_request/complex/client/ComplexTypes.py
new file mode 100644
index 0000000..fa62530
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/receive_request/complex/client/ComplexTypes.py
@@ -0,0 +1,13 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'types:User')
diff --git a/ZSI/zsi/doc/examples/server/receive_request/complex/client/client.py b/ZSI/zsi/doc/examples/server/receive_request/complex/client/client.py
new file mode 100755
index 0000000..16b6ff6
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/receive_request/complex/client/client.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+
+from ComplexTypes import User
+
+import sys
+
+def main():
+ user = User('john_doe', 'John Doe', 25)
+ nsdict = { 'types' : 'http://pycon.org/typs' }
+ registration = ServiceProxy('../binding.wsdl',
+ nsdict=nsdict,
+ tracefile=sys.stdout)
+ response = registration.RegisterUser(user)
+ print response
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/server/receive_request/complex/manual/ComplexTypes.py b/ZSI/zsi/doc/examples/server/receive_request/complex/manual/ComplexTypes.py
new file mode 100644
index 0000000..00918f7
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/receive_request/complex/manual/ComplexTypes.py
@@ -0,0 +1,13 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'User')
diff --git a/ZSI/zsi/doc/examples/server/receive_request/complex/manual/server.py b/ZSI/zsi/doc/examples/server/receive_request/complex/manual/server.py
new file mode 100755
index 0000000..b7d1f48
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/receive_request/complex/manual/server.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+import ComplexTypes as MyComplexTypes
+
+
+def RegisterUser(user):
+ return "OK"
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080, typesmodule=(MyComplexTypes,))
diff --git a/ZSI/zsi/doc/examples/server/receive_request/complex/wsdl2py/server.py b/ZSI/zsi/doc/examples/server/receive_request/complex/wsdl2py/server.py
new file mode 100755
index 0000000..92e1abc
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/receive_request/complex/wsdl2py/server.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+import Registration_services_types
+from Registration_services import RegisterUserResponseWrapper
+
+def RegisterUser(user):
+ response = RegisterUserResponseWrapper()
+ response._Message = "OK"
+ return response
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080, typesmodule=(Registration_services_types,))
diff --git a/ZSI/zsi/doc/examples/server/receive_request/simple/CGI/cgi.py b/ZSI/zsi/doc/examples/server/receive_request/simple/CGI/cgi.py
new file mode 100755
index 0000000..5fbf61a
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/receive_request/simple/CGI/cgi.py
@@ -0,0 +1,10 @@
+from Echo_services import EchoResponseWrapper
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
+
+
+from ZSI import dispatch
+dispatch.AsCGI()
diff --git a/ZSI/zsi/doc/examples/server/receive_request/simple/client/client.py b/ZSI/zsi/doc/examples/server/receive_request/simple/client/client.py
new file mode 100755
index 0000000..78bfe9e
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/receive_request/simple/client/client.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+import sys
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ server = ServiceProxy('../binding.wsdl', use_wsdl=True)
+
+ print ' Sending: %s' % MESSAGE
+ response = server.echo(Message=MESSAGE)
+ print 'Response: %s' % response['Message']
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/server/receive_request/simple/mod_python/MyHandler.py b/ZSI/zsi/doc/examples/server/receive_request/simple/mod_python/MyHandler.py
new file mode 100644
index 0000000..b8a2201
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/receive_request/simple/mod_python/MyHandler.py
@@ -0,0 +1,6 @@
+from Example_services import EchoResponseWrapper
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
diff --git a/ZSI/zsi/doc/examples/server/receive_request/simple/mod_python/mod_python.py b/ZSI/zsi/doc/examples/server/receive_request/simple/mod_python/mod_python.py
new file mode 100644
index 0000000..771265e
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/receive_request/simple/mod_python/mod_python.py
@@ -0,0 +1,10 @@
+from ZSI import dispatch
+from mod_python import apache
+
+mod = __import__('encodings.utf_8', globals(), locals(), '*')
+mod = __import__('encodings.utf_16_be', globals(), locals(), '*')
+
+import MyHandler
+def handler(req):
+ dispatch.AsHandler(modules=(MyHandler,), request=req)
+ return apache.OK
diff --git a/ZSI/zsi/doc/examples/server/receive_request/simple/standalone/standalone.py b/ZSI/zsi/doc/examples/server/receive_request/simple/standalone/standalone.py
new file mode 100755
index 0000000..c878cd8
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/receive_request/simple/standalone/standalone.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from Example_services import EchoResponseWrapper
+
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/ZSI/zsi/doc/examples/server/send_response/complex/client/client.py b/ZSI/zsi/doc/examples/server/send_response/complex/client/client.py
new file mode 100755
index 0000000..904bb51
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/send_response/complex/client/client.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+import sys
+
+from Registration_services import *
+
+def main():
+ locator = RegistrationServiceLocator()
+ service = locator.getRegistration(tracefile=sys.stdout)
+
+ request = GetUserRequestWrapper()
+ request._UserId = "john_doe"
+
+ response = service.GetUser(request)
+ print ' Age: %d' % response._User._Age
+ print ' Name: %s' % response._User._Name
+ print 'UserId: %s' % response._User._UserId
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/server/send_response/complex/manual/ComplexTypes.py b/ZSI/zsi/doc/examples/server/send_response/complex/manual/ComplexTypes.py
new file mode 100644
index 0000000..7dae26a
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/send_response/complex/manual/ComplexTypes.py
@@ -0,0 +1,14 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'User')
+
diff --git a/ZSI/zsi/doc/examples/server/send_response/complex/manual/server.py b/ZSI/zsi/doc/examples/server/send_response/complex/manual/server.py
new file mode 100755
index 0000000..525ca51
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/send_response/complex/manual/server.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from ComplexTypes import User
+
+def GetUser(user_id):
+ user = User(user_id, 'John Doe', 28)
+ return user
+
+if __name__ == '__main__':
+ nsdict = { 'types' : 'http://pycon.org/types' }
+ dispatch.AsServer(port=8080, nsdict=nsdict)
diff --git a/ZSI/zsi/doc/examples/server/send_response/complex/wsdl2py/server.py b/ZSI/zsi/doc/examples/server/send_response/complex/wsdl2py/server.py
new file mode 100755
index 0000000..3379339
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/send_response/complex/wsdl2py/server.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from Registration_services import GetUserResponseWrapper
+from Registration_services_types import ns1
+
+
+def GetUser(user_id):
+ user = ns1.User_Def()
+ user._UserId = user_id
+ user._Name = "John Doe"
+ user._Age = 32
+
+ response = GetUserResponseWrapper()
+ response._User = user
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/ZSI/zsi/doc/examples/server/send_response/simple/no_typecode/client.py b/ZSI/zsi/doc/examples/server/send_response/simple/no_typecode/client.py
new file mode 100755
index 0000000..e65af11
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/send_response/simple/no_typecode/client.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+import sys
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ server = ServiceProxy('../binding.wsdl', use_wsdl=False)
+
+ print ' Sending: %s' % MESSAGE
+ response = server.echo(MESSAGE)
+ print 'Response: %s' % response
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/server/send_response/simple/no_typecode/server.py b/ZSI/zsi/doc/examples/server/send_response/simple/no_typecode/server.py
new file mode 100755
index 0000000..14abe3a
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/send_response/simple/no_typecode/server.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+
+def echo(message):
+ return message
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/ZSI/zsi/doc/examples/server/send_response/simple/wsdl2py/client.py b/ZSI/zsi/doc/examples/server/send_response/simple/wsdl2py/client.py
new file mode 100755
index 0000000..78bfe9e
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/send_response/simple/wsdl2py/client.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+import sys
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ server = ServiceProxy('../binding.wsdl', use_wsdl=True)
+
+ print ' Sending: %s' % MESSAGE
+ response = server.echo(Message=MESSAGE)
+ print 'Response: %s' % response['Message']
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/doc/examples/server/send_response/simple/wsdl2py/server.py b/ZSI/zsi/doc/examples/server/send_response/simple/wsdl2py/server.py
new file mode 100755
index 0000000..ccaf220
--- /dev/null
+++ b/ZSI/zsi/doc/examples/server/send_response/simple/wsdl2py/server.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+from Example_services import EchoResponseWrapper
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/ZSI/zsi/interop/client.py b/ZSI/zsi/interop/client.py
new file mode 100755
index 0000000..7dda6fe
--- /dev/null
+++ b/ZSI/zsi/interop/client.py
@@ -0,0 +1,102 @@
+#! /usr/bin/env python
+import getopt, socket, sys
+from cpackets import testlist
+
+try:
+ (opts, args) = getopt.getopt(sys.argv[1:],
+ 'h:lp:qst:w',
+ ( 'host=', 'list', 'port=',
+ 'quit', 'statusonly', 'test=', 'wsdl', 'help'))
+except getopt.GetoptError, e:
+ print >>sys.stderr, sys.argv[0] + ': ' + str(e)
+ sys.exit(1)
+if args:
+ print sys.argv[0] + ': Usage error; try --help.'
+ sys.exit(1)
+
+hostname, portnum, tests, quitting, getwsdl, verbose = \
+ 'localhost', 1122, [0,1], 0, 0, 1
+for opt, val in opts:
+ if opt in [ '--help' ]:
+ print '''Options include:
+ --host HOST (-h HOST) Name of server host
+ --port PORT (-p PORT) Port server is listening on
+ --quit (-q) Send server a QUIT command
+ --testnum 1,2,3 (-t ...) Run comma-separated tests; use * or all for all
+ --list (-l) List tests (brief description)
+ --statusonly (-s) Do not output reply packets; just status code
+ --wsdl (-w) Get the WSDL file
+Default is -h%s -p%d -t%s''' % \
+ (hostname, portnum, ','.join([str(x) for x in tests]))
+ sys.exit(0)
+ if opt in [ '-h', '--host' ]:
+ hostname = val
+ elif opt in [ '-p', '--port' ]:
+ portnum = int(val)
+ elif opt in [ '-s', '--statusonly' ]:
+ verbose = 0
+ elif opt in [ '-q', '--quit' ]:
+ quitting = 1
+ elif opt in [ '-t', '--testnum' ]:
+ if val in [ '*', 'all' ]:
+ tests = range(len(testlist))
+ else:
+ tests = [ int(t) for t in val.split(',') ]
+ elif opt in [ '-l', '--list' ]:
+ for i in range(len(testlist)):
+ print i, testlist[i][0]
+ sys.exit(0)
+ elif opt in [ '-w', '--wsdl' ]:
+ getwsdl = 1
+
+if quitting:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ try:
+ s.connect((hostname, portnum))
+ except socket.error, e:
+ if e.args[1] == 'Connection refused': sys.exit(0)
+ raise
+ f = s.makefile('r+')
+ f.write('QUIT / HTTP/1.0\r\n')
+ f.flush()
+ sys.exit(0)
+
+if getwsdl:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((hostname, portnum))
+ f = s.makefile('r+')
+ f.write('GET /wsdl HTTP/1.0\r\n\r\n')
+ f.flush()
+ status = f.readline()
+ print status,
+ while 1:
+ l = f.readline()
+ if l == '': break
+ print l,
+ sys.exit(0)
+
+for T in tests:
+ descr, IN, header = testlist[T]
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((hostname, portnum))
+ f = s.makefile('r+')
+
+ print '-' * 60, '\n\n\n', T, descr
+ f.write('POST / HTTP/1.0\r\n')
+ f.write('SOAPAction: "http://soapinterop.org/"\r\n')
+ if header == None:
+ f.write('Content-type: text/xml; charset="utf-8"\r\n')
+ f.write('Content-Length: %d\r\n\r\n' % len(IN))
+ else:
+ f.write(header)
+ f.write(IN)
+ f.flush()
+
+ status = f.readline()
+ print status,
+ while 1:
+ l = f.readline()
+ if l == '': break
+ if verbose: print l,
+
+ f.close()
diff --git a/ZSI/zsi/interop/cpackets.py b/ZSI/zsi/interop/cpackets.py
new file mode 100755
index 0000000..30dd335
--- /dev/null
+++ b/ZSI/zsi/interop/cpackets.py
@@ -0,0 +1,205 @@
+#! /usr/bin/env python
+
+pocketsoap_struct_test = '''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:ps42='http://soapinterop.org/xsd'
+xmlns:xsd='http://www.w3.org/1999/XMLSchema'
+xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance'>
+<SOAP-ENV:Header>
+<ww:echoMeStringRequest xmlns:ww="http://soapinterop.org/echoheader/">
+Please release &lt;me</ww:echoMeStringRequest>
+</SOAP-ENV:Header>
+<SOAP-ENV:Body>
+ <m:echoStruct xmlns:m='http://soapinterop.org/'>
+ <inputStruct xsi:type='ps42:SOAPStruct'>
+ <varInt xsi:type='xsd:int'>1073741824</varInt>
+
+ <varFloat xsi:type='xsd:float'>-42.24</varFloat>
+
+ <varString xsi:type='xsd:string'>pocketSOAP
+ rocks!&lt;g&gt;</varString>
+ </inputStruct>
+ </m:echoStruct>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+phalanx_b64_test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:m='http://soapinterop.org/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
+ <SOAP-ENV:Body>
+ <m:echoBase64>
+ <inputBase64 xsi:type='xsd:base64Binary'>Ty4rY6==</inputBase64>
+ </m:echoBase64>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+hexbin_test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:m='http://soapinterop.org/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
+ <SOAP-ENV:Body>
+ <m:echoHexBinary>
+ <inputHexBinary xsi:type='xsd:hexBinary'>656174206d792073686f72747321</inputHexBinary>
+ </m:echoHexBinary>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+phalanx_badhref_test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns1='http://soapinterop.org/xsd' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:ns='http://soapinterop.org/' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
+ <SOAP-ENV:Body>
+ <ns:echoStructArray>
+ <inputStructArray xsi:type='SOAP-ENC:Array' SOAP-ENC:arrayType='ns1:SOAPStruct[3]'>
+ <item xsi:type='ns1:SOAPStruct' href='#2'>invalid value</item>
+ <item xsi:type='ns1:SOAPStruct'>
+ <varFloat xsi:type='xsd:float'>21.02</varFloat>
+ <varString xsi:type='xsd:string'>c</varString>
+ <varInt xsi:type='xsd:int'>3</varInt>
+ </item>
+ <item xsi:type='ns1:SOAPStruct' href='#1'/>
+ </inputStructArray>
+ </ns:echoStructArray>
+ <mrA xsi:type='ns1:SOAPStruct' id='1'>
+ <varInt xsi:type='xsd:int'>-33</varInt>
+ <varFloat xsi:type='xsd:float'>33.33</varFloat>
+ <varString xsi:type='xsd:string'>test 1</varString>
+ </mrA>
+ <mrB xsi:type='ns1:SOAPStruct' id='2'>
+ <varFloat xsi:type='xsd:float'>11.11</varFloat>
+ <varString xsi:type='xsd:string'>test 2</varString>
+ <varInt xsi:type='xsd:int'>-11</varInt>
+ </mrB>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+someones_b64_test = '''<S:Envelope
+ S:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
+ xmlns:S='http://schemas.xmlsoap.org/soap/envelope/'
+ xmlns:E='http://schemas.xmlsoap.org/soap/encoding/'
+ xmlns:a='http://soapinterop.org/'
+ xmlns:b='http://www.w3.org/2001/XMLSchema-instance'>
+ <S:Body>
+ <a:echoBase64><inputBase64
+ b:type='E:base64'>AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T1</inputBase64>
+ </a:echoBase64>
+ </S:Body></S:Envelope>'''
+
+phalanx_void_test = '''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:ps42='http://soapinterop.org/xsd'
+xmlns:xsd='http://www.w3.org/1999/XMLSchema'
+xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance'>
+<SOAP-ENV:Header>
+<ww:echoMeStringRequest SOAP-ENV:mustUnderstand="1" xmlns:ww="http://soapinterop.org/echoheader/">
+Please release me</ww:echoMeStringRequest>
+<ww:echoMeStructRequest xmlns:ww="http://soapinterop.org/echoheader/">
+ <varInt>111</varInt>
+ <varFloat>-42.24</varFloat>
+ <varString>Header text string.</varString>
+ </ww:echoMeStructRequest>
+</SOAP-ENV:Header>
+<SOAP-ENV:Body>
+ <m:echoVoid xmlns:m='http://soapinterop.org/'/>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+multipart_test = '''
+Ignore this
+--sep
+Content-Type: text/xml
+
+<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:ps42='http://soapinterop.org/xsd'
+xmlns:xsd='http://www.w3.org/1999/XMLSchema'
+xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance'>
+<SOAP-ENV:Body>
+ <m:echoStruct xmlns:m='http://soapinterop.org/'>
+ <inputStruct xsi:type='ps42:SOAPStruct'>
+ <varInt xsi:type='xsd:int'>1073741824</varInt>
+
+ <varFloat xsi:type='xsd:float'>-42.24</varFloat>
+
+ <varString xsi:type='xsd:string' href="cid:123@456"/>
+ </inputStruct>
+ </m:echoStruct>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+--sep
+Content-ID: otherwise
+
+yehll
+
+--sep
+Content-ID: 123@456
+
+this is a very long string
+it is separated over several lines.
+hwlleasdfasdf
+asdfad
+--sep--
+'''
+phalanx_badstructtype_test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns1='http://soapinterop.org/xsd' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:m='http://soapinterop.org/' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
+ <SOAP-ENV:Body>
+ <m:echoStruct>
+ <inputStruct xsi:type='ns1:roastbeef'>
+ <varString xsi:type='xsd:string'>easy test</varString>
+ <varInt xsi:type='xsd:int'>11</varInt>
+ <varFloat xsi:type='xsd:float'>22.33</varFloat>
+ </inputStruct>
+ </m:echoStruct>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+phalanx_int_href_test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:m='http://soapinterop.org/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
+ <SOAP-ENV:Body>
+ <m:echoInteger>
+ <inputInteger href='#a1'/>
+ </m:echoInteger>
+ <multiref xsi:type='xsd:int' id='a1'>13</multiref>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+'''
+
+wm_simple_as_struct_test = '''<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope
+SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<SOAP-ENV:Body>
+<m:echoSimpleTypesAsStruct xmlns:m="http://soapinterop.org/">
+<inputString>White Mesa Test</inputString>
+<inputInteger>42</inputInteger>
+<inputFloat>0.0999</inputFloat>
+</m:echoSimpleTypesAsStruct>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+'''
+
+apache_float_test = '''<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Body>
+ <ns1:echoFloat xmlns:ns1="http://soapinterop.org/">
+ <inputFloat xsi:type="xsd:float">3.7</inputFloat>
+ </ns1:echoFloat>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+testlist = (
+ ( 'struct test', pocketsoap_struct_test, None),
+ ( 'base64 test', phalanx_b64_test, None),
+ ( 'hexBinary', hexbin_test, None),
+ ( 'big base64 test', someones_b64_test, None),
+ ( 'echovoid', phalanx_void_test, None),
+ ( 'simple2struct', wm_simple_as_struct_test, None),
+ ( 'multipart', multipart_test,
+ 'Content-type: multipart/related; boundary="sep"\r\n' ),
+ ( 'int href test', phalanx_int_href_test, None),
+ ( 'apache float', apache_float_test, None),
+ ( 'bad href test', phalanx_badhref_test, None),
+ ( 'bad type attr on struct', phalanx_badstructtype_test, None),
+)
diff --git a/ZSI/zsi/interop/sclasses.py b/ZSI/zsi/interop/sclasses.py
new file mode 100644
index 0000000..b748af8
--- /dev/null
+++ b/ZSI/zsi/interop/sclasses.py
@@ -0,0 +1,233 @@
+#! /usr/bin/env python
+
+WSDL_DEFINITION = '''<?xml version="1.0"?>
+
+<definitions name="InteropTest"
+ targetNamespace="http://soapinterop.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:tns="http://soapinterop.org/">
+
+ <import
+ location="http://www.whitemesa.com/interop/InteropTest.wsdl"
+ namespace="http://soapinterop.org/xsd"/>
+ <import
+ location="http://www.whitemesa.com/interop/InteropTest.wsdl"
+ namespace="http://soapinterop.org/"/>
+ <import
+ location="http://www.whitemesa.com/interop/InteropTestB.wsdl"
+ namespace="http://soapinterop.org/"/>
+ <import
+ location="http://www.whitemesa.com/interop/echoHeaderBindings.wsdl"
+ namespace="http://soapinterop.org/"/>
+ <import
+ location="http://www.whitemesa.com/interop/InteropTestMap.wsdl"
+ namespace="http://soapinterop.org/"/>
+<!-- DOCSTYLE; soon.
+ <import
+ location="http://www.whitemesa.com/interop/interopdoc.wsdl"
+ namespace="http://soapinterop.org/"/>
+-->
+
+ <service name="interop">
+ <port name="TestSoap" binding="tns:InteropTestSoapBinding">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+ <port name="TestSoapB" binding="tns:InteropTestSoapBindingB">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+ <port name="EchoHeaderString" binding="tns:InteropEchoHeaderStringBinding">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+ <port name="EchoHeaderStruct" binding="tns:InteropEchoHeaderStructBinding">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+ <port name="TestSoapMap" binding="tns:InteropTestSoapBindingMap">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+<!-- DOCSTYLE; soon.
+ <port name="TestDoc" binding="tns:doc_test_binding">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+-->
+ </service>
+
+</definitions>
+'''
+
+from ZSI import *
+from ZSI import _copyright, _seqtypes
+import types
+
+class SOAPStruct:
+ def __init__(self, name):
+ pass
+ def __str__(self):
+ return str(self.__dict__)
+
+class TC_SOAPStruct(TC.Struct):
+ def __init__(self, pname=None, **kw):
+ TC.Struct.__init__(self, SOAPStruct, [
+ TC.String('varString', strip=0, inline=1),
+ TC.Iint('varInt'),
+ TC.FPfloat('varFloat', format='%.18g'),
+ ], pname, **kw)
+
+class TC_SOAPStructStruct(TC.Struct):
+ def __init__(self, pname=None, **kw):
+ TC.Struct.__init__(self, SOAPStruct, [
+ TC.String('varString', strip=0),
+ TC.Iint('varInt'),
+ TC.FPfloat('varFloat', format='%.18g'),
+ TC_SOAPStruct('varStruct'),
+ ], pname, **kw)
+
+class TC_SOAPArrayStruct(TC.Struct):
+ def __init__(self, pname=None, **kw):
+ TC.Struct.__init__(self, SOAPStruct, [
+ TC.String('varString', strip=0),
+ TC.Iint('varInt'),
+ TC.FPfloat('varFloat', format='%.18g'),
+ TC.Array('xsd:string', TC.String(string=0), 'varArray'),
+ ], pname, **kw)
+
+class TC_ArrayOfstring(TC.Array):
+ def __init__(self, pname=None, **kw):
+ TC.Array.__init__(self, 'xsd:string', TC.String(string=0), pname, **kw)
+
+class TC_ArrayOfint(TC.Array):
+ def __init__(self, pname=None, **kw):
+ TC.Array.__init__(self, 'xsd:int', TC.Iint(), pname, **kw)
+
+class TC_ArrayOffloat(TC.Array):
+ def __init__(self, pname=None, **kw):
+ TC.Array.__init__(self, 'xsd:float', TC.FPfloat(format='%.18g'),
+ pname, **kw)
+
+class TC_ArrayOfSOAPStruct(TC.Array):
+ def __init__(self, pname=None, **kw):
+ TC.Array.__init__(self, 'Za:SOAPStruct', TC_SOAPStruct(), pname, **kw)
+
+#class TC_ArrayOfstring2D(TC.Array):
+# def __init__(self, pname=None, **kw):
+# TC.Array.__init__(self, 'xsd:string', TC.String(string=0), pname, **kw)
+
+class RPCParameters:
+ def __init__(self, name):
+ pass
+ def __str__(self):
+ t = str(self.__dict__)
+ if hasattr(self, 'inputStruct'):
+ t += '\ninputStruct\n'
+ t += str(self.inputStruct)
+ if hasattr(self, 'inputStructArray'):
+ t += '\ninputStructArray\n'
+ t += str(self.inputStructArray)
+ return t
+ def frominput(self, arg):
+ self.v = s = SOAPStruct(None)
+ self.v.varString = arg.inputString
+ self.v.varInt = arg.inputInteger
+ self.v.varFloat = arg.inputFloat
+ return self
+
+class Operation:
+ dispatch = {}
+ SOAPAction = '''"http://soapinterop.org/"'''
+ ns = "http://soapinterop.org/"
+ hdr_ns = "http://soapinterop.org/echoheader/"
+
+ def __init__(self, name, tcin, tcout, **kw):
+ self.name = name
+ if type(tcin) not in _seqtypes: tcin = tcin,
+ self.TCin = TC.Struct(RPCParameters, tuple(tcin), name)
+ if type(tcout) not in _seqtypes: tcout = tcout,
+ self.TCout = TC.Struct(RPCParameters, tuple(tcout), name + 'Response')
+ self.convert = kw.get('convert', None)
+ self.headers = kw.get('headers', [])
+ self.nsdict = kw.get('nsdict', {})
+ Operation.dispatch[name] = self
+
+Operation("echoString",
+ TC.String('inputString', strip=0),
+ TC.String('inputString', oname='return', strip=0)
+)
+Operation("echoStringArray",
+ TC_ArrayOfstring('inputStringArray'),
+ TC_ArrayOfstring('inputStringArray', oname='return')
+)
+Operation("echoInteger",
+ TC.Iint('inputInteger'),
+ TC.Iint('inputInteger', oname='return'),
+)
+Operation("echoIntegerArray",
+ TC_ArrayOfint('inputIntegerArray'),
+ TC_ArrayOfint('inputIntegerArray', oname='return'),
+)
+Operation("echoFloat",
+ TC.FPfloat('inputFloat', format='%.18g'),
+ TC.FPfloat('inputFloat', format='%.18g', oname='return'),
+)
+Operation("echoFloatArray",
+ TC_ArrayOffloat('inputFloatArray'),
+ TC_ArrayOffloat('inputFloatArray', oname='return'),
+)
+Operation("echoStruct",
+ TC_SOAPStruct('inputStruct'),
+ TC_SOAPStruct('inputStruct', oname='return'),
+)
+Operation("echoStructArray",
+ TC_ArrayOfSOAPStruct('inputStructArray'),
+ TC_ArrayOfSOAPStruct('inputStructArray', oname='return'),
+ nsdict={'Za': 'http://soapinterop.org/xsd'}
+)
+Operation("echoVoid",
+ [],
+ [],
+ headers=( ( Operation.hdr_ns, 'echoMeStringRequest' ),
+ ( Operation.hdr_ns, 'echoMeStructRequest' ) )
+)
+Operation("echoBase64",
+ TC.Base64String('inputBase64'),
+ TC.Base64String('inputBase64', oname='return'),
+)
+Operation("echoDate",
+ TC.gDateTime('inputDate'),
+ TC.gDateTime('inputDate', oname='return'),
+)
+Operation("echoHexBinary",
+ TC.HexBinaryString('inputHexBinary'),
+ TC.HexBinaryString('inputHexBinary', oname='return'),
+)
+Operation("echoDecimal",
+ TC.Decimal('inputDecimal'),
+ TC.Decimal('inputDecimal', oname='return'),
+)
+Operation("echoBoolean",
+ TC.Boolean('inputBoolean'),
+ TC.Boolean('inputBoolean', oname='return'),
+)
+Operation("echoStructAsSimpleTypes",
+ TC_SOAPStruct('inputStruct'),
+ ( TC.String('outputString', strip=0), TC.Iint('outputInteger'),
+ TC.FPfloat('outputFloat', format='%.18g') ),
+ convert=lambda s: (s.v.varString, s.v.varInt, s.v.varFloat),
+)
+Operation("echoSimpleTypesAsStruct",
+ ( TC.String('inputString', strip=0), TC.Iint('inputInteger'),
+ TC.FPfloat('inputFloat') ),
+ TC_SOAPStruct('v', opname='return'),
+ convert=lambda arg: RPCParameters(None).frominput(arg),
+)
+#Operation("echo2DStringArray",
+# TC_ArrayOfstring2D('input2DStringArray'),
+# TC_ArrayOfstring2D('return')
+#),
+Operation("echoNestedStruct",
+ TC_SOAPStructStruct('inputStruct'),
+ TC_SOAPStructStruct('inputStruct', oname='return'),
+)
+Operation("echoNestedArray",
+ TC_SOAPArrayStruct('inputStruct'),
+ TC_SOAPArrayStruct('inputStruct', oname='return'),
+)
diff --git a/ZSI/zsi/interop/server.py b/ZSI/zsi/interop/server.py
new file mode 100755
index 0000000..56317ed
--- /dev/null
+++ b/ZSI/zsi/interop/server.py
@@ -0,0 +1,209 @@
+#! /usr/bin/env python
+from ZSI import *
+from ZSI import _copyright, resolvers, _child_elements, _textprotect
+import sys, time, cStringIO as StringIO
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+
+from sclasses import Operation, WSDL_DEFINITION, TC_SOAPStruct
+
+class InteropRequestHandler(BaseHTTPRequestHandler):
+ server_version = 'ZSI/1.2 OS390/VM5.4 ' + BaseHTTPRequestHandler.server_version
+
+ def send_xml(self, text, code=200):
+ '''Send some XML.'''
+ self.send_response(code)
+ self.send_header('Content-type', 'text/xml; charset="utf-8"')
+ self.send_header('Content-Length', str(len(text)))
+ self.end_headers()
+ self.wfile.write(text)
+ self.trace(text, 'SENT')
+ self.wfile.flush()
+
+ def send_fault(self, f):
+ '''Send a fault.'''
+ self.send_xml(f.AsSOAP(), 500)
+
+ def trace(self, text, what):
+ '''Log a debug/trace message.'''
+ F = self.server.tracefile
+ if not F: return
+ print >>F, '=' * 60, '\n%s %s %s %s:' % \
+ (what, self.client_address, self.path, time.ctime(time.time()))
+ print >>F, text
+ print >>F, '=' * 60, '\n'
+ F.flush()
+
+ def do_QUIT(self):
+ '''Quit.'''
+ self.server.quitting = 1
+ self.log_message('Got QUIT command')
+ sys.stderr.flush()
+ raise SystemExit
+
+ def do_GET(self):
+ '''The GET command. Always returns the WSDL.'''
+ self.send_xml(WSDL_DEFINITION.replace('>>>URL<<<', self.server.url))
+
+ def do_POST(self):
+ '''The POST command.'''
+
+ try:
+ # SOAPAction header.
+ action = self.headers.get('soapaction', None)
+ if not action:
+ self.send_fault(Fault(Fault.Client,
+ 'SOAPAction HTTP header missing.'))
+ return
+ if action != Operation.SOAPAction:
+ self.send_fault(Fault(Fault.Client,
+ 'SOAPAction is "%s" not "%s"' % \
+ (action, Operation.SOAPAction)))
+ return
+
+ # Parse the message.
+ ct = self.headers['content-type']
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, self.rfile)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ cl = int(self.headers['content-length'])
+ IN = self.rfile.read(cl)
+ self.trace(IN, 'RECEIVED')
+ ps = ParsedSoap(IN)
+ except ParseException, e:
+ self.send_fault(FaultFromZSIException(e))
+ return
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ self.send_fault(FaultFromException(e, 1, sys.exc_info()[2]))
+ return
+
+ try:
+ # Actors?
+ a = ps.WhatActorsArePresent()
+ if len(a):
+ self.send_fault(FaultFromActor(a[0]))
+ return
+
+ # Is the operation defined?
+ root = ps.body_root
+ if root.namespaceURI != Operation.ns:
+ self.send_fault(Fault(Fault.Client,
+ 'Incorrect namespace "%s"' % root.namespaceURI))
+ return
+ n = root.localName
+ op = Operation.dispatch.get(n, None)
+ if not op:
+ self.send_fault(Fault(Fault.Client,
+ 'Undefined operation "%s"' % n))
+ return
+
+ # Scan headers. First, see if we understand all headers with
+ # mustUnderstand set. Then, get the ones intended for us (ignoring
+ # others since step 1 insured they're not mustUnderstand).
+ for mu in ps.WhatMustIUnderstand():
+ if mu not in op.headers:
+ uri, localname = mu
+ self.send_fault(FaultFromNotUnderstood(uri, localname))
+ return
+ headers = [ e for e in ps.GetMyHeaderElements()
+ if (e.namespaceURI, e.localName) in op.headers ]
+ nsdict={ 'Z': Operation.ns }
+ if headers:
+ nsdict['E'] = Operation.hdr_ns
+ self.process_headers(headers, ps)
+ else:
+ self.headertext = None
+
+ try:
+ results = op.TCin.parse(ps.body_root, ps)
+ except ParseException, e:
+ self.send_fault(FaultFromZSIException(e))
+ self.trace(str(results), 'PARSED')
+ if op.convert:
+ results = op.convert(results)
+ if op.nsdict: nsdict.update(op.nsdict)
+ reply = StringIO.StringIO()
+ sw = SoapWriter(reply, nsdict=nsdict, header=self.headertext)
+ sw.serialize(results, op.TCout,
+ name = 'Z:' + n + 'Response', inline=1)
+ sw.close()
+ self.send_xml(reply.getvalue())
+ except Exception, e:
+ # Fault while processing; now it's in the body.
+ self.send_fault(FaultFromException(e, 0, sys.exc_info()[2]))
+ return
+
+ def process_headers(self, headers, ps):
+ '''Process headers, set self.headertext to be what to output.
+ '''
+ self.headertext = ''
+ for h in headers:
+ if h.localName == 'echoMeStringRequest':
+ s = TC.String().parse(h, ps)
+ self.headertext += \
+ '<E:echoMeStringResponse>%s</E:echoMeStringResponse>\n' % _textprotect(s)
+ elif h.localName == 'echoMeStructRequest':
+ tc = TC_SOAPStruct('echoMeStructRequest', inline=1)
+ data = tc.parse(h, ps)
+ s = StringIO.StringIO()
+ sw = SoapWriter(s, envelope=0)
+ tc.serialize(sw, data, name='E:echoMeStructResponse')
+ sw.close()
+ self.headertext += s.getvalue()
+ else:
+ raise TypeError('Unhandled header ' + h.nodeName)
+ pass
+
+
+class InteropHTTPServer(HTTPServer):
+ def __init__(self, me, url, **kw):
+ HTTPServer.__init__(self, me, InteropRequestHandler)
+ self.quitting = 0
+ self.tracefile = kw.get('tracefile', None)
+ self.url = url
+ def handle_error(self, req, client_address):
+ if self.quitting: sys.exit(0)
+ HTTPServer.handle_error(self, req, client_address)
+
+
+import getopt
+try:
+ (opts, args) = getopt.getopt(sys.argv[1:], 'l:p:t:u:',
+ ('log=', 'port=', 'tracefile=', 'url=') )
+except getopt.GetoptError, e:
+ print >>sys.stderr, sys.argv[0] + ': ' + str(e)
+ sys.exit(1)
+if args:
+ print >>sys.stderr, sys.argv[0] + ': Usage error.'
+ sys.exit(1)
+
+portnum = 1122
+tracefile = None
+url = None
+for opt, val in opts:
+ if opt in [ '-l', '--logfile' ]:
+ sys.stderr = open(val, 'a')
+ elif opt in [ '-p', '--port' ]:
+ portnum = int(val)
+ elif opt in [ '-t', '--tracefile' ]:
+ if val == '-':
+ tracefile = sys.stdout
+ else:
+ tracefile = open(val, 'a')
+ elif opt in [ '-u', '--url' ]:
+ url = val
+ME = ( '', portnum )
+
+if not url:
+ import socket
+ url = 'http://' + socket.getfqdn()
+ if portnum != 80: url += ':%d' % portnum
+ url += '/interop.wsdl'
+
+try:
+ InteropHTTPServer(ME, url, tracefile=tracefile).serve_forever()
+except SystemExit:
+ pass
+sys.exit(0)
diff --git a/ZSI/zsi/newver.py b/ZSI/zsi/newver.py
new file mode 100755
index 0000000..8a38030
--- /dev/null
+++ b/ZSI/zsi/newver.py
@@ -0,0 +1,45 @@
+#! /usr/bin/env python
+
+# This script does not use 2.x features so that you can build an RPM
+# on a system that doesn't have 2.x installed (e.g., basic RedHat).
+
+import ConfigParser
+import os
+import sys
+import time
+
+cf = ConfigParser.ConfigParser()
+cf.read('setup.cfg')
+
+major = cf.getint('version', 'major')
+minor = cf.getint('version', 'minor')
+patchlevel = cf.getint('version', 'patchlevel')
+
+if '--incr' in sys.argv[1:]:
+ patchlevel = patchlevel + 1
+ cf.set('version', 'patchlevel', patchlevel)
+ f = open('setup.cfg', 'w')
+ cf.write(f)
+ f.close()
+
+def write_changes(filename, text):
+ """Write text to the file only if the text changed."""
+ oldtext = None
+ if os.path.exists(filename):
+ f = open(filename)
+ oldtext = f.read()
+ f.close()
+ if oldtext != text:
+ f = open(filename, 'w')
+ f.write(text)
+ f.close()
+
+write_changes('ZSI/version.py',
+ '# Auto-generated file; do not edit\n'
+ 'Version = (%d, %d, %d)\n' % (major, minor, patchlevel))
+
+write_changes('doc/version.tex',
+ '% Auto-generated file; do not edit\n'
+ + time.strftime('\\date{%B %d, %Y}\n', time.localtime())
+ + ('\\release{%d.%d.%d}\n' % (major, minor, patchlevel))
+ + ('\\setshortversion{%d.%d}\n' % (major, minor)))
diff --git a/ZSI/zsi/samples/Echo/EchoClient.py b/ZSI/zsi/samples/Echo/EchoClient.py
new file mode 100644
index 0000000..3d457a4
--- /dev/null
+++ b/ZSI/zsi/samples/Echo/EchoClient.py
@@ -0,0 +1,17 @@
+#
+# Exampe echo client, to show extended code generation in ZSI
+#
+# Import the client proxy object
+from EchoServer_client import EchoServerSOAP
+
+# Instantiate a client proxy object, then call it
+hw = EchoServerSOAP("http://localhost:9999/EchoServer")
+print hw.Echo("Test TIE String")
+
+# Instantiate a client proxy object, then call it
+hw2 = EchoServerSOAP("http://localhost:9999/EchoServIn")
+try:
+ print hw2.Echo("Test INHERIT String")
+except Exception, e:
+ print "Failed to echo (Inherited): ", e
+
diff --git a/ZSI/zsi/samples/Echo/EchoServer.py b/ZSI/zsi/samples/Echo/EchoServer.py
new file mode 100644
index 0000000..d6eb3d0
--- /dev/null
+++ b/ZSI/zsi/samples/Echo/EchoServer.py
@@ -0,0 +1,67 @@
+#
+# How to build an echo server using the extended code generation
+#
+
+# Import the ZSI stuff you'd need no matter what
+from ZSI.ServiceContainer import ServiceContainer
+
+# This is a new method imported to show it's value
+from ZSI.ServiceContainer import GetSOAPContext
+
+# Import the generated Server Object
+import EchoServer_interface
+
+# Create a Server implementation
+
+# Here we use a Tie approach
+class EchoServer:
+ def authorize(self, auth_info, post, action):
+ print "NOT Authorizing TIE Echo"
+ ctx = GetSOAPContext()
+ print dir(ctx)
+ print "Container: ", ctx.connection
+ print "Parsed SOAP: ", ctx.parsedsoap
+ print "Container: ", ctx.container
+ print "HTTP Headers:\n", ctx.httpheaders
+ print "----"
+ print "XML Data:\n", ctx.xmldata
+ return 0
+
+ def Echo(self, input):
+ return input * 3
+
+# This using a derived server instead
+class EchoServIn(EchoServer_interface.EchoServer):
+ def __init__(self, post='', **kw):
+ EchoServer_interface.EchoServer.__init__(self, post, kw=kw)
+
+ def authorize(self, auth_info, post, action):
+ print "Authorizing INHERIT Echo"
+ ctx = GetSOAPContext()
+ print dir(ctx)
+ print "Container: ", ctx.connection
+ print "Parsed SOAP: ", ctx.parsedsoap
+ print "Container: ", ctx.container
+ print "HTTP Headers:\n", ctx.httpheaders
+ print "----"
+ print "XML Data:\n", ctx.xmldata
+ return 1
+
+ def Echo(self, input):
+ return input * 3
+
+# Here we set up the server
+serviceContainer = ServiceContainer(('localhost', 9999))
+
+# Create the TIE version of the server
+hws = EchoServer()
+hwsi = EchoServer_interface.EchoServer(impl=hws,
+ auth_method_name="authorize")
+serviceContainer.setNode(hwsi, url="/EchoServer")
+
+# Create the Inherited version of the server
+hws2 = EchoServIn()
+serviceContainer.setNode(hws2, url="/EchoServIn")
+
+# Run the service container
+serviceContainer.serve_forever()
diff --git a/ZSI/zsi/samples/poly.py b/ZSI/zsi/samples/poly.py
new file mode 100755
index 0000000..009cc55
--- /dev/null
+++ b/ZSI/zsi/samples/poly.py
@@ -0,0 +1,121 @@
+#! /usr/bin/env python
+
+from ZSI import *
+
+"""Polymorphic containers using TC.Choice
+
+Based on code and text from Dan Gunter <dkgunter@lbl.gov>.
+
+The TC.Choice typecode can be used to create a polymorphic type for
+a specified set of object types. Here's how:
+ 1. Define all your data classes (D1, D2, ...). If they derive from
+ a common base class (Base), some feel you get "cleaner" code.
+ 2. Create a typecode class (TC_D1, TC_D2, ...) for each data class.
+ 3. Create a "base" typecode that uses TC.Choice to do the actual
+ parsing and serializing. Two versions are shown, below.
+Then you can instantiate, e.g., an Array that can handle multiple
+datatypes with:
+ TC.Array("base-class-name", TC_Base(), "MyArray")
+"""
+
+class Base: pass
+class D1(Base): pass
+class D2(Base): pass
+
+class TC_D1(TC.TypeCode): pass
+class TC_D2(TC.TypeCode): pass
+D1.typecode = TC_D1()
+D2.typecode = TC_D2()
+
+# A simple version of TC_Base that is "hardwired" with the types of
+# objects it can handle. We defer setting the choice attribute because
+# with nested containers you could get too much recursion.
+class TC_Base(TC.TypeCode):
+ def parse(self, elt, ps):
+ return self.choice.parse(elt, ps)[1]
+
+ def serialize(self, sw, pyobj, **kw):
+ if not isinstance(pyobj, Base):
+ raise TypeError(str(pyobj.__class__) + " not in type hierarchy")
+ if isinstance(pyobj, D1):
+ self.choice.serialize(sw, ('D1', pyobj), **kw)
+ elif isinstance(pyobj, D2):
+ self.choice.serialize(sw, ('D2', pyobj), **kw)
+ else:
+ raise TypeError(str(pyobj.__class__) + " unknown type")
+ return
+
+ def __getattr__(self, attr):
+ if attr == 'choice':
+ choice = TC.Choice((D1.typecode, D2.typecode), 'Item')
+ self.__dict__['choice'] = choice
+ return choice
+ raise AttributeError(attr)
+
+## Another version that takes a dictionary that maps element names to
+## the python class.
+
+class TC_Polymorphic(TC.TypeCode):
+ def __init__(self, name2class, pname=None, **kw):
+ TC.TypeCode.__init__(self, pname, **kw)
+ self.name2class = name2class
+
+ def parse(self, elt, ps):
+ return self.choice.parse(elt, ps)[1]
+
+ def serialize(self, sw, pyobj, **kw):
+ self.choice.serialize(sw,
+ (self.class2name[pyobj.__class__], pyobj), **kw)
+
+ def __getattr__(self, attr):
+ if attr == 'choice':
+ choice = TC.Choice(
+ [getattr(v, 'typecode') for k,v in self.name2class.items()],
+ 'Item')
+ self.__dict__['choice'] = choice
+ return choice
+ if attr == 'class2name':
+ class2name = {}
+ for k,v in self.name2class.items(): class2name[v] = k
+ self.__dict__['class2name'] = class2name
+ return class2name
+ raise AttributeError(attr)
+
+class P1: pass
+class P2: pass
+P1.typecode = TC.String('s')
+P2.typecode = TC.Integer('i')
+myTC = TC.Array("Base", TC_Polymorphic({'i': P2, 's': P1}))
+
+test = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body xmlns='test-uri'>
+ <array SOAP-ENC:arrayType="Base">
+ <i>34</i>
+ <s>hello</s>
+ <s>34</s>
+ <i>12</i>
+ <s>goodbye</s>
+ </array>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+ps = ParsedSoap(test)
+a = myTC.parse(ps.body_root, ps)
+print a
+
+if 0:
+ # XXX. Does not work. :(
+ b = [ P1(), P1(), P2(), P1() ]
+ b[0].s = 'string'
+ b[1].s = '34'
+ b[2].i = 34
+ b[3].s = 'adios'
+
+ import sys
+ sw = SoapWriter(sys.stdout)
+ myTC.serialize(sw, b)
diff --git a/ZSI/zsi/setup.py b/ZSI/zsi/setup.py
new file mode 100755
index 0000000..37f95a6
--- /dev/null
+++ b/ZSI/zsi/setup.py
@@ -0,0 +1,68 @@
+#! /usr/bin/env python
+# $Header$
+import sys
+try:
+ from setuptools import setup
+ hasSetuptools = True
+except ImportError:
+ from distutils.core import setup
+ hasSetuptools = False
+
+_url = "http://pywebsvcs.sf.net/"
+
+import ConfigParser
+cf = ConfigParser.ConfigParser()
+cf.read('setup.cfg')
+major = cf.getint('version', 'major')
+minor = cf.getint('version', 'minor')
+patchlevel = cf.getint('version', 'patchlevel')
+candidate = cf.getint('version', 'candidate')
+
+_version = "%d.%d" % ( major, minor )
+if patchlevel:
+ _version += '.%d' % patchlevel
+if candidate:
+ _version += '_rc%d' % candidate
+
+try:
+ open('ZSI/version.py', 'r').close()
+except:
+ print 'ZSI/version.py not found; run "make"'
+ sys.exit(1)
+
+_packages = [ "ZSI", "ZSI.generate", "ZSI.wstools"]
+if sys.version_info[0:2] >= (2, 4):
+ _packages.append("ZSI.twisted")
+
+
+# setuptools specific logic
+additional_params = {}
+if hasSetuptools:
+ additional_params['entry_points'] = {
+ 'console_scripts': [
+ 'wsdl2py = ZSI.generate.commands:wsdl2py',
+ 'wsdl2dispatch = ZSI.generate.commands:wsdl2dispatch',
+ ],
+ }
+ additional_params['install_requires'] = [ "PyXML >= 0.8.3", ]
+ additional_params['setup_requires'] = [ "setuptools >= 0.6c3", ]
+ additional_params['dependency_links'] = [
+ "http://sourceforge.net/project/showfiles.php?group_id=6473&package_id=6541&release_id=286213",
+ ]
+else:
+ additional_params['scripts'] = ["scripts/wsdl2py", "scripts/wsdl2dispatch"]
+
+setup(
+ name="ZSI",
+ version=_version,
+ license="Python",
+ packages=_packages,
+ description="Zolera SOAP Infrastructure",
+ author="Rich Salz, et al",
+ author_email="rsalz@datapower.com",
+ maintainer="Rich Salz, et al",
+ maintainer_email="pywebsvcs-talk@lists.sf.net",
+ url=_url,
+ long_description="For additional information, please see " + _url,
+ **additional_params
+)
diff --git a/ZSI/zsi/test/cgicli.py b/ZSI/zsi/test/cgicli.py
new file mode 100755
index 0000000..95f80b0
--- /dev/null
+++ b/ZSI/zsi/test/cgicli.py
@@ -0,0 +1,75 @@
+#! /usr/bin/env python
+import getopt, socket, sys
+
+try:
+ (opts, args) = getopt.getopt(sys.argv[1:],
+ 'h:p:s',
+ ( 'host=', 'port=',
+ 'statusonly', 'help'))
+except getopt.GetoptError, e:
+ print >>sys.stderr, sys.argv[0] + ': ' + str(e)
+ sys.exit(1)
+if args:
+ print sys.argv[0] + ': Usage error; try --help.'
+ sys.exit(1)
+
+hostname, portnum, verbose = 'localhost', 80, 1
+for opt, val in opts:
+ if opt in [ '--help' ]:
+ print '''Options include:
+ --host HOST (-h HOST) Name of server host
+ --port PORT (-p PORT) Port server is listening on
+ --statusonly (-s) Do not output reply packets; just status code
+Default is -h%s -p%d -t%s''' % \
+ (hostname, portnum, ','.join([str(x) for x in tests]))
+ sys.exit(0)
+ if opt in [ '-h', '--host' ]:
+ hostname = val
+ elif opt in [ '-p', '--port' ]:
+ portnum = int(val)
+ elif opt in [ '-s', '--statusonly' ]:
+ verbose = 0
+
+
+IN = '''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
+<SOAP-ENV:Body>
+ <hello/>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+'''
+
+IN = '''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
+<SOAP-ENV:Body>
+ <echo>
+ <SOAP-ENC:int>1</SOAP-ENC:int>
+ <SOAP-ENC:int>2</SOAP-ENC:int>
+ </echo>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+'''
+
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.connect((hostname, portnum))
+f = s.makefile('r+')
+
+f.write('POST /cgi-bin/x HTTP/1.0\r\n')
+f.write('Content-type: text/xml; charset="utf-8"\r\n')
+f.write('Content-Length: %d\r\n\r\n' % len(IN))
+f.write(IN)
+f.flush()
+
+status = f.readline()
+print status,
+while 1:
+ l = f.readline()
+ if l == '': break
+ if verbose: print l,
+
+f.close()
diff --git a/ZSI/zsi/test/test_TCtimes.py b/ZSI/zsi/test/test_TCtimes.py
new file mode 100644
index 0000000..9e38a55
--- /dev/null
+++ b/ZSI/zsi/test/test_TCtimes.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+import unittest, sys, tests_good, tests_bad, time
+from ZSI import *
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
+
+class TestCase(unittest.TestCase):
+ '''Examples from "Definitive XML Schema, Priscilla Walmsley, p237-246
+ '''
+ def check_dateTime_local_offset(self):
+ # UTC with local timezone offset
+ #
+ typecode = TC.gDateTime()
+ off_hour = time.altzone/60/60
+ off_min = time.altzone%60
+ stamp_offset = '1968-04-02T13:20:00+%02d:%02d' %(off_hour,off_min)
+ data = typecode.text_to_data(stamp_offset, None, None)
+ stamp = typecode.get_formatted_content(data)
+
+ correct = "1968-04-01T22:20:00Z"
+ self.failUnless(stamp == correct,
+ 'dateTime with local offset(%s), expecting "%s" got "%s"' %(
+ stamp_offset, correct, stamp))
+
+ def check_valid_dateTime(self):
+ typecode = TC.gDateTime()
+ for i in ('1968-04-02T13:20:00', '1968-04-02T13:20:15.5',
+ '1968-04-02T13:20:00-05:00', '1968-04-02T13:20:00Z'):
+ data = typecode.text_to_data(i, None, None)
+ text = typecode.get_formatted_content(data)
+
+ def check_parse_microseconds(self):
+ good = (1968, 4, 2, 13, 20, 15, 511, 0, 0)
+ typecode = TC.gDateTime()
+ data = typecode.text_to_data('1968-04-02T13:20:15.511', None, None)
+ self.failUnless(data == good,
+ 'did not parse something %s, not equal %s' %(data,good))
+
+ def check_serialize_microseconds(self):
+ dateTime = '1968-04-02T13:20:15.511Z'
+ typecode = TC.gDateTime()
+ text = typecode.get_formatted_content((1968, 4, 2, 13, 20, 15, 511, 0, 0))
+ self.failUnless(text == dateTime,
+ 'did not serialze correctly %s, not equal %s' %(text, dateTime))
+
+ def check_serialize_microseconds_1000(self):
+ bad = (1968, 4, 2, 13, 20, 15, 1000, 0)
+ typecode = TC.gDateTime()
+ self.failUnlessRaises(ValueError, typecode.get_formatted_content, bad)
+
+ def check_serialize_microseconds_lessZero(self):
+ '''ignore negative microseconds
+ '''
+ bad = (1968, 4, 2, 13, 20, 15, -1, 0)
+ typecode = TC.gDateTime()
+ text = typecode.get_formatted_content(bad)
+ typecode.get_formatted_content(bad)
+
+ def check_parse_microseconds2(self):
+ good = (1968, 4, 2, 13, 20, 15, 500, 0, 0)
+ typecode = TC.gDateTime()
+ data = typecode.text_to_data('1968-04-02T13:20:15.5Z', None,None)
+ self.failUnless(data == good,
+ 'did not serialze correctly %s, not equal %s' %(data, good))
+
+ #text = typecode.get_formatted_content((1968, 4, 2, 13, 20, 15, 5, 0, 500))
+ #self.failUnless(text == dateTime,
+ # 'did not serialze correctly %s, not equal %s' %(text, dateTime))
+
+ def check_invalid_dateTime(self):
+ typecode = TC.gDateTime()
+
+ def check_valid_time(self):
+ typecode = TC.gTime()
+ for i in ('13:20:00', '13:20:30.5555', '13:20:00Z'):
+ data = typecode.text_to_data(i, None, None)
+ text = typecode.get_formatted_content(data)
+
+ def broke_valid_time(self):
+ typecode = TC.gTime()
+ data = typecode.text_to_data('13:20:00-05:00', None, None)
+
+ def check_invalid_time(self):
+ typecode = TC.gTime()
+ for i in ('5:20:00', '13:20.5:00',):
+ self.failUnlessRaises(Exception, typecode.text_to_data, i, None, None),
+
+ def broke_invalid_time_no_seconds(self):
+ typecode = TC.gTime()
+ i = '13:20:'
+ self.failUnlessRaises(Exception, typecode.text_to_data, i, None, None)
+
+ def broke_invalid_time_bad_timeofday(self):
+ typecode = TC.gTime()
+ i = '13:65:00'
+ self.failUnlessRaises(Exception, typecode.text_to_data, i, None, None)
+
+ def check_valid_date(self):
+ typecode = TC.gDate()
+ for i in ('1968-04-02', '-0045-01-01', '11968-04-02', '1968-04-02+05:00', '1968-04-02Z'):
+ data = typecode.text_to_data(i, None, None)
+ text = typecode.get_formatted_content(data)
+
+ def check_invalid_date(self):
+ typecode = TC.gDate()
+ for i in ('68-04-02', '1968-4-2', '1968/04/02', '04-02-1968',):
+ self.failUnlessRaises(Exception, typecode.text_to_data, i, None, None),
+
+ def broke_invalid_date_april31(self):
+ # No checks for valid date April 30 days
+ typecode = TC.gDate()
+ self.failUnlessRaises(Exception, typecode.text_to_data, '1968-04-31', None, None),
+
+#
+# Creates permutation of test options: "check", "check_any", etc
+#
+_SEP = '_'
+for t in [i[0].split(_SEP) for i in filter(lambda i: callable(i[1]), TestCase.__dict__.items())]:
+ test = ''
+ for f in t:
+ test += f
+ if globals().has_key(test): test += _SEP; continue
+ def _closure():
+ name = test
+ def _makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestCase, name))
+ return suite
+ return _makeTestSuite
+
+ globals()[test] = _closure()
+ test += _SEP
+
+
+makeTestSuite = check
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/test/test_t1.py b/ZSI/zsi/test/test_t1.py
new file mode 100644
index 0000000..2cc3936
--- /dev/null
+++ b/ZSI/zsi/test/test_t1.py
@@ -0,0 +1,229 @@
+#!/usr/bin/env python
+import unittest, sys, tests_good, tests_bad, time
+from ZSI import *
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
+class t1TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t1 test case"
+
+ def setUp(self):
+ self.goodTests = []
+ self.badTests = []
+ for key,val in tests_good.__dict__.items():
+ try:
+ if key[0:4] == "test" and int(key[4:]) > 0:
+ self.goodTests.append((key,val))
+ except:
+ pass
+ for key,val in tests_bad.__dict__.items():
+ try:
+ if key[0:4] == "test" and int(key[4:]) > 0:
+ self.badTests.append((key,val))
+ except:
+ pass
+ self.goodTests.sort(lambda a,b: cmp(a[0], b[0]))
+ self.badTests.sort(lambda a,b: cmp(a[0], b[0]))
+
+ def checkt1(self):
+ for key,val in self.badTests:
+ print "\n", "." * 60, key
+ self.failUnlessRaises(ParseException, ParsedSoap, val)
+ for key,val in self.goodTests:
+ print "\n", "." * 60, key
+ ps = ParsedSoap(val)
+
+ ps = ParsedSoap(datatest)
+ elts = ps.data_elements
+
+ self.failUnlessEqual(TC.Integer(None, nillable=True).parse(elts[10], ps),
+ None)
+ self.failUnlessEqual(TC.Ibyte(None, nillable=True).parse(elts[10], ps),
+ None)
+ B = [ TC.Integer('Price'), TC.Integer('p2'), TC.String(unique=1) ]
+ self.failUnlessEqual(TC.Integer(('test-uri', 'Price')).parse(elts[0], ps),
+ 34)
+ self.failUnlessEqual(B[0].parse(elts[0], ps), 34)
+ self.failUnlessEqual(B[1].parse(elts[1], ps), 44)
+ self.failUnlessEqual(B[2].parse(elts[2], ps), u"This is the name")
+ self.failUnlessEqual(TC.HexBinaryString().parse(elts[9], ps), "? A")
+ self.failUnlessEqual(TC.String('Name').parse(elts[2], ps),
+ u"This is the name")
+ self.failUnlessEqual(TC.Any('Price').parse(elts[0], ps), 34)
+ self.failUnlessEqual(TC.Any('n3').parse(elts[4], ps),
+ u"The value of n3")
+ TC.XML('n2').parse(elts[3], ps)
+ nodelist = TC.XML('a2').parse(elts[7], ps)
+ self.failUnlessEqual(TC.String('n3').parse(elts[4], ps),
+ u"The value of n3")
+ self.failUnlessEqual(TC.Base64String('n64').parse(elts[5], ps),
+ u"hello")
+ self.failUnlessEqual(TC.String('n64').parse(elts[5], ps),
+ u"a GVsbG8=")
+ enum = TC.Enumeration(['Red', 'Blue', 'Green'], 'color')
+ self.failUnlessEqual(enum.parse(elts[6], ps), u'Red')
+ self.failUnlessEqual(TC.IEnumeration([44,46,47]).parse(elts[1],ps),
+ 44)
+ S = TC.Struct(None, [TC.String('t'), TC.Integer('i')], inorder=0)
+ pyobj = S.parse(elts[8], ps)
+ S2 = TC.Struct(myclass, [TC.IunsignedShort('i'), TC.String('q:z',
+ minOccurs=0), TC.String('t')], 'a2', typed=0)
+ pyobj2 = S2.parse(elts[8], ps)
+ self.failUnlessEqual(TC.URI().parse(elts[12], ps),
+ u'"http://foo.com/~salz"')
+ self.failUnlessEqual(pyobj["i"], pyobj2.i)
+ self.failUnlessEqual(pyobj["t"], pyobj2.t)
+
+ tcary = TC.Array('SOAP-ENC:int', TC.Integer())
+ nsa = tcary.parse(elts[14],ps)
+ self.failUnlessEqual(nsa, [None, None, None, 12, 13, 14, 15, 16, 17])
+ tcary.sparse = 1
+ sa = tcary.parse(elts[14],ps)
+ self.failUnlessEqual(sa,
+ [(3, 12), (4, 13), (5, 14), (6, 15), (7, 16), (8, 17)])
+
+ """
+ mychoice = TC.Choice([
+ TC.String('n3'),
+ TC.URI('uri'),
+ TC.Integer('Price'),
+ ])
+
+ b = mychoice.parse(elts[0], ps)
+ self.failUnlessEqual(b[0], 'Price')
+ self.failUnlessEqual(b[1], 34)
+ b = mychoice.parse(elts[12], ps)
+ self.failUnlessEqual(b[0], 'uri')
+ self.failUnlessEqual(b[1], u'"http://foo.com/~salz"')
+ b = mychoice.parse(elts[4], ps)
+ self.failUnlessEqual(b[0], 'n3')
+ self.failUnlessEqual(b[1], u'The value of n3')
+ """
+
+ self.failUnlessEqual(TC.Array(('test-uri','x'), TC.Any()).parse(elts[15], ps),
+ [u'The value of n3', u'rich salz', 13])
+ self.failUnlessEqual(TC.Struct(None,(TC.FPfloat('a'), TC.Decimal('b'),
+ TC.FPdouble('c'))).parse(elts[13],ps),
+ {'a': 6.9000000000000004, 'c':
+ TC._make_inf(), 'b': 0.0})
+ nsdict = ps.GetElementNSdict(ps.header)
+ nsdict[''] = "http://www.zolera.com/ns/"
+ nsdict['q'] = 'q-namespace-uri'
+ sio = StringIO.StringIO()
+ z = SoapWriter(sio, header=ps.header_elements, nsdict=nsdict)
+ z.serialize(pyobj2, S2)
+ S2.inline = 1
+ S2.typed = 0
+ tc = TC.gDateTime('dt')
+ z.serialize(pyobj2, S2)
+ z.serialize(pyobj, S)
+ #z.serialize(('n3', '******this is the value of a union'), mychoice)
+ z.serialize('uri:some/place/special', TC.XML('foo', nsdict=nsdict))
+ tcary.sparse = False
+ z.serialize(nsa, tcary, childnames='tt')
+ tcary.sparse = True
+ z.serialize(sa, tcary, name='MYSPARSEARRAY')
+ z.serialize(time.time(), tc)
+ z.serialize(time.time(), TC.gTime('monthday'))
+ z.serialize('$$$$$foo<', TC.String(textprotect=0))
+ self.failUnlessEqual(TC.Any().parse(elts[11], ps),
+ {'urt-i': 12, 'urt-t': u'rich salz'})
+
+ try:
+ a = bar()
+ except Exception, e:
+ f = FaultFromException(e, 0, sys.exc_info()[2])
+ print f.AsSOAP()
+ print
+ print
+ print FaultFromNotUnderstood('myuri', 'dalocalname', actor='cher').AsSOAP()
+ print
+ print
+ print FaultFromActor('actor:i:dont:understand').AsSOAP()
+
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t1TestCase, "check"))
+ return suite
+
+## exceptions
+def foo():
+ '''dummy'''
+ return 3 / 0
+
+def bar():
+ return foo() + 2
+
+class zParseException: pass
+
+class myclass:
+ def __init__(self, name=None):
+ self.name = name or id(self)
+ self.z = 'z value'
+ def __str__(self):
+ return 'myclass-%s-(%d,"%s")' % (self.name, self.i, self.t) + \
+ str(self.z)
+
+datatest = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header xmlns:t="http://www.zolera.com/ns/" xmlns:q='"'>
+ <t:sometag SOAP-ENV:mustUnderstand="1">you must grok sometag</t:sometag>
+ </SOAP-ENV:Header>
+ <SOAP-ENV:Body xmlns='test-uri'>
+ <root SOAP-ENC:root='1'/>
+ <Price xsi:type='xsd:integer'>34</Price> <!-- 0 -->
+ <SOAP-ENC:byte>44</SOAP-ENC:byte> <!-- 1 -->
+ <Name>This is the name</Name> <!-- 2 -->
+ <n2><xmldoc><![CDATA[<greeting>Hello</greeting>]]></xmldoc></n2> <!-- 3 -->
+ <n3 href='#zzz' xsi:type='SOAP-ENC:string'/> <!-- 4 -->
+ <n64>a GVsbG8=</n64> <!-- 5 -->
+ <SOAP-ENC:string>Red</SOAP-ENC:string> <!-- 6 -->
+ <a2 href='#tri2'/> <!-- 7 -->
+ <a2><i>12</i><t>rich salz</t></a2> <!-- 8 -->
+ <xsd:hexBinary>3F2041</xsd:hexBinary> <!-- 9 -->
+ <nullint xsi:nil='1'/> <!-- 10 -->
+ <Anytest><urt-i xsi:type='SOAP-ENC:byte'>12</urt-i>
+ <urt-t id="urtid"
+ xsi:type="xsd:string">rich salz</urt-t></Anytest> <!-- 11 -->
+ <uri>"http://foo.com/%7Esalz"</uri> <!-- 12 -->
+ <floattest> <!-- 13 -->
+ <a>6.9</a> <b>-0</b> <c>INF</c>
+ </floattest>
+ <atest SOAP-ENC:offset='[3]' SOAP-ENC:arrayType="x"> <!-- 14 -->
+ <i>12</i>
+ <SOAP-ENC:integer id='n13'>13</SOAP-ENC:integer>
+ <i>14</i>
+ <i>15</i>
+ <i>16</i>
+ <i>17</i>
+ </atest>
+ <sarray SOAP-ENC:arrayType="struct"> <!-- 15 -->
+ <i href="#zzz" xsi:type='xsd:string'/>
+ <i href="#urtid"/>
+ <thing href="#n13"/>
+ </sarray>
+ <xpath>//sarray</xpath> <!-- 16 -->
+ <z xmlns='myns' xsi:type='SOAP-ENC:string' id='zzz'>The value of n3</z>
+ <zz xmlns='myns2' id='tri2'>
+ <inner xmlns='myns2' >
+ <f1>content</f1>
+ <sec xmlns='myns2' >ond</sec >
+ </inner>
+ </zz>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/test/test_t2.py b/ZSI/zsi/test/test_t2.py
new file mode 100644
index 0000000..75c2731
--- /dev/null
+++ b/ZSI/zsi/test/test_t2.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+import unittest, sys
+from ZSI import *
+
+
+class t2TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t2 test case"
+
+ def checkt2(self):
+ try:
+ ps = ParsedSoap(IN)
+ except ParseException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
+ except Exception, e:
+ # Faulted while processing; assume it's in the
+ # header.
+ print >>OUT, FaultFromException(e, 1).AsSOAP()
+ self.fail()
+ # We are not prepared to handle any actors or mustUnderstand elements.
+ # Arbitrary fault back with the first one found.
+ a = ps.WhatActorsArePresent()
+ if len(a):
+ print >>OUT, FaultFromActor(a[0]).AsSOAP()
+ self.fail()
+ mu = ps.WhatMustIUnderstand()
+ if len(mu):
+ uri, localname = mu[0]
+ print >>OUT, FaultFromNotUnderstood(uri, localname).AsSOAP()
+ self.fail()
+
+
+ try:
+ player = ps.Parse(Player)
+ except EvaluateException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
+
+ try:
+ import operator
+ total = reduce(operator.add, player.Scores, 0)
+ result = Average(foo(total, len(player.Scores)))
+ sw = SoapWriter().serialize(result)
+ print >>OUT, str(sw)
+ except Exception, e:
+ print >>OUT, FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP()
+ self.fail()
+
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t2TestCase, "check"))
+ return suite
+
+
+class Player:
+ '''Input class.'''
+ def __init__(self, name=None):
+ pass
+Player.typecode = TC.Struct(Player, [ TC.String('Name', optional=1),
+ TC.Array('xsd:integer', TC.Integer(),
+ 'Scores'), ], 'GetAverage')
+class Average:
+ '''Output class.'''
+ def __init__(self, average):
+ self.average = average
+Average.typecode = TC.Struct(Average, [ TC.Integer('average'),
+ ], 'GetAverageResponse', inline=1)
+
+def bar(total, len):
+ return total / len
+
+def foo(total, len):
+ return bar(total, len)
+
+OUT = sys.stdout
+IN='''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/">
+ <SOAP-ENV:Header>
+ <trans SOAP-ENV:mustUnderstand="0"/>
+ </SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <GetAverage>
+ <Scores SOAP-ENC:arrayType="xsd:integer">
+ <i>84</i>
+ <xxi>101</xxi>
+ <foi>200</foi>
+ <izzz>4</izzz>
+ </Scores>
+ <Name>John Doe</Name>
+ </GetAverage>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/test/test_t3.py b/ZSI/zsi/test/test_t3.py
new file mode 100644
index 0000000..837b52b
--- /dev/null
+++ b/ZSI/zsi/test/test_t3.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+import unittest
+from ZSI import *
+from ZSI.wstools.logging import setBasicLoggerDEBUG
+setBasicLoggerDEBUG()
+
+class t3TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t3 test case"
+
+ def checkt3(self):
+ a = None
+ try:
+ 3 / 0
+ except Exception, e:
+ a = e
+ f = FaultFromException(a, 0)
+ text = f.AsSOAP()
+ i = 0
+ for l in text.split('\n'):
+ print i, l
+ i += 1
+ ps = ParsedSoap(text)
+ if ps.IsAFault():
+ f = FaultFromFaultMessage(ps)
+ print f.AsSOAP()
+ self.failUnless(f.AsSOAP().find(str(a)) > 0)
+ print '--'*20
+
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t3TestCase, "check"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/test/test_t4.py b/ZSI/zsi/test/test_t4.py
new file mode 100644
index 0000000..e2f7a3c
--- /dev/null
+++ b/ZSI/zsi/test/test_t4.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+import unittest, sys
+from ZSI import *
+from ZSI import resolvers
+from xml.dom.ext import PrettyPrint
+
+OUT = sys.stdout
+IN='''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/">
+ <SOAP-ENV:Body>
+ <hreftest>
+ <xmltest href="http://www-itg.lbl.gov/~kjackson/zsi.xsd"/>
+ <stringtest href="http://www.microsoft.com"/>
+ </hreftest>
+ </SOAP-ENV:Body>
+ </SOAP-ENV:Envelope>
+ '''
+
+class t4TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t4 test case"
+
+ def checkt4(self):
+ try:
+ r = resolvers.NetworkResolver(['http:'])
+ ps = ParsedSoap(IN, resolver=r.Resolve)
+ except ParseException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ print >>OUT, FaultFromException(e, 1, sys.exc_info()[2]).AsSOAP()
+ self.fail()
+ print 'resolving'
+ typecode = TC.Struct(None, [ TC.XML('xmltest'),
+ TC.String('stringtest', resolver=r.Opaque), ])
+ try:
+ dict = ps.Parse(typecode)
+ except EvaluateException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
+ except Exception, e:
+ # Faulted while processing; now it's the body
+ print >>OUT, FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP()
+ self.fail()
+ PrettyPrint(dict['xmltest'])
+ print '**', dict['stringtest'], '**'
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t4TestCase, "check"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/test/test_t5.py b/ZSI/zsi/test/test_t5.py
new file mode 100644
index 0000000..863e02f
--- /dev/null
+++ b/ZSI/zsi/test/test_t5.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+import unittest, multifile, mimetools
+from ZSI import *
+from ZSI import resolvers
+from xml.dom import Node
+from xml.dom.ext.reader import PyExpat
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
+class t5TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t5 test case"
+
+ def checkt5(self):
+ istr = StringIO.StringIO(intext)
+ m = mimetools.Message(istr)
+ if m.gettype()[0:10] == "multipart/":
+ cid = resolvers.MIMEResolver(m['content-type'], istr)
+ xml = cid.GetSOAPPart()
+ print 'xml=', xml.getvalue()
+ for h,b in cid.parts:
+ print h, b.read()
+ dom = PyExpat.Reader().fromStream(xml)
+ print dom
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t5TestCase, "check"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+intext = '''Content-Type: multipart/mixed; boundary="sep"
+Subject: testing
+
+skip this
+
+--sep
+Content-type: text/xml
+
+<foo xmlns='www.zolera.com'>hello world</foo>
+--sep
+Content-Type: text/plain
+content-id: <part111@example.zolera.com>
+
+this is some plain text
+--sep
+content-type: application/skipme
+
+do not see this
+okay?
+--sep
+Content-Type: text/xml
+Content-ID: <part2@example.zolera.com>
+
+<xml>spoken</xml>
+--sep--
+'''
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/test/test_t6.py b/ZSI/zsi/test/test_t6.py
new file mode 100644
index 0000000..b5ff9f6
--- /dev/null
+++ b/ZSI/zsi/test/test_t6.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+import unittest, sys, multifile, mimetools, base64
+from xml.dom import Node
+from xml.dom.ext.reader import PyExpat
+from ZSI import *
+from ZSI import resolvers
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
+class t6TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t6 test case"
+
+ def checkt6(self):
+ try:
+ istr = StringIO.StringIO(intext)
+ m = mimetools.Message(istr)
+ cid = resolvers.MIMEResolver(m['content-type'], istr)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ except ParseException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ print >>OUT, FaultFromException(e, 1, sys.exc_info()[2]).AsSOAP()
+ self.fail()
+
+ try:
+ dict = ps.Parse(typecode)
+ except Exception, e:
+ # Faulted while processing; now it's the body
+ print >>OUT, FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP()
+ self.fail()
+
+ self.failUnlessEqual(dict['stringtest'], strExtTest,
+ "Failed to extract stringtest correctly")
+ print base64.encodestring(cid['partii@zolera.com'].read())
+ v = dict['b64']
+ print type(v), 'is type(v)'
+ self.failUnlessEqual(cid['partii@zolera.com'].getvalue(), v,
+ "mismatch")
+ print base64.encodestring(v)
+ from ZSI.wstools.c14n import Canonicalize
+ z = dict['xmltest']
+ print type(z), z
+ print Canonicalize(z)
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t6TestCase, "check"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+OUT = sys.stdout
+typecode = TC.Struct(None, [
+ TC.String('b64'),
+ TC.String('stringtest'),
+ TC.XML('xmltest'),
+ ])
+
+intext='''Return-Path: <rsalz@zolera.com>
+Received: from zolera.com (os390.zolera.com [10.0.1.9])
+ by zolera.com (8.11.0/8.11.0) with ESMTP id f57I2sf00832
+ for <rsalz@zolera.com>; Thu, 7 Jun 2001 14:02:54 -0400
+Sender: rsalz@zolera.com
+Message-ID: <3B1FC1D1.FF6B21B4@zolera.com>
+Date: Thu, 07 Jun 2001 14:02:57 -0400
+From: Rich Salz <rsalz@zolera.com>
+X-Mailer: Mozilla 4.72 [en] (X11; U; Linux 2.2.14-5.0 i686)
+X-Accept-Language: en
+MIME-Version: 1.0
+To: rsalz@zolera.com
+Subject: mime with attachments
+Content-Type: multipart/mixed;
+ boundary="------------68E4BAC5B266315E42428C64"
+Status: R
+
+This is a multi-part message in MIME format.
+--------------68E4BAC5B266315E42428C64
+Content-Type: text/plain; charset=us-ascii
+Content-Transfer-Encoding: 7bit
+
+<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/">
+<SOAP-ENV:Body>
+<hreftest>
+ <stringtest href="cid:part1@zolera.com"/>
+ <b64 href="cid:partii@zolera.com"/>
+ <xmltest href="cid:12@zolera.com"/>
+</hreftest>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+--------------68E4BAC5B266315E42428C64
+Content-Type: text/plain; charset=us-ascii;
+ name="abs.txt"
+Content-Transfer-Encoding: 7bit
+Content-ID: <part1@zolera.com>
+Content-Disposition: inline;
+ filename="abs.txt"
+
+
+Digitial Signatures in a Web Services World
+
+An influential Forrestor report created the term inverted security: it's
+not about who you keep out, it's about who you let in. Customer portals,
+without a costly PKI deployment or application integration issues.
+
+--------------68E4BAC5B266315E42428C64
+Content-Type: application/pdf;
+ name="gmpharma.pdf"
+Content-Transfer-Encoding: base64
+Content-ID: <partii@zolera.com>
+Content-Disposition: inline;
+ filename="gmpharma.pdf"
+
+JVBERi0xLjINJeLjz9MNCjQzIDAgb2JqDTw8IA0vTGluZWFyaXplZCAxIA0vTyA0NSANL0gg
+WyAxMTQ0IDM5NiBdIA0vTCA2NjkwMiANL0UgMTAyODIgDS9OIDkgDS9UIDY1OTI0IA0+PiAN
+RB3nwVOQH9JpmFv6Ri2Zq7mlddSS2B5WcZwvAP+gy9QtuYlfqj1rsi9WqJOszzHXmXZ8fXxK
+XBBztIpgbkRrd+SGtY4QXo0fX0VN86uKXwtrkd7h1qiq2FUtXl6uNfnCoyX1Dve1O3RPRyhG
+sKn6fLMb+uSSIHPQkClRBwu5gechz/1PBUBSB34jXbPdMTIb+/wRP+pauSAhLBzFELDOgk5b
+PaIPAnIudFovQTc7Df2Ws9Atz4Bua+oINphIOojogG5LP3Tb3oNu8bsmuK+wFXEdbfgFIx+G
+gKULYx5A2WnaDXB5JeoRQg90S0HcX2dCPmRCqDXB/aX34KujsPwJ/UpRdxXPeAftDkQS6hag
+bh/yTOiUyqBz9CzxnyMYQGDO0jrUZ47kkWfmYvVg
+--------------68E4BAC5B266315E42428C64
+Content-ID: <12@zolera.com>
+
+<foo xmlns="example.com" xmlns:Z="zolera">
+ this is a foo
+ <b xmlns:Z="zolera">redundnant ns decl</b>
+ <b Z:x="this was first" Z:a="2nd-orig">b test</b>
+</foo>
+
+--------------68E4BAC5B266315E42428C64--
+'''
+
+strExtTest = '''
+Digitial Signatures in a Web Services World
+
+An influential Forrestor report created the term inverted security: it's
+not about who you keep out, it's about who you let in. Customer portals,
+without a costly PKI deployment or application integration issues.
+
+'''
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/test/test_t7.py b/ZSI/zsi/test/test_t7.py
new file mode 100644
index 0000000..0c71405
--- /dev/null
+++ b/ZSI/zsi/test/test_t7.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+import unittest, sys
+from ZSI import *
+
+
+class t7TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t7 test case"
+
+ def checkt7(self):
+ ps = ParsedSoap(text)
+
+ tcdict = TC.Apache.Map('c-gensym1')
+ tclist = TC.Apache.Map('c-gensym1', aslist=1)
+
+ d = tcdict.parse(ps.body_root, ps)
+ self.assertEqual(d, { u'a':123, '\x00\x01':456 })
+ print 'as dictionary\n', d
+
+ l = tclist.parse(ps.body_root, ps)
+ self.assertEqual(l, [('\x00\x01', 456), (u'a', 123)])
+ print '\n', '=' * 30
+ print 'as list\n', l
+
+ print '\n', '=' * 30
+ sw = SoapWriter()
+ sw.serialize(d, tcdict)
+ print >>sys.stdout, sw
+
+ print '\n', '=' * 30
+ sw = SoapWriter()
+ sw.serialize(l, tclist)
+ print >>sys.stdout, sw
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t7TestCase, "check"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+text = '''
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/1999/XMLSchema"
+ xmlns:xmlsoap="http://xml.apache.org/xml-soap">
+<SOAP-ENV:Body>
+<c-gensym1 xsi:type="xmlsoap:Map">
+ <item>
+ <key xsi:type="SOAP-ENC:base64">AAE=</key>
+ <value xsi:type="xsd:int">456</value>
+ </item>
+ <item>
+ <key xsi:type="xsd:string">a</key>
+ <value xsi:type="xsd:int">123</value>
+ </item>
+</c-gensym1>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+'''
+
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/test/test_t8.py b/ZSI/zsi/test/test_t8.py
new file mode 100644
index 0000000..bc5b278
--- /dev/null
+++ b/ZSI/zsi/test/test_t8.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python
+import unittest, sys, types, time
+from ZSI import TC, SoapWriter, ParsedSoap, EvaluateException
+from ZSI.wstools.Namespaces import SCHEMA, SOAP
+
+NSDICT = {'tns':'xmlns:tns="urn:a"',
+ 'xsi':'xmlns:xsi="%s"' %SCHEMA.XSI3,
+ 'xsd':'xmlns:xsd="%s"' %SCHEMA.XSD3,
+ 'soap':'xmlns:SOAP-ENC="%s"' %SOAP.ENC,
+}
+
+class AnyTestCase(unittest.TestCase):
+ "Test Any serialize and parse"
+
+ def check_empty_array(self):
+ """Empty Array returned as list()
+ """
+ data = []
+ s = str(SoapWriter().serialize(data,TC.Any(aslist=True)))
+ p = ParsedSoap(s).Parse(TC.Any())
+ self.failUnless(data==p, 'expecting "%s", got "%s"' %(data,p))
+
+ def check_empty_struct(self):
+ """Empty Struct is None, maybe dict() makes more sense, but this
+ is fairly hard to determine if not typed (which is the norm).
+ """
+ data = {}
+ s = str(SoapWriter().serialize(data,TC.Any()))
+ p = ParsedSoap(s).Parse(TC.Any())
+ self.failUnless(p==None, 'expecting "%s", got "%s"' %(None,p))
+
+ def check_parse_empty_all(self):
+ # None
+ skip = [TC.FPEnumeration, TC.Enumeration, TC.IEnumeration, TC.List, TC.Integer]
+ for typeclass in filter(lambda c: type(c) in [types.ClassType,type] and not issubclass(c, TC.String) and issubclass(c, TC.SimpleType), TC.__dict__.values()):
+ if typeclass in skip: continue
+ tc = typeclass()
+ sw = SoapWriter()
+ sw.serialize(None, typecode=tc, typed=True)
+ soap = str(sw)
+ ps = ParsedSoap(soap)
+ parsed = ps.Parse(TC.Any())
+ self.assertEqual(None, parsed)
+
+ def check_parse_empty_string(self):
+ # Empty String
+ typecodes = TC.Any.parsemap.values()
+ for tc in filter(lambda c: isinstance(c, TC.String), TC.Any.parsemap.values()):
+ sw = SoapWriter()
+ sw.serialize("", typecode=tc, typed=True)
+ soap = str(sw)
+ ps = ParsedSoap(soap)
+ parsed = ps.Parse(TC.Any())
+ self.assertEqual("", parsed)
+
+ def check_builtins(self):
+ myInt,myLong,myStr,myDate,myFloat = 123,2147483648,\
+ u"hello", time.gmtime(), 1.0001
+ orig = [myInt,myLong,myStr,myDate,myFloat]
+
+ sw = SoapWriter()
+ sw.serialize(orig, typecode=TC.Any(pname="builtins", aslist=True))
+
+ ps = ParsedSoap(str(sw))
+ parsed = ps.Parse(TC.Any())
+ self.assertEqual(len(orig), len(parsed))
+
+ self.assertEqual(myInt, parsed[0])
+ self.assertEqual(myLong, parsed[1])
+ self.assertEqual(myStr, parsed[2])
+ self.assertEqual(myDate[0:6], parsed[3][0:6])
+ self.assertEqual(myFloat, parsed[4])
+
+ self.assertEqual(type(myInt), type(parsed[0]))
+ self.assertEqual(type(myLong), type(parsed[1]))
+ self.assertEqual(type(myStr), type(parsed[2]))
+ self.assertEqual(tuple, type(parsed[3]))
+ self.assertEqual(type(myFloat), type(parsed[4]))
+
+ def check_any_nill(self):
+ result = ['23', {'a' : None, 'b': 5}]
+ soap = str(SoapWriter().serialize(result, TC.Any(pname="NilRequest", nillable=True, aslist=True)))
+
+ ps = ParsedSoap(soap)
+ tc = TC.Any(nillable=True)
+ pyobj = ps.Parse(tc)
+
+ def check_any_compound(self):
+ # from zsi developer's guide
+ xml = """
+<tns:foo %(tns)s %(xsi)s %(soap)s>
+ <tns:i xsi:type="SOAP-ENC:integer">12</tns:i>
+ <tns:name xsi:type="SOAP-ENC:string">Hello world</tns:name>
+</tns:foo>""" %NSDICT
+
+ ps = ParsedSoap(xml, envelope=False)
+ self.failUnless(ps.Parse(TC.Any()) == {'i': 12, 'name': 'Hello world'})
+ self.failUnless(ps.Parse(TC.Any(aslist=True)) == [12, 'Hello world'])
+
+ def check_any_typed_soap_integer(self):
+ # from zsi developer's guide
+ value = 12
+ d = dict(value=value)
+ d.update(NSDICT)
+ xml = """<tns:i xsi:type="SOAP-ENC:integer" %(xsi)s %(soap)s %(tns)s>%(value)d</tns:i>""" %d
+ ps = ParsedSoap(xml, envelope=False)
+ self.failUnless(ps.Parse(TC.Any()) == value)
+
+ def check_any_typed_xsd_int(self):
+ # from zsi developer's guide
+ value = 12
+ d = dict(value=value)
+ d.update(NSDICT)
+ xml = """<tns:i xsi:type="xsd:int" %(xsi)s %(soap)s %(tns)s %(xsd)s>%(value)d</tns:i>""" %d
+ ps = ParsedSoap(xml, envelope=False)
+ self.failUnless(ps.Parse(TC.Any()) == value)
+
+ def check_any_typed_nonNegativeInteger(self):
+ # from zsi developer's guide
+ value = 12
+ d = dict(value=value)
+ d.update(NSDICT)
+ xml = """<tns:i xsi:type="xsd:nonNegativeInteger" %(xsi)s %(soap)s %(tns)s %(xsd)s>%(value)d</tns:i>""" %d
+ ps = ParsedSoap(xml, envelope=False)
+ self.failUnless(ps.Parse(TC.Any()) == value)
+
+ def check_any_untyped_int(self):
+ # from zsi developer's guide
+ d = dict(value=12)
+ d.update(NSDICT)
+ xml = """<tns:i %(tns)s>12</tns:i>""" %NSDICT
+ ps = ParsedSoap(xml, envelope=False)
+ self.failUnlessRaises(EvaluateException, ps.Parse, TC.Any())
+
+
+#
+# Creates permutation of test options: "check", "check_any", etc
+#
+_SEP = '_'
+for t in [i[0].split(_SEP) for i in filter(lambda i: callable(i[1]), AnyTestCase.__dict__.items())]:
+ test = ''
+ for f in t:
+ test += f
+ if globals().has_key(test): test += _SEP; continue
+ def _closure():
+ name = test
+ def _makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(AnyTestCase, name))
+ return suite
+ return _makeTestSuite
+
+ globals()[test] = _closure()
+ test += _SEP
+
+makeTestSuite = check
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+if __name__ == "__main__" : main()
+
+
diff --git a/ZSI/zsi/test/test_t9.py b/ZSI/zsi/test/test_t9.py
new file mode 100644
index 0000000..d2b6c84
--- /dev/null
+++ b/ZSI/zsi/test/test_t9.py
@@ -0,0 +1,363 @@
+#!/usr/bin/env python
+import unittest, sys, sha, base64
+from ZSI import _get_element_nsuri_name
+from ZSI.parse import ParsedSoap
+from ZSI.wstools.c14n import Canonicalize
+from ZSI.wstools.Namespaces import WSA200403, SOAP
+from cStringIO import StringIO
+
+
+class CanonicalizeFromTestCase(unittest.TestCase):
+ "c14n tests, this has nothing to do with ws-addressing."
+
+ def setUp(self):
+ self.ps = ParsedSoap(XML_INST1)
+ self.el = filter(lambda el: _get_element_nsuri_name(el) == (WSA200403.ADDRESS, "From"),
+ self.ps.header_elements)[0]
+
+ def tearDown(self):
+ del self.ps
+ del self.el
+
+ def check_c14n(self):
+ """http://www.w3.org/TR/xml-c14n
+ """
+ s = StringIO()
+ Canonicalize(self.el, s, unsuppressedPrefixes=None)
+ cxml = s.getvalue()
+ d1 = base64.encodestring(sha.sha(C14N_INC1).digest()).strip()
+ d2 = base64.encodestring(sha.sha(cxml).digest()).strip()
+ self.assertEqual(d1, d2)
+ self.assertEqual(d1, C14N_INC1_DIGEST)
+
+ def check_c14n_exc(self):
+ """http://www.w3.org/TR/xml-exc-c14n/
+ """
+ s = StringIO()
+ Canonicalize(self.el, s, unsuppressedPrefixes=[])
+ cxml = s.getvalue()
+ d1 = base64.encodestring(sha.sha(C14N_EXCL1).digest()).strip()
+ d2 = base64.encodestring(sha.sha(cxml).digest()).strip()
+ self.assertEqual(d1, C14N_EXCL1_DIGEST)
+ self.assertEqual(d1, d2)
+
+ def check_c14n_exc2_unsuppressed(self):
+ """http://www.w3.org/TR/xml-exc-c14n/
+ The method of canonicalization described in this specification receives
+ an InclusiveNamespaces PrefixList parameter, which lists namespace prefixes
+ that are handled in the manner described by the Canonical XML Recommendation
+ """
+ s = StringIO()
+ Canonicalize(self.el, s, unsuppressedPrefixes=['xsi', 'xsd'])
+ cxml = s.getvalue()
+ d1 = base64.encodestring(sha.sha(C14N_EXCL2).digest()).strip()
+ d2 = base64.encodestring(sha.sha(cxml).digest()).strip()
+ self.assertEqual(d1, C14N_EXCL2_DIGEST)
+ self.assertEqual(d1, d2)
+
+ def check_c14n_exc3(self):
+ """http://www.w3.org/TR/xml-exc-c14n/
+ tests if a namespace defined in a parent node to the top node
+ to be canonicalized is added when discovered that this namespace
+ is used.
+ """
+ self.ps = ParsedSoap(XML_INST2)
+ self.el = self.ps.body
+
+ s = StringIO()
+ Canonicalize(self.el, s, unsuppressedPrefixes=[])
+ cxml = s.getvalue()
+ print cxml
+ d1 = base64.encodestring(sha.sha(C14N_EXCL3).digest()).strip()
+ d2 = base64.encodestring(sha.sha(cxml).digest()).strip()
+ self.assertEqual(d1, C14N_EXCL3_DIGEST)
+ self.assertEqual(d1, d2)
+
+ def xcheck_c14n_exc4(self):
+ RCVDIGEST = "jhTbi7gWlY9oLqsRr+EZ0bokRFA="
+ CALDIGEST = "IkMyI4zCDlK41qE7sZxvkFHJioU="
+
+ d1 = base64.encodestring(sha.sha(WRONG).digest()).strip()
+ d2 = base64.encodestring(sha.sha(CORRECT).digest()).strip()
+
+ ps = ParsedSoap(XML_INST4)
+ el = filter(lambda el: _get_element_nsuri_name(el) == (WSA200403.ADDRESS, "MessageID"),
+ ps.header_elements)[0]
+
+ s = StringIO()
+ Canonicalize(el, s, unsuppressedPrefixes=[])
+ cxml = s.getvalue()
+ print "-- "*20
+ print cxml
+ print "-- "*20
+ d3 = base64.encodestring(sha.sha(cxml).digest()).strip()
+
+ self.assertEqual(d1, RCVDIGEST)
+ self.assertEqual(d2, CALDIGEST)
+ self.assertEqual(d3, CALDIGEST)
+
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ #suite.addTest(unittest.makeSuite(CanonicalizeFromTestCase, "check"))
+ suite.addTest(unittest.makeSuite(CanonicalizeFromTestCase, "xcheck"))
+ return suite
+
+
+C14N_EXCL1_DIGEST = "xSOXT+dlQwo5uT9PbK08of6W9PM="
+C14N_EXCL1 = """<wsa:From xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" ns3:Id="id-7680063" soapenv:mustUnderstand="0"><wsa:Address>http://bosshog.lbl.gov:9999/wsrf/services/SecureCounterService</wsa:Address><wsa:ReferenceProperties><ns1:CounterKey xmlns:ns1="http://counter.com" ns3:Id="10112">10577413</ns1:CounterKey></wsa:ReferenceProperties></wsa:From>"""
+
+C14N_INC1_DIGEST = "qdU4f7/+BeHV/JlVGIPM90fNeV8="
+C14N_INC1 = """<wsa:From xmlns:ns1="http://counter.com" xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ns3:Id="id-7680063" soapenv:mustUnderstand="0"><wsa:Address>http://bosshog.lbl.gov:9999/wsrf/services/SecureCounterService</wsa:Address><wsa:ReferenceProperties><ns1:CounterKey ns3:Id="10112">10577413</ns1:CounterKey></wsa:ReferenceProperties></wsa:From>"""
+
+C14N_EXCL2_DIGEST = "+IEqF6DRo36Bh93A06S7C4Cmcuo="
+C14N_EXCL2 = """<wsa:From xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ns3:Id="id-7680063" soapenv:mustUnderstand="0"><wsa:Address>http://bosshog.lbl.gov:9999/wsrf/services/SecureCounterService</wsa:Address><wsa:ReferenceProperties><ns1:CounterKey xmlns:ns1="http://counter.com" ns3:Id="10112">10577413</ns1:CounterKey></wsa:ReferenceProperties></wsa:From>"""
+
+
+C14N_EXCL3_DIGEST = "VJvTr+Mx3TeWsQY6iwGbhAJ9/eA="
+C14N_EXCL3 = """<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-28219008"><RequestSecurityTokenResponse xmlns="http://schemas.xmlsoap.org/ws/2004/04/trust"><wsa:EndpointReference xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"><wsa:Address>http://131.243.2.147:8888/wsrf/services/DelegationService</wsa:Address><wsa:ReferenceProperties><ns1:DelegationKey xmlns:ns1="http://www.globus.org/08/2004/delegationService">8adaa710-ba01-11da-bc99-cbed73daa755</ns1:DelegationKey></wsa:ReferenceProperties></wsa:EndpointReference></RequestSecurityTokenResponse></soapenv:Body>"""
+
+
+XML_INST1 = """<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Header>
+ <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:SignedInfo>
+ <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="soapenv wsa xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:CanonicalizationMethod>
+ <ds:SignatureMethod Algorithm="http://www.globus.org/2002/04/xmlenc#gssapi-sign"/>
+ <ds:Reference URI="#id-8409752">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="wsa xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>m9pihAqIBdcdk7ytDvccj89eWi8=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-11434871">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>ofD+Ket5kzR2u/5jWKbFTMtmigk=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-19645447">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>SoQ7RlJa3r94weDWBuWAg/BvydQ=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-5428820">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>z6sCEkkRJrCuY/C0S5b+46WfyMs=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-7680063">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>+IEqF6DRo36Bh93A06S7C4Cmcuo=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-28476580">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>NFltkKAJpmMkPbJQj5MW1qVceto=</ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue>AAAAAAAAAAMAAAvZTrXlZjRSO7tP12tId+lehprEKgk=</ds:SignatureValue>
+ <ds:KeyInfo>
+ <wsse:SecurityTokenReference><wsse:Reference URI="#SecurityContextToken-32970611" ValueType="http://www.globus.org/ws/2004/09/security/sc#GSSAPI_CONTEXT_TOKEN"/></wsse:SecurityTokenReference>
+ </ds:KeyInfo>
+ </ds:Signature><wsc:SecurityContextToken wsu:Id="SecurityContextToken-32970611" xmlns:wsc="http://schemas.xmlsoap.org/ws/2004/04/sc" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsc:Identifier>3b1ef410-ab3d-11da-9436-88b687faed94</wsc:Identifier></wsc:SecurityContextToken></wsse:Security><wsa:MessageID wsu:Id="id-11434871" soapenv:mustUnderstand="0" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">uuid:3d592ca0-ab3d-11da-9436-88b687faed94</wsa:MessageID><wsa:To wsu:Id="id-19645447" soapenv:mustUnderstand="0" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:To><wsa:Action wsu:Id="id-5428820" soapenv:mustUnderstand="0" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">http://counter.com/CounterPortType/addResponse</wsa:Action><wsa:From ns3:Id="id-7680063" soapenv:mustUnderstand="0" xmlns:ns1="http://counter.com" xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsa:Address>http://bosshog.lbl.gov:9999/wsrf/services/SecureCounterService</wsa:Address><wsa:ReferenceProperties><ns1:CounterKey ns3:Id="10112">10577413</ns1:CounterKey></wsa:ReferenceProperties></wsa:From><wsa:RelatesTo RelationshipType="wsa:Reply" wsu:Id="id-28476580" soapenv:mustUnderstand="0" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">uuid:1141449047.05</wsa:RelatesTo></soapenv:Header><soapenv:Body wsu:Id="id-8409752" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><addResponse xmlns="http://counter.com">13</addResponse></soapenv:Body></soapenv:Envelope>"""
+
+
+XML_INST2 = """<?xml version="1.0" encoding="UTF-8"?>
+ <soapenv:Envelope
+xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Header>
+ <wsse:Security soapenv:mustUnderstand="1"
+xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><wsse:BinarySecurityToken
+EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
+ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"
+wsu:Id="CertId-1851922"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">MIIGbDCCA6owggKSoAMCAQICAh07MA0GCSqGSIb3DQEBBQUAMGkxEzARBgoJkiaJk/IsZAEZFgNv
+
+cmcxGDAWBgoJkiaJk/IsZAEZFghET0VHcmlkczEgMB4GA1UECxMXQ2VydGlmaWNhdGUgQXV0aG9y
+
+aXRpZXMxFjAUBgNVBAMTDURPRUdyaWRzIENBIDEwHhcNMDUxMjIxMjExNzUzWhcNMDYxMjIxMjEx
+
+NzUzWjBfMRMwEQYKCZImiZPyLGQBGRYDb3JnMRgwFgYKCZImiZPyLGQBGRYIZG9lZ3JpZHMxDzAN
+
+BgNVBAsTBlBlb3BsZTEdMBsGA1UEAxMUTWF0dCBSb2RyaWd1ZXogODkzMzAwggEiMA0GCSqGSIb3
+
+DQEBAQUAA4IBDwAwggEKAoIBAQCec6hEiQcu1lIa2pS/KxgmXbkfKLKrOm6AxPrfkht6Ja91+rdY
+
+TLQ4a21S792hglezFbylzLkDmCzYp43fH1xh0LlLea+YzUB7LoUnG29qv73CylSYqDnJWAU+sHhw
+
+fr3Hqpp6GxbxPqXJXcICs1lKbwinsgZQxMsml25O6ZF0x772b1kyiL4IsKwaS9/BQQCWCDA6vcMX
+
+4cKx67EYtDqopRfMUf9Ne3MAOpsfp17U/yeznDemjuxL5Q+zI1Qbq3Kx1kpFcLXKlSNz258EPF/u
+
+/9sOLME3EVp/9n+MjvgHJsTXvlMahF6Ci1UF+clZgMLjEhDHaLghiaagt7t8tqVnAgMBAAGjZjBk
+
+MBEGCWCGSAGG+EIBAQQEAwIF4DAOBgNVHQ8BAf8EBAMCBPAwHwYDVR0jBBgwFoAUyhkdEo5upDhd
+
+QtQxDgjb2Y0XDV0wHgYDVR0RBBcwFYETTUtSb2RyaWd1ZXpAbGJsLmdvdjANBgkqhkiG9w0BAQUF
+
+AAOCAQEAgRZkSHe4Gn9djOBlkn+5iGL5fiWb9LbZDeomS9OzfFePAP9G/8ihl+RLBZXgSdLXZm9v
+
+d6Ep+yVD4YHs0cZzaFlPnPxv6h6yWva+nEsTKkbm70yJrv1nsWP1k+nuBY6U6OQsa6um6Z1OCU6H
+
+u6uPSlyuedV93Vf77THU/1nv6Awf9pFhKolQVlmtQ4zfS9M4WNlNIAZYGgldaFjHVYQYee07Mb4S
+
+Y5EIGUQ6XiabX5C1xbynxniNTL5p4beW/dPZ6w7znHxHpJScoqELAVg2WbQhlcKQaKZPOO1fHy0/
+
+VM907Q1v541/FAhO1+5sTEYf1JPhsNYvNXMw+Z9ukb1PSzCCArowggGioAMCAQICBFSZyLUwDQYJ
+
+KoZIhvcNAQEEBQAwXzETMBEGCgmSJomT8ixkARkWA29yZzEYMBYGCgmSJomT8ixkARkWCGRvZWdy
+
+aWRzMQ8wDQYDVQQLEwZQZW9wbGUxHTAbBgNVBAMTFE1hdHQgUm9kcmlndWV6IDg5MzMwMB4XDTA2
+
+MDMyMjE4NTQwOFoXDTA2MDMyMzA2NTkwOFowdDETMBEGCgmSJomT8ixkARkWA29yZzEYMBYGCgmS
+
+JomT8ixkARkWCGRvZWdyaWRzMQ8wDQYDVQQLEwZQZW9wbGUxHTAbBgNVBAMTFE1hdHQgUm9kcmln
+
+dWV6IDg5MzMwMRMwEQYDVQQDEwoxNDE5MzY0NTMzMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMpt
+
+7hUlms1nmxRfeSlJQP7omyLujGCKkTTU0CAG2r40eKOqHNtCeFYCXT5/oCMrgB7YyEmxuUz57bJP
+
+sGPyHnsCAwEAAaMxMC8wDgYDVR0PAQH/BAQDAgSwMB0GCCsGAQUFBwEOAQH/BA4wDDAKBggrBgEF
+
+BQcVATANBgkqhkiG9w0BAQQFAAOCAQEALEPirNkcuhZB4/nouueISh/x+tD3GAgvAEERsVdJyWrF
+
+EceT9v0xN2FI00sk2U5yi5wCOhyCZfwN79/dVo0CGB8OqpG5rJ4GnhJ/eea8h98ZVqR0oRWb7IcG
+
+FhqU1ja930dCZGpoaBKjy39HHgzQTFuvwXjaWyoV6C7sAE1Aw3PSafMGaHxjJoK386KpolVxZbrq
+
+DpeKZoxPZKBC7+hyv4vO7KG6s9G/tmIkTroMKEtHHz7NhZHkv+h1aO8g8p57j9uZ8EvdUWUcnwiS
+
+EWXM9AMmho4Z5rex2cdE/s3d+Wa7IFhYoo61VW6v4amSHQH/o4Vdt0pN4hh+/9y32lp89g==</wsse:BinarySecurityToken><ds:Signature
+xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:SignedInfo>
+ <ds:CanonicalizationMethod
+Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ <ds:SignatureMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+ <ds:Reference URI="#id-25484440">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>xqoPUGjk97yY+StAheOFmeaHgbw=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-28219008">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>VJvTr+Mx3TeWsQY6iwGbhAJ9/eA=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-18539969">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>W1PrEK32GMCbF6FTEmlYiYwqAeQ=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-14816181">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>lWhqYlKqBnB4LwkRWyXMwHy18hc=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-8120088">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>2Zjgz4McHaxMLfpBbqelAqWvRsU=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-8450175">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>knsi7QmfOvjrn5mWClmsbCpZ32A=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-19744521">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>/yBXQ0yxPqpgSGYym/DA08k0dXM=</ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue>
+
+LuGNfoVBzUIoF0AU0lzJkH9kAOi+PQVG8hMrCIEjWh1lifSG/bquhu/qZVq78x3UR+tGK411hWuQ
+ nGle1GvY0A==
+ </ds:SignatureValue>
+ <ds:KeyInfo Id="KeyId-17834932">
+ <wsse:SecurityTokenReference wsu:Id="STRId-9973812"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsse:Reference
+URI="#CertId-1851922"
+ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"/></wsse:SecurityTokenReference>
+ </ds:KeyInfo>
+ </ds:Signature>
+ <wsu:Timestamp wsu:Id="id-25484440"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsu:Created>2006-03-23T00:11:14Z</wsu:Created><wsu:Expires>2006-03-23T00:16:14Z</wsu:Expires></wsu:Timestamp></wsse:Security><wsa:MessageID
+wsu:Id="id-8120088" soapenv:mustUnderstand="0"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">uuid:8aec8160-ba01-11da-bc99-cbed73daa755</wsa:MessageID><wsa:To
+wsu:Id="id-18539969" soapenv:mustUnderstand="0"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:To><wsa:Action
+wsu:Id="id-19744521" soapenv:mustUnderstand="0"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">http://www.globus.org/08/2004/delegationService/DelegationFactoryPortType/RequestSecurityTokenResponse</wsa:Action><wsa:From
+wsu:Id="id-14816181" soapenv:mustUnderstand="0"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsa:Address>http://bosshog.lbl.gov:8888/wsrf/services/DelegationFactoryService</wsa:Address></wsa:From><wsa:RelatesTo
+RelationshipType="wsa:Reply" wsu:Id="id-8450175"
+soapenv:mustUnderstand="0"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">uuid:1143072675.25</wsa:RelatesTo></soapenv:Header><soapenv:Body
+wsu:Id="id-28219008"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><RequestSecurityTokenResponse
+xmlns="http://schemas.xmlsoap.org/ws/2004/04/trust"><wsa:EndpointReference
+xmlns:ns1="http://www.globus.org/08/2004/delegationService"><wsa:Address>http://131.243.2.147:8888/wsrf/services/DelegationService</wsa:Address><wsa:ReferenceProperties><ns1:DelegationKey>8adaa710-ba01-11da-bc99-cbed73daa755</ns1:DelegationKey></wsa:ReferenceProperties></wsa:EndpointReference></RequestSecurityTokenResponse></soapenv:Body></soapenv:Envelope>"""
+
+
+
+CORRECT = """<ns1:MessageID xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10102">uuid:1143760705.98</ns1:MessageID>"""
+
+WRONG = """<ns1:MessageID xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" ns2:Id="10102">uuid:1143760705.98</ns1:MessageID>"""
+
+XML_INST4 = """<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"><SOAP-ENV:Header xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/03/addressing"><ns1:MessageID xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10102">uuid:1143760705.98</ns1:MessageID><ns1:Action xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10103">http://counter.com/CounterPortType/createCounterRequest</ns1:Action><ns1:To xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10104">http://131.243.2.159:9080/wsrf/services/SecureCounterService</ns1:To><ns1:From xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10105"><ns1:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</ns1:Address></ns1:From><ns2:Security xmlns:ns3="http://www.w3.org/2000/09/xmldsig#" xmlns:ns4="http://schemas.xmlsoap.org/ws/2004/04/sc"><ns3:Signature><ns3:SignedInfo xsi:type="ns3:SignedInfoType"><ns3:CanonicalizationMethod xsi:type="ns3:CanonicalizationMethodType" Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ns3:CanonicalizationMethod><ns3:SignatureMethod xsi:type="ns3:SignatureMethodType" Algorithm="http://www.globus.org/2002/04/xmlenc#gssapi-sign"></ns3:SignatureMethod><ns3:Reference xsi:type="ns3:ReferenceType" URI="#10102"><ns3:DigestMethod xsi:type="ns3:DigestMethodType" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns3:DigestMethod><ns3:DigestValue xsi:type="ns3:DigestValueType">
+ IkMyI4zCDlK41qE7sZxvkFHJioU=
+ </ns3:DigestValue></ns3:Reference><ns3:Reference xsi:type="ns3:ReferenceType" URI="#10103"><ns3:DigestMethod xsi:type="ns3:DigestMethodType" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns3:DigestMethod><ns3:DigestValue xsi:type="ns3:DigestValueType">
+ DyEF6Pa7w3SSEVJ98LIoX2LW85k=
+ </ns3:DigestValue></ns3:Reference><ns3:Reference xsi:type="ns3:ReferenceType" URI="#10104"><ns3:DigestMethod xsi:type="ns3:DigestMethodType" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns3:DigestMethod><ns3:DigestValue xsi:type="ns3:DigestValueType">
+ p/2PhmYP+/1UPcpwsRcdlvLmOAg=
+ </ns3:DigestValue></ns3:Reference><ns3:Reference xsi:type="ns3:ReferenceType" URI="#10105"><ns3:DigestMethod xsi:type="ns3:DigestMethodType" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns3:DigestMethod><ns3:DigestValue xsi:type="ns3:DigestValueType">
+ KFLeYjf5ohGUIoPoZV/oew9SuUM=
+ </ns3:DigestValue></ns3:Reference><ns3:Reference xsi:type="ns3:ReferenceType" URI="#10106"><ns3:DigestMethod xsi:type="ns3:DigestMethodType" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns3:DigestMethod><ns3:DigestValue xsi:type="ns3:DigestValueType">
+ 7Gg0SC1wltHVAwiOfdgZsGM9W5g=
+ </ns3:DigestValue></ns3:Reference></ns3:SignedInfo><ns3:SignatureValue xsi:type="ns3:SignatureValueType">
+ AAAAAAAAAAEAAAdrBxzrHLZG4NglRglL9F3rKQu0658=
+ </ns3:SignatureValue><ns3:KeyInfo xsi:type="ns3:KeyInfoType"><ns2:SecurityTokenReference><ns2:Reference URI="#CertId-10107" ValueType="http://www.globus.org/ws/2004/09/security/sc#GSSAPI_CONTEXT_TOKEN"></ns2:Reference></ns2:SecurityTokenReference></ns3:KeyInfo></ns3:Signature><ns4:SecurityContextToken xmlns:ns5="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns5:Id="CertId-10107"><ns4:Identifier xsi:type="xsd:anyURI">1000</ns4:Identifier></ns4:SecurityContextToken></ns2:Security></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="http://counter.com"><ns1:createCounter xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10106"></ns1:createCounter></SOAP-ENV:Body></SOAP-ENV:Envelope>"""
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+if __name__ == '__main__':
+ main()
+
diff --git a/ZSI/zsi/test/test_zsi.py b/ZSI/zsi/test/test_zsi.py
new file mode 100644
index 0000000..c15725c
--- /dev/null
+++ b/ZSI/zsi/test/test_zsi.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+import unittest
+import test_t1
+import test_t2
+import test_t3
+import test_t5
+import test_t6
+import test_t7
+import test_t8
+import test_t9
+
+def makeTestSuite():
+ suite1 = test_t1.makeTestSuite()
+ suite2 = test_t2.makeTestSuite()
+ suite3 = test_t3.makeTestSuite()
+ suite5 = test_t5.makeTestSuite()
+ suite6 = test_t6.makeTestSuite()
+ suite7 = test_t7.makeTestSuite()
+ suite8 = test_t8.makeTestSuite()
+ suite9 = test_t9.makeTestSuite()
+ t = (suite1, suite2, suite3, suite5, suite6, suite7, suite8, suite9)
+ suite = unittest.TestSuite(t)
+ return suite
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+ suite = unittest.TestSuite()
+
+if __name__ == "__main__" : main()
diff --git a/ZSI/zsi/test/test_zsi_net.py b/ZSI/zsi/test/test_zsi_net.py
new file mode 100644
index 0000000..65aed78
--- /dev/null
+++ b/ZSI/zsi/test/test_zsi_net.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+import unittest
+import test_t1
+import test_t2
+import test_t3
+import test_t4
+import test_t5
+import test_t6
+import test_t7
+import test_t8
+import test_t9
+
+def makeTestSuite():
+ suite1 = test_t1.makeTestSuite()
+ suite2 = test_t2.makeTestSuite()
+ suite3 = test_t3.makeTestSuite()
+ suite4 = test_t4.makeTestSuite()
+ suite5 = test_t5.makeTestSuite()
+ suite6 = test_t6.makeTestSuite()
+ suite7 = test_t7.makeTestSuite()
+ suite8 = test_t8.makeTestSuite()
+ suite9 = test_t9.makeTestSuite()
+ t = (suite1, suite2, suite3, suite4, suite5, suite6, suite7, suite8, suite9)
+ suite = unittest.TestSuite(t)
+ return suite
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+ suite = unittest.TestSuite()
+
+if __name__ == "__main__" : main()
diff --git a/ZSI/zsi/test/tests_bad.py b/ZSI/zsi/test/tests_bad.py
new file mode 100644
index 0000000..4c50c80
--- /dev/null
+++ b/ZSI/zsi/test/tests_bad.py
@@ -0,0 +1,126 @@
+test01 = '''<SOAP-ENV:Envelope foo='bar'
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body>
+ <m:GetLastTradePrice xmlns:m="Some-URI">
+ <symbol>DIS</symbol>
+ </m:GetLastTradePrice>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test02 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+<SOAP-ENV:Header>
+ <t:Transaction xmlns:t="some-URI" SOAP-ENV:mustUnderstand="1">
+ 5
+ </t:Transaction>
+</SOAP-ENV:Header>
+<SOAP-ENV:Body/>
+</SOAP-ENV:Envelope>'''
+
+test03 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+ <SOAP-ENV:Body>
+ <SOAP-ENV:Fault>
+ <faultcode>SOAP-ENV:MustUnderstand</faultcode>
+ <faultstring>SOAP Must Understand Error</faultstring>
+ <?MYPI spenser?>
+ </SOAP-ENV:Fault>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test04 = '''<SOAP-ENV:Envelope fooattr='bar'
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+ <SOAP-ENV:Body>
+ <SOAP-ENV:Fault>
+ <faultcode>SOAP-ENV:Server</faultcode>
+ <faultstring>Server Error</faultstring>
+ <detail>
+ <e:myfaultdetails xmlns:e="Some-URI">
+ <message>
+ My application didn't work
+ </message>
+ <errorcode>
+ 1001
+ </errorcode>
+ </e:myfaultdetails>
+ </detail>
+ </SOAP-ENV:Fault>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test05 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test06 = '''<SOAP-ENV:ChemicalX
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+</SOAP-ENV:ChemicalX>'''
+
+test07 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+</SOAP-ENV:Envelope>'''
+
+test08 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:zBody></SOAP-ENV:zBody>
+</SOAP-ENV:Envelope>'''
+
+test09 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test10 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+</SOAP-ENV:Envelope>'''
+
+test11 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <m:data xmlns:m="data-URI">
+ <symbol>DEF</symbol>
+ </m:data>
+</SOAP-ENV:Envelope>'''
+
+test12 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <m:data xmlns:m="data-URI">
+ <?PIE?>
+ <symbol>DEF</symbol>
+ </m:data>
+</SOAP-ENV:Envelope>'''
+
+test13 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <?xoo?>
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <m:data xmlns:m="data-URI">
+ <symbol>DEF</symbol>
+ </m:data>
+</SOAP-ENV:Envelope>'''
+
diff --git a/ZSI/zsi/test/tests_good.py b/ZSI/zsi/test/tests_good.py
new file mode 100644
index 0000000..eca6112
--- /dev/null
+++ b/ZSI/zsi/test/tests_good.py
@@ -0,0 +1,88 @@
+test01 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <!-- foo foo-pi -->
+ <SOAP-ENV:Body>
+ <m:GetLastTradePriceResponse xmlns:m="Some-URI">
+ <Price>34.5</Price>
+ </m:GetLastTradePriceResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test02 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header>
+ <t:Transaction
+ actor="foobar"
+ xmlns:t="some-URI"
+ SOAP-ENV:mustUnderstand="1">
+ 5
+ </t:Transaction>
+ </SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <m:GetLastTradePrice xmlns:m="Some-URI">
+ <symbol>DEF</symbol>
+ </m:GetLastTradePrice>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test03 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body>
+ <m:GetLastTradePriceDetailed
+ xmlns:m="Some-URI">
+ <Symbol>DEF</Symbol>
+ <Company>DEF Corp</Company>
+ <Price>34.1</Price>
+ </m:GetLastTradePriceDetailed>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test04 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:xsi='xmlschemainstance'>
+ <SOAP-ENV:Header>
+ <t:Transaction xmlns:t="some-URI" xsi:type="xsd:int" mustUnderstand="1">
+ 5
+ <nested mustUndertand="1"/>
+ </t:Transaction>
+ </SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <m:GetLastTradePriceResponse xmlns:m="Some-URI">
+ <m:Price>34.5</m:Price>
+ </m:GetLastTradePriceResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test05 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body>
+ <m:GetLastTradePriceResponse
+ xmlns:m="Some-URI">
+ <PriceAndVolume>
+ <LastTradePrice>
+ 34.5
+ </LastTradePrice>
+ <DayVolume>
+ 10000
+ </DayVolume>
+ </PriceAndVolume>
+ </m:GetLastTradePriceResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test06 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body>
+ <foo/>
+ <m:GetLastTradePriceResponse xmlns:m="Some-URI">
+ <Price>34.5</Price>
+ </m:GetLastTradePriceResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
diff --git a/ZSI/zsi/test/wsdl2py/ServiceTest.py b/ZSI/zsi/test/wsdl2py/ServiceTest.py
new file mode 100644
index 0000000..6ac6c13
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/ServiceTest.py
@@ -0,0 +1,418 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+from compiler.ast import Module
+import StringIO, copy, getopt
+import os, sys, unittest, urlparse, signal, time, warnings, subprocess
+from ConfigParser import ConfigParser, NoSectionError, NoOptionError
+from ZSI.wstools.TimeoutSocket import TimeoutError
+
+"""Global Variables:
+ CONFIG_FILE -- configuration file
+ CONFIG_PARSER -- ConfigParser instance
+ DOCUMENT -- test section variable, specifying document style.
+ LITERAL -- test section variable, specifying literal encodings.
+ BROKE -- test section variable, specifying broken test.
+ TESTS -- test section variable, whitespace separated list of modules.
+ SECTION_CONFIGURATION -- configuration section, turn on/off debuggging.
+ TRACEFILE -- file class instance.
+ TOPDIR -- current working directory
+ MODULEDIR -- stubs directory
+ PORT -- port of local container
+ HOST -- address of local container
+ SECTION_SERVERS -- services to be tested, values are paths to executables.
+"""
+CONFIG_FILE = 'config.txt'
+CONFIG_PARSER = ConfigParser()
+DOCUMENT = 'document'
+LITERAL = 'literal'
+BROKE = 'broke'
+TESTS = 'tests'
+SECTION_CONFIGURATION = 'configuration'
+SECTION_DISPATCH = 'dispatch'
+TRACEFILE = sys.stdout
+TOPDIR = os.getcwd()
+MODULEDIR = TOPDIR + '/stubs'
+SECTION_SERVERS = 'servers'
+
+CONFIG_PARSER.read(CONFIG_FILE)
+
+DEBUG = CONFIG_PARSER.getboolean(SECTION_CONFIGURATION, 'debug')
+SKIP = CONFIG_PARSER.getboolean(SECTION_CONFIGURATION, 'skip')
+TWISTED = CONFIG_PARSER.getboolean(SECTION_CONFIGURATION, 'twisted')
+LAZY = CONFIG_PARSER.getboolean(SECTION_CONFIGURATION, 'lazy')
+OUTPUT = CONFIG_PARSER.get(SECTION_CONFIGURATION, 'output') or sys.stdout
+
+if DEBUG:
+ from ZSI.wstools.logging import setBasicLoggerDEBUG
+ setBasicLoggerDEBUG()
+
+sys.path.append('%s/%s' %(os.getcwd(), 'stubs'))
+ENVIRON = copy.copy(os.environ)
+ENVIRON['PYTHONPATH'] = ENVIRON.get('PYTHONPATH', '') + ':' + MODULEDIR
+
+
+def _SimpleMain():
+ """Gets tests to run from configuration file.
+ """
+ unittest.TestProgram(defaultTest="all")
+main = _SimpleMain
+
+
+def _TwistedMain():
+ """Gets tests to run from configuration file.
+ """
+ from twisted.internet import reactor
+ reactor.callWhenRunning(_TwistedTestProgram, defaultTest="all")
+ reactor.run(installSignalHandlers=0)
+if TWISTED: main = _TwistedMain
+
+
+def _LaunchContainer(cmd):
+ '''
+ Parameters:
+ cmd -- executable, sets up a ServiceContainer or ?
+ '''
+ host = CONFIG_PARSER.get(SECTION_DISPATCH, 'host')
+ port = CONFIG_PARSER.get(SECTION_DISPATCH, 'port')
+ process = subprocess.Popen([cmd, port], env=ENVIRON)
+ time.sleep(1)
+ return process
+
+
+class _TwistedTestProgram(unittest.TestProgram):
+
+ def runTests(self):
+ from twisted.internet import reactor
+ if self.testRunner is None:
+ self.testRunner = unittest.TextTestRunner(verbosity=self.verbosity)
+
+ result = self.testRunner.run(self.test)
+ reactor.stop()
+ return result.wasSuccessful()
+
+
+
+class ConfigException(Exception):
+ """Exception thrown when configuration settings arent correct.
+ """
+ pass
+
+class TestException(Exception):
+ """Exception thrown when test case isn't correctly set up.
+ """
+ pass
+
+
+class ServiceTestCase(unittest.TestCase):
+ """Conventions for method names:
+ test_net*
+ -- network tests
+
+ test_local*
+ -- local tests
+
+ test_dispatch*
+ -- tests that use the a spawned local container
+
+ class attributes: Edit/Override these in the inheriting class as needed
+ out -- file descriptor to write output to
+ name -- configuration item, must be set in class.
+ url_section -- configuration section, maps a test module
+ name to an URL.
+ client_file_name --
+ types_file_name --
+ server_file_name --
+ """
+ out = OUTPUT
+ name = None
+ url_section = 'WSDL'
+ client_file_name = None
+ types_file_name = None
+ server_file_name = None
+
+ def __init__(self, methodName):
+ """
+ parameters:
+ methodName --
+ instance variables:
+ client_module
+ types_module
+ server_module
+ processID
+ done
+
+ """
+ self.methodName = methodName
+ self.url = None
+ self.wsdl2py_args = []
+ self.wsdl2dispatch_args = []
+ self.portkwargs = {}
+ self.client_module = self.types_module = self.server_module = None
+ self.done = False
+
+ if TWISTED:
+ self.wsdl2py_args.append('--twisted')
+
+ if LAZY:
+ self.wsdl2py_args.append('--lazy')
+
+ unittest.TestCase.__init__(self, methodName)
+
+ write = lambda self, arg: self.out.write(arg)
+
+ if sys.version_info[:2] >= (2,5):
+ _exc_info = unittest.TestCase._exc_info
+ else:
+ _exc_info = unittest.TestCase._TestCase__exc_info
+
+ def __call__(self, *args, **kwds):
+ self.run(*args, **kwds)
+
+ def run(self, result=None):
+ if result is None: result = self.defaultTestResult()
+ result.startTest(self)
+ testMethod = getattr(self, self.methodName)
+ try:
+ try:
+ self.setUp()
+ except KeyboardInterrupt:
+ raise
+ except:
+ result.addError(self, self._exc_info())
+ return
+
+ ok = False
+ try:
+ t1 = time.time()
+ pyobj = testMethod()
+ t2 = time.time()
+ ok = True
+ except self.failureException:
+ result.addFailure(self, self._exc_info())
+ except KeyboardInterrupt:
+ raise
+ except:
+ result.addError(self, self._exc_info())
+
+ try:
+ self.tearDown()
+ except KeyboardInterrupt:
+ raise
+ except:
+ result.addError(self, self._exc_info())
+ ok = False
+ if ok:
+ result.addSuccess(self)
+ print>>self
+ print>>self, "|"+"-"*60
+ print>>self, "| TestCase: %s" %self.methodName
+ print>>self, "|"+"-"*20
+ print>>self, "| run time: %s ms" %((t2-t1)*1000)
+ print>>self, "| return : %s" %pyobj
+ print>>self, "|"+"-"*60
+
+ finally:
+ result.stopTest(self)
+
+
+
+
+ def getPortKWArgs(self):
+ kw = {}
+ if CONFIG_PARSER.getboolean(SECTION_CONFIGURATION, 'tracefile'):
+ kw['tracefile'] = TRACEFILE
+
+ kw.update(self.portkwargs)
+ return kw
+
+ def _setUpDispatch(self):
+ """Set this test up as a dispatch test.
+ url --
+ """
+ host = CONFIG_PARSER.get(SECTION_DISPATCH, 'host')
+ port = CONFIG_PARSER.get(SECTION_DISPATCH, 'port')
+ path = CONFIG_PARSER.get(SECTION_DISPATCH, 'path')
+
+ scheme = 'http'
+ netloc = '%s:%s' %(host, port)
+ params = query = fragment = None
+
+ self.portkwargs['url'] = \
+ urlparse.urlunparse((scheme,netloc,path,params,query,fragment))
+
+ _wsdl = {}
+ def _generate(self):
+ """call the wsdl2py and wsdl2dispatch scripts and
+ automatically add the "-f" or "-u" argument. Other args
+ can be appended via the "wsdl2py_args" and "wsdl2dispatch_args"
+ instance attributes.
+ """
+ url = self.url
+ if SKIP:
+ ServiceTestCase._wsdl[url] = True
+ return
+
+ args = []
+ ServiceTestCase._wsdl[url] = False
+ if os.path.isfile(url):
+ args += ['-f', os.path.abspath(url)]
+ else:
+ args += ['-u', url]
+
+ try:
+ os.mkdir(MODULEDIR)
+ except OSError, ex:
+ pass
+
+ os.chdir(MODULEDIR)
+ if MODULEDIR not in sys.path:
+ sys.path.append(MODULEDIR)
+
+ try:
+ # Client Stubs
+ wsdl2py = ['wsdl2py'] + args + self.wsdl2py_args
+ try:
+ exit = subprocess.call(wsdl2py)
+ except OSError, ex:
+ warnings.warn("TODO: Not sure what is going on here?")
+ exit = -1
+
+ #TODO: returncode WINDOWS?
+ self.failUnless(os.WIFEXITED(exit),
+ '"%s" exited with signal#: %d' %(wsdl2py, exit))
+ self.failUnless(exit == 0,
+ '"%s" exited with exit status: %d' %(wsdl2py, exit))
+
+ # Service Stubs
+ if '-x' not in self.wsdl2py_args:
+ wsdl2dispatch = (['wsdl2dispatch'] + args +
+ self.wsdl2dispatch_args)
+ try:
+ exit = subprocess.call(wsdl2dispatch)
+ except OSError, ex:
+ warnings.warn("TODO: Not sure what is going on here?")
+
+ #TODO: returncode WINDOWS?
+ self.failUnless(os.WIFEXITED(exit),
+ '"%s" exited with signal#: %d' %(wsdl2dispatch, exit))
+ self.failUnless(exit == 0,
+ '"%s" exited with exit status: %d' %(wsdl2dispatch, exit))
+
+ ServiceTestCase._wsdl[url] = True
+
+ finally:
+ os.chdir(TOPDIR)
+
+ _process = None
+ _lastToDispatch = None
+ def setUp(self):
+ """Generate types and services modules once, then make them
+ available thru the *_module attributes if the *_file_name
+ attributes were specified.
+ """
+ section = self.url_section
+ name = self.name
+ if not section or not name:
+ raise TestException, 'section(%s) or name(%s) not defined' %(
+ section, name)
+
+ if not CONFIG_PARSER.has_section(section):
+ raise TestException,\
+ 'No such section(%s) in configuration file(%s)' %(
+ self.url_section, CONFIG_FILE)
+
+ self.url = CONFIG_PARSER.get(section, name)
+
+ status = ServiceTestCase._wsdl.get(self.url)
+ if status is False:
+ self.fail('generation failed for "%s"' %self.url)
+
+ if status is None:
+ self._generate()
+
+ # Check for files
+ tfn = self.types_file_name
+ cfn = self.client_file_name
+ sfn = self.server_file_name
+
+ files = filter(lambda f: f is not None, [cfn, tfn,sfn])
+ if None is cfn is tfn is sfn:
+ return
+
+ for n,m in map(lambda i: (i,__import__(i.split('.py')[0])), files):
+ if tfn is not None and tfn == n:
+ self.types_module = m
+ elif cfn is not None and cfn == n:
+ self.client_module = m
+ elif sfn is not None and sfn == n:
+ self.server_module = m
+ else:
+ self.fail('Unexpected module %s' %n)
+
+ # DISPATCH PORTION OF SETUP
+ if not self.methodName.startswith('test_dispatch'):
+ return
+
+ self._setUpDispatch()
+ if ServiceTestCase._process is not None:
+ return
+
+ try:
+ expath = CONFIG_PARSER.get(SECTION_DISPATCH, name)
+ except (NoSectionError, NoOptionError), ex:
+ self.fail('section dispatch has no item "%s"' %name)
+
+ if ServiceTestCase._lastToDispatch == expath:
+ return
+
+ if ServiceTestCase._lastToDispatch is not None:
+ ServiceTestCase.CleanUp()
+
+ ServiceTestCase._lastToDispatch = expath
+ ServiceTestCase._process = _LaunchContainer(TOPDIR + '/' + expath)
+
+ def CleanUp(cls):
+ """call this when dispatch server is no longer needed,
+ maybe another needs to be started. Assumption that
+ a single "Suite" uses the same server, once all the
+ tests are run in that suite do a cleanup.
+ """
+ if cls._process is None:
+ return
+ os.kill(cls._process.pid, signal.SIGKILL)
+ cls._process = None
+ CleanUp = classmethod(CleanUp)
+
+
+class ServiceTestSuite(unittest.TestSuite):
+ """A test suite is a composite test consisting of a number of TestCases.
+
+ For use, create an instance of TestSuite, then add test case instances.
+ When all tests have been added, the suite can be passed to a test
+ runner, such as TextTestRunner. It will run the individual test cases
+ in the order in which they were added, aggregating the results. When
+ subclassing, do not forget to call the base class constructor.
+ """
+ def __init__(self, tests=()):
+ unittest.TestSuite.__init__(self, tests)
+
+ def __call__(self, result):
+ # for python2.4
+ return self.run(result)
+
+ def addTest(self, test):
+ unittest.TestSuite.addTest(self, test)
+
+ def run(self, result):
+ for test in self._tests:
+ if result.shouldStop:
+ break
+ test(result)
+
+ ServiceTestCase.CleanUp()
+ return result
+
+
diff --git a/ZSI/zsi/test/wsdl2py/runTests.py b/ZSI/zsi/test/wsdl2py/runTests.py
new file mode 100755
index 0000000..6cb68af
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/runTests.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+###########################################################################
+import unittest, warnings
+from ServiceTest import main, CONFIG_PARSER, DOCUMENT, LITERAL, BROKE, TESTS
+
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ return _dispatchTestSuite(broke=False)
+
+def local():
+ """Run all local tests"""
+ return _localTestSuite(broke=False)
+
+def net():
+ """Run all network tests"""
+ return _netTestSuite(broke=False)
+
+def all():
+ """Run all tests"""
+ return _allTestSuite(broke=False)
+
+
+# Specialized binding targets
+def docLitTestSuite():
+ """Run all doc/lit network tests"""
+ return _netTestSuite(broke=False, document=True, literal=True)
+
+def rpcLitTestSuite():
+ """Run all rpc/lit network tests"""
+ return _netTestSuite(broke=False, document=False, literal=True)
+
+def rpcEncTestSuite():
+ """Run all rpc/enc network tests"""
+ return _netTestSuite(broke=False, document=False, literal=False)
+
+
+# Low level functions
+def _allTestSuite(document=None, literal=None, broke=None):
+ return _makeTestSuite('all', document, literal, broke)
+
+def _netTestSuite(document=None, literal=None, broke=None):
+ return _makeTestSuite('net', document, literal, broke)
+
+def _localTestSuite(document=None, literal=None, broke=None):
+ return _makeTestSuite('local', document, literal, broke)
+
+def _dispatchTestSuite(document=None, literal=None, broke=None):
+ return _makeTestSuite('dispatch', document, literal, broke)
+
+
+def _makeTestSuite(test, document=None, literal=None, broke=None):
+ """Return a test suite containing all test cases that satisfy
+ the parameters. None means don't check.
+
+ Parameters:
+ test -- "net" run network tests, "local" run local tests,
+ "dispatch" run dispatch tests, "all" run all tests.
+ document -- None, True, False
+ literal -- None, True, False
+ broke -- None, True, False
+ """
+ assert test in ['net', 'local', 'dispatch', 'all'],(
+ 'test must be net, local, dispatch, or all')
+
+ cp = CONFIG_PARSER
+ testSections = []
+ sections = [\
+ 'rpc_encoded' , 'rpc_encoded_broke',
+ 'rpc_literal', 'rpc_literal_broke', 'rpc_literal_broke_interop',
+ 'doc_literal', 'doc_literal_broke', 'doc_literal_broke_interop',
+ ]
+ boo = cp.getboolean
+ for s,d,l,b in map(\
+ lambda sec: \
+ (sec, (None,boo(sec,DOCUMENT)), (None,boo(sec,LITERAL)), (None,boo(sec,BROKE))), sections):
+ if document in d and literal in l and broke in b:
+ testSections.append(s)
+
+ suite = unittest.TestSuite()
+ for section in testSections:
+ moduleList = cp.get(section, TESTS).split()
+ for module in map(__import__, moduleList):
+ def _warn_empty():
+ warnings.warn('"%s" has no test "%s"' %(module, test))
+ return unittest.TestSuite()
+
+ s = getattr(module, test, _warn_empty)()
+ suite.addTest(s)
+ return suite
+
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/ZSI/zsi/test/wsdl2py/servers/EchoServer.py b/ZSI/zsi/test/wsdl2py/servers/EchoServer.py
new file mode 100755
index 0000000..75eb971
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/servers/EchoServer.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys
+from ZSI.ServiceContainer import AsServer
+from EchoServer_services_server import EchoServer
+
+"""
+EchoServer example service
+
+WSDL: ../../samples/Echo/Echo.wsdl
+
+"""
+
+
+class Service(EchoServer):
+ def soap_Echo(self, ps):
+ response = EchoServer.soap_Echo(self, ps)
+ response.EchoResult = self.request.EchoIn
+ return response
+
+
+if __name__ == "__main__" :
+ port = int(sys.argv[1])
+ AsServer(port, (Service('test'),))
diff --git a/ZSI/zsi/test/wsdl2py/servers/WhiteMesa.py b/ZSI/zsi/test/wsdl2py/servers/WhiteMesa.py
new file mode 100755
index 0000000..73858d9
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/servers/WhiteMesa.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys
+from ZSI.ServiceContainer import AsServer
+from RPC_Literal_TestDefinitions_services_server import WhiteMesaSoapRpcLitTestSvc as WhiteMesa
+"""
+WhiteMesa web service for rpc/literal tests.
+
+WSDL: http://www.whitemesa.net/wsdl/test-rpc-lit.wsdl
+
+"""
+
+class Service(WhiteMesa):
+ def soap_echoStruct(self, ps):
+ response = WhiteMesa.soap_echoStruct(self, ps)
+ return response
+ def soap_echoStructArray(self, ps):
+ response = WhiteMesa.soap_echoStructArray(self, ps)
+ return response
+ def soap_echoStructAsSimpleTypes(self, ps):
+ response = WhiteMesa.soap_echoStructAsSimpleTypes(self, ps)
+ return response
+ def soap_echoSimpleTypesAsStruct(self, ps):
+ response = WhiteMesa.soap_echoSimpleTypesAsStruct(self, ps)
+ return response
+ def soap_echoNestedStruct(self, ps):
+ response = WhiteMesa.soap_echoNestedStruct(self, ps)
+ return response
+ def soap_echoNestedArray(self, ps):
+ response = WhiteMesa.soap_echoNestedArray(self, ps)
+ return response
+ def soap_echoStringArray(self, ps):
+ response = WhiteMesa.soap_echoStringArray(self, ps)
+ return response
+ def soap_echoIntegerArray(self, ps):
+ response = WhiteMesa.soap_echoIntegerArray(self, ps)
+ return response
+ def soap_echoBoolean(self, ps):
+ response = WhiteMesa.soap_echoBoolean(self, ps)
+ response._return = self.request._inputBoolean
+ return response
+ def soap_echoString(self, ps):
+ response = WhiteMesa.soap_echoString(self, ps)
+ return response
+
+if __name__ == "__main__" :
+ port = int(sys.argv[1])
+ AsServer(port, (Service('test'),))
+
+
diff --git a/ZSI/zsi/test/wsdl2py/test_AWSECommerceService.py b/ZSI/zsi/test/wsdl2py/test_AWSECommerceService.py
new file mode 100755
index 0000000..b5708ec
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_AWSECommerceService.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite, TestException
+from ZSI.schema import ElementDeclaration, GED
+from ZSI import ParsedSoap
+
+"""
+Unittest for contacting the Amazon ECommerce Service
+
+WSDL:
+
+"""
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_'))
+ return suite
+
+
+class AmazonTestCase(ServiceTestCase):
+ """Test case for Amazon ECommerce Web service
+ """
+ name = "test_AWSECommerceService"
+ client_file_name = "AWSECommerceService_services.py"
+ types_file_name = "AWSECommerceService_services_types.py"
+ server_file_name = "AWSECommerceService_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+ self.wsdl2py_args.append('--lazy')
+
+ def test_local_bug_1525567(self):
+ element = GED("http://webservices.amazon.com/AWSECommerceService/2006-09-18", 'Items')
+ # Make sure this is a GED
+ self.failUnless(isinstance(element, ElementDeclaration), '"%s" not a GED' %element)
+
+ def test_local_parse_ItemSearch(self):
+ msg = self.client_module.ItemSearchResponseMsg()
+ ps = ParsedSoap(ItemSearchResponseMsg)
+ response = ps.Parse(msg.typecode)
+ response.OperationRequest.Arguments
+ for i in response.OperationRequest.Arguments.Argument:
+ i.get_attribute_Name()
+ i.get_attribute_Value()
+
+ for i in response.OperationRequest.HTTPHeaders.Header or []:
+ i.get_attribute_Name()
+ i.get_attribute_Value()
+
+ response.OperationRequest.RequestId
+ response.OperationRequest.RequestProcessingTime
+ for its in response.Items:
+ self.failUnless(its.TotalResults == 55, '')
+ self.failUnless(its.TotalPages == 6, '')
+ for it in its.Item:
+ it.ASIN;
+ it.Accessories;
+ #it.AlternateVersions;
+ it.BrowseNodes
+ #it.Collections;
+ it.CustomerReviews ;it.DetailPageURL
+ it.EditorialReviews; it.Errors; it.ImageSets; it.ItemAttributes
+ it.LargeImage; it.ListmaniaLists; it.MediumImage; it.MerchantItemAttributes
+ it.OfferSummary; it.Offers;
+ #it.ParentASIN;
+ it.SalesRank; it.SearchInside
+ it.SimilarProducts; it.SmallImage; it.Subjects; it.Tracks;
+
+
+ def test_net_ItemSearch(self):
+ loc = self.client_module.AWSECommerceServiceLocator()
+ port = loc.getAWSECommerceServicePortType(**self.getPortKWArgs())
+
+ msg = self.client_module.ItemSearchRequestMsg()
+ msg.SubscriptionId = '0HP1WHME000749APYWR2'
+ request = msg.new_Request()
+ msg.Request = [request]
+
+ # request
+ request.ItemPage = 1
+ request.SearchIndex = "Books"
+ request.Keywords = 'Tamerlane'
+ request.ResponseGroup = ['Medium',]
+
+ response = port.ItemSearch(msg)
+
+ response.OperationRequest
+ self.failUnless(response.OperationRequest.Errors is None, 'ecommerce site reported errors')
+
+ response.OperationRequest.Arguments
+ for i in response.OperationRequest.Arguments.Argument:
+ i.get_attribute_Name()
+ i.get_attribute_Value()
+
+ for i in response.OperationRequest.HTTPHeaders.Header or []:
+ i.get_attribute_Name()
+ i.get_attribute_Value()
+
+ response.OperationRequest.RequestId
+ response.OperationRequest.RequestProcessingTime
+ for its in response.Items:
+ for it in its.Item:
+ it.ASIN;
+ it.Accessories;
+ #it.AlternateVersions;
+ it.BrowseNodes
+ #it.Collections;
+ it.CustomerReviews ;it.DetailPageURL
+ it.EditorialReviews; it.Errors; it.ImageSets; it.ItemAttributes
+ it.LargeImage; it.ListmaniaLists; it.MediumImage; it.MerchantItemAttributes
+ it.OfferSummary; it.Offers;
+ #it.ParentASIN;
+ it.SalesRank; it.SearchInside
+ it.SimilarProducts; it.SmallImage; it.Subjects; it.Tracks;
+ it.VariationSummary; it.Variations
+
+
+ItemSearchResponseMsg="""<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SOAP-ENV:Body><ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2006-09-18"><OperationRequest><HTTPHeaders><Header Name="UserAgent"></Header></HTTPHeaders><RequestId>1C167XDF9BX253MEYAF2</RequestId><Arguments><Argument Name="Service" Value="AWSECommerceService"></Argument></Arguments><RequestProcessingTime>0.987805128097534</RequestProcessingTime></OperationRequest><Items><Request><IsValid>True</IsValid><ItemSearchRequest><ItemPage>1</ItemPage><Keywords>Tamerlane</Keywords><ResponseGroup>Medium</ResponseGroup><SearchIndex>Books</SearchIndex></ItemSearchRequest></Request><TotalResults>55</TotalResults><TotalPages>6</TotalPages><Item><ASIN>030681465X</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=030681465X%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/030681465X%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>135340</SalesRank><SmallImage><URL>http://images.amazon.com/images/P/030681465X.01._SCTHUMBZZZ_V66860320_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">50</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/030681465X.01._SCMZZZZZZZ_V66860320_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">108</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/030681465X.01._SCLZZZZZZZ_V66860320_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">337</Width></LargeImage><ImageSets><ImageSet Category="primary"><SmallImage><URL>http://images.amazon.com/images/P/030681465X.01._SCTHUMBZZZ_V66860320_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">50</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/030681465X.01._SCMZZZZZZZ_V66860320_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">108</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/030681465X.01._SCLZZZZZZZ_V66860320_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">337</Width></LargeImage></ImageSet></ImageSets><ItemAttributes><Author>Justin Marozzi</Author><Binding>Hardcover</Binding><DeweyDecimalNumber>920</DeweyDecimalNumber><EAN>9780306814655</EAN><Edition>New Ed</Edition><ISBN>030681465X</ISBN><Label>Da Capo Press</Label><ListPrice><Amount>2695</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$26.95</FormattedPrice></ListPrice><Manufacturer>Da Capo Press</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>368</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">150</Height><Length Units="hundredths-inches">904</Length><Weight Units="hundredths-pounds">174</Weight><Width Units="hundredths-inches">640</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>2006-02-22</PublicationDate><Publisher>Da Capo Press</Publisher><Studio>Da Capo Press</Studio><Title>Tamerlane: Sword of Islam, Conqueror of the World</Title></ItemAttributes><OfferSummary><LowestNewPrice><Amount>831</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$8.31</FormattedPrice></LowestNewPrice><LowestUsedPrice><Amount>832</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$8.32</FormattedPrice></LowestUsedPrice><TotalNew>43</TotalNew><TotalUsed>26</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary><EditorialReviews><EditorialReview><Source>Book Description</Source><Content>A powerful account of the life of Tamerlane the Great (1336-1405), the last great Mongol conqueror of Central Asia, ruler of a vast empire, and one of history's most brutal tyrants &lt;P&gt; Tamerlane, aka Temur-the Mongol successor to Genghis Khan-ranks with Alexander the Great as one of the world's great conquerors, yet the details of his life are scarcely known in the West. Born in obscurity and poverty, he rose to become a fierce tribal leader, and with that his dominion and power grew with astonishing speed. He blazed through Asia, razing cities to the ground. He tortured conquered inhabitants without mercy, sometimes ordering them buried alive, at other times decapitating them. Over the ruins of conquered Baghdad, Tamerlane had his soldiers erect a pyramid of 90,000 enemy heads. As he and his armies swept through Central Asia, sacking, and then rebuilding cities, Tamerlane gradually imposed an iron rule and a refined culture over a vast territory-from the steppes of Asia to the Syrian coastline. &lt;P&gt; Justin Marozzi traveled in the footsteps of this fearsome emperor of Samarkand (modern-day Uzbekistan) to write this book, which is part history, part travelogue. He carefully follows the path of this infamous and enigmatic conqueror, recounting the history and the story of this cruel, cultivated, and indomitable warrior.</Content></EditorialReview></EditorialReviews></Item><Item><ASIN>1853141046</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=1853141046%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/1853141046%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>366445</SalesRank><ItemAttributes><Author>David Nicolle</Author><Author>Richard Hook</Author><Binding>Hardcover</Binding><DeweyDecimalNumber>950.20922</DeweyDecimalNumber><EAN>9781853141041</EAN><ISBN>1853141046</ISBN><Label>Firebird</Label><ListPrice><Amount>2495</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$24.95</FormattedPrice></ListPrice><Manufacturer>Firebird</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>208</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">1000</Height><Length Units="hundredths-inches">75</Length><Weight Units="hundredths-pounds">160</Weight><Width Units="hundredths-inches">775</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>1990-09</PublicationDate><Publisher>Firebird</Publisher><Studio>Firebird</Studio><Title>The Mongol Warlords: Ghengis Khan, Kublai Khan, Hulegu, Tamerlane (Heroes &amp; Warriors)</Title></ItemAttributes><OfferSummary><LowestUsedPrice><Amount>1095</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$10.95</FormattedPrice></LowestUsedPrice><TotalNew>0</TotalNew><TotalUsed>3</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary></Item><Item><ASIN>0521633842</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0521633842%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/0521633842%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>429712</SalesRank><SmallImage><URL>http://images.amazon.com/images/P/0521633842.01._SCTHUMBZZZ_V1114821525_.jpg</URL><Height Units="pixels">60</Height><Width Units="pixels">39</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/0521633842.01._SCMZZZZZZZ_V1114821525_.jpg</URL><Height Units="pixels">140</Height><Width Units="pixels">90</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/0521633842.01._SCLZZZZZZZ_V1114821525_.jpg</URL><Height Units="pixels">475</Height><Width Units="pixels">306</Width></LargeImage><ImageSets><ImageSet Category="primary"><SmallImage><URL>http://images.amazon.com/images/P/0521633842.01._SCTHUMBZZZ_V1114821525_.jpg</URL><Height Units="pixels">60</Height><Width Units="pixels">39</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/0521633842.01._SCMZZZZZZZ_V1114821525_.jpg</URL><Height Units="pixels">140</Height><Width Units="pixels">90</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/0521633842.01._SCLZZZZZZZ_V1114821525_.jpg</URL><Height Units="pixels">475</Height><Width Units="pixels">306</Width></LargeImage></ImageSet></ImageSets><ItemAttributes><Author>Beatrice Forbes Manz</Author><Binding>Paperback</Binding><DeweyDecimalNumber>950.2</DeweyDecimalNumber><EAN>9780521633840</EAN><Edition>Reprint</Edition><ISBN>0521633842</ISBN><Label>Cambridge University Press</Label><ListPrice><Amount>2299</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$22.99</FormattedPrice></ListPrice><Manufacturer>Cambridge University Press</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>246</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">67</Height><Length Units="hundredths-inches">850</Length><Weight Units="hundredths-pounds">74</Weight><Width Units="hundredths-inches">562</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>1999-03-28</PublicationDate><Publisher>Cambridge University Press</Publisher><Studio>Cambridge University Press</Studio><Title>The Rise and Rule of Tamerlane (Canto original series)</Title></ItemAttributes><OfferSummary><LowestNewPrice><Amount>1298</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$12.98</FormattedPrice></LowestNewPrice><LowestUsedPrice><Amount>1054</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$10.54</FormattedPrice></LowestUsedPrice><LowestCollectiblePrice><Amount>2299</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$22.99</FormattedPrice></LowestCollectiblePrice><TotalNew>25</TotalNew><TotalUsed>21</TotalUsed><TotalCollectible>1</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary><EditorialReviews><EditorialReview><Source>Book Description</Source><Content>This is the first serious study of Tamerlane, the great nomad conqueror who rose to power in 1370 on the ruins of the Mongol Empire and led his armies on campaigns of unprecedented scope, ranging from Moscow to Delhi. As the last nomad ruler to unite the steppe regions of Eurasia, Tamerlane marks the transition from the era of nomad conquest and rule to the modern ascendency of the settled world.</Content></EditorialReview></EditorialReviews></Item><Item><ASIN>1885221770</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=1885221770%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/1885221770%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>474520</SalesRank><SmallImage><URL>http://images.amazon.com/images/P/1885221770.01._SCTHUMBZZZ_V1056534986_.jpg</URL><Height Units="pixels">60</Height><Width Units="pixels">40</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/1885221770.01._SCMZZZZZZZ_V1056534986_.jpg</URL><Height Units="pixels">140</Height><Width Units="pixels">93</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/1885221770.01._SCLZZZZZZZ_V1056534986_.jpg</URL><Height Units="pixels">475</Height><Width Units="pixels">317</Width></LargeImage><ImageSets><ImageSet Category="primary"><SmallImage><URL>http://images.amazon.com/images/P/1885221770.01._SCTHUMBZZZ_V1056534986_.jpg</URL><Height Units="pixels">60</Height><Width Units="pixels">40</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/1885221770.01._SCMZZZZZZZ_V1056534986_.jpg</URL><Height Units="pixels">140</Height><Width Units="pixels">93</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/1885221770.01._SCLZZZZZZZ_V1056534986_.jpg</URL><Height Units="pixels">475</Height><Width Units="pixels">317</Width></LargeImage></ImageSet></ImageSets><ItemAttributes><Author>Roy Stier</Author><Binding>Paperback</Binding><EAN>9781885221773</EAN><ISBN>1885221770</ISBN><Label>Bookpartners</Label><ListPrice><Amount>1695</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$16.95</FormattedPrice></ListPrice><Manufacturer>Bookpartners</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>304</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">79</Height><Length Units="hundredths-inches">900</Length><Weight Units="hundredths-pounds">92</Weight><Width Units="hundredths-inches">606</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>1998-09</PublicationDate><Publisher>Bookpartners</Publisher><Studio>Bookpartners</Studio><Title>Tamerlane: The Ultimate Warrior</Title></ItemAttributes><OfferSummary><LowestUsedPrice><Amount>975</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$9.75</FormattedPrice></LowestUsedPrice><LowestCollectiblePrice><Amount>2295</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$22.95</FormattedPrice></LowestCollectiblePrice><TotalNew>0</TotalNew><TotalUsed>3</TotalUsed><TotalCollectible>1</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary><EditorialReviews><EditorialReview><Source>Book Description</Source><Content>From humble beginnings, Tamerlane, the ancient Turki-Mongol conqueror, rose to become the scourge of his time and changed the course of history. &lt;P&gt;The name Tamerlane runs the gamut of human emotions, evoking in many a revulsion for the devil incarnate, in others, an appreciation for the benefactor of millions. &lt;P&gt;By using accounts from Tamerlane's detractors and his admirers, Roy Stier has captured an amazing story that gives credence to the old adage, "truth is stranger than fiction." &lt;P&gt;Tamerlane, the Ultimate Warrior is presented as a fascinating series of events and captures the reader in the first comprehensive view of this historical figure who dominated Asia and made Europe tremble.</Content></EditorialReview></EditorialReviews></Item><Item><ASIN>0850459494</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0850459494%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/0850459494%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>480359</SalesRank><SmallImage><URL>http://images.amazon.com/images/P/0850459494.01._SCTHUMBZZZ_V1128022797_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">56</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/0850459494.01._SCMZZZZZZZ_V1128022797_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">119</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/0850459494.01._SCLZZZZZZZ_V1128022797_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">372</Width></LargeImage><ImageSets><ImageSet Category="primary"><SmallImage><URL>http://images.amazon.com/images/P/0850459494.01._SCTHUMBZZZ_V1128022797_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">56</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/0850459494.01._SCMZZZZZZZ_V1128022797_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">119</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/0850459494.01._SCLZZZZZZZ_V1128022797_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">372</Width></LargeImage></ImageSet></ImageSets><ItemAttributes><Author>David Nicolle</Author><Binding>Paperback</Binding><Creator Role="Illustrator">Angus Mcbride</Creator><EAN>9780850459494</EAN><ISBN>0850459494</ISBN><Label>Osprey Publishing</Label><ListPrice><Amount>1595</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$15.95</FormattedPrice></ListPrice><Manufacturer>Osprey Publishing</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>48</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">15</Height><Length Units="hundredths-inches">978</Length><Weight Units="hundredths-pounds">36</Weight><Width Units="hundredths-inches">722</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>1990-07-26</PublicationDate><Publisher>Osprey Publishing</Publisher><ReleaseDate>1990-07-26</ReleaseDate><Studio>Osprey Publishing</Studio><Title>The Age of Tamerlane (Men-at-Arms)</Title></ItemAttributes><OfferSummary><LowestNewPrice><Amount>920</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$9.20</FormattedPrice></LowestNewPrice><LowestUsedPrice><Amount>815</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$8.15</FormattedPrice></LowestUsedPrice><TotalNew>12</TotalNew><TotalUsed>9</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary><EditorialReviews><EditorialReview><Source>Book Description</Source><Content>Tamerlane or Timur-i-Lenk ('Timur the Lame') is one of the most extraordinary conquerors in history. In the late 14th century his armies seized huge territories from the borders of Mongolia to Palestine and Anatolia. His passage was marked by massacres that outdid even those of the Mongols for sheer savagery. Timur's career was unequalled since Alexander the Great in terms of constant battlefield success. Only in his youth, while recovering his family estates south of Samarqand, did he face occasional defeat. This title tells the remarkable story of Timur and details the organisation, tactics, arms and armour of his all-conquering army.</Content></EditorialReview></EditorialReviews></Item><Item><ASIN>1851684573</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=1851684573%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/1851684573%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>600493</SalesRank><SmallImage><URL>http://images.amazon.com/images/P/1851684573.01._SCTHUMBZZZ_V63318467_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">49</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/1851684573.01._SCMZZZZZZZ_V63318467_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">104</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/1851684573.01._SCLZZZZZZZ_V63318467_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">324</Width></LargeImage><ImageSets><ImageSet Category="primary"><SmallImage><URL>http://images.amazon.com/images/P/1851684573.01._SCTHUMBZZZ_V63318467_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">49</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/1851684573.01._SCMZZZZZZZ_V63318467_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">104</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/1851684573.01._SCLZZZZZZZ_V63318467_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">324</Width></LargeImage></ImageSet></ImageSets><ItemAttributes><Author>Robert Rand</Author><Binding>Paperback</Binding><DeweyDecimalNumber>320</DeweyDecimalNumber><EAN>9781851684571</EAN><ISBN>1851684573</ISBN><Label>Oneworld Publications</Label><ListPrice><Amount>1495</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$14.95</FormattedPrice></ListPrice><Manufacturer>Oneworld Publications</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>224</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">66</Height><Length Units="hundredths-inches">784</Length><Weight Units="hundredths-pounds">54</Weight><Width Units="hundredths-inches">512</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>2006-09-11</PublicationDate><Publisher>Oneworld Publications</Publisher><Studio>Oneworld Publications</Studio><Title>Tamerlane's Children: Dispatches from Contemporary Uzbekistan</Title></ItemAttributes><OfferSummary><LowestNewPrice><Amount>915</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$9.15</FormattedPrice></LowestNewPrice><LowestUsedPrice><Amount>950</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$9.50</FormattedPrice></LowestUsedPrice><TotalNew>31</TotalNew><TotalUsed>6</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary><EditorialReviews><EditorialReview><Source>Book Description</Source><Content>In the central park of Tashkent, in a place the Uzbeks call the square, a magnificent statue of a mounted warrior dominates the surroundings - Tamerlane - national hero of post-Soviet Uzbekistan. And yet how does this 14th century conqueror reflect one of the world's most diverse and politically intriguing countries?Having spent three years in the region, renowned journalist Robert Rand seeks to answer this question, covering an assortment of fascinating topics, ranging from the effect of 9/11 to the clash of culture in Uzbek pop music. Overflowing with charming anecdotes and loveable personalities, Rand gives the reader a real sense of the country's confused identity and the challenges which it and its people will face in generations to come.</Content></EditorialReview></EditorialReviews></Item><Item><ASIN>B000GKT9AQ</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=B000GKT9AQ%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/B000GKT9AQ%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>619234</SalesRank><ItemAttributes><Author>Harold Lamb</Author><Binding>Hardcover</Binding><Label>Garden City, N.Y., U.S.A.: Doubleday &amp; Company, Inc.</Label><Manufacturer>Garden City, N.Y., U.S.A.: Doubleday &amp; Company, Inc.</Manufacturer><ProductGroup>Book</ProductGroup><ProductTypeName>BOOKS_1973_AND_LATER</ProductTypeName><PublicationDate>1941</PublicationDate><Publisher>Garden City, N.Y., U.S.A.: Doubleday &amp; Company, Inc.</Publisher><Studio>Garden City, N.Y., U.S.A.: Doubleday &amp; Company, Inc.</Studio><Title>Earth Shakers: The: The March of the Barbarians and Tamerlane</Title></ItemAttributes><OfferSummary><LowestUsedPrice><Amount>850</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$8.50</FormattedPrice></LowestUsedPrice><TotalNew>0</TotalNew><TotalUsed>2</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary></Item><Item><ASIN>B00087SKA2</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=B00087SKA2%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/B00087SKA2%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>855185</SalesRank><ItemAttributes><Author>Harold Lamb</Author><Binding>Unknown Binding</Binding><Label>Bantam Books</Label><Manufacturer>Bantam Books</Manufacturer><NumberOfPages>216</NumberOfPages><ProductGroup>Book</ProductGroup><PublicationDate>1955</PublicationDate><Publisher>Bantam Books</Publisher><Studio>Bantam Books</Studio><Title>Tamerlane: Conqueror of the earth</Title></ItemAttributes><OfferSummary><LowestUsedPrice><Amount>650</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$6.50</FormattedPrice></LowestUsedPrice><TotalNew>0</TotalNew><TotalUsed>5</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary></Item><Item><ASIN>0689304463</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0689304463%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/0689304463%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>1028876</SalesRank><ItemAttributes><Author>Barbara Corcoran</Author><Binding>Hardcover</Binding><Creator Role="Illustrator">Charles Robinson</Creator><EAN>9780689304460</EAN><Edition>[1st ed.]</Edition><ISBN>0689304463</ISBN><Label>Macmillan Pub Co</Label><ListPrice><Amount>695</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$6.95</FormattedPrice></ListPrice><Manufacturer>Macmillan Pub Co</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>152</NumberOfPages><ProductGroup>Book</ProductGroup><PublicationDate>1975-02</PublicationDate><Publisher>Macmillan Pub Co</Publisher><ReadingLevel>Young Adult</ReadingLevel><Studio>Macmillan Pub Co</Studio><Title>Meet Me at Tamerlane's Tomb</Title></ItemAttributes><OfferSummary><LowestUsedPrice><Amount>30</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$0.30</FormattedPrice></LowestUsedPrice><LowestCollectiblePrice><Amount>1000</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$10.00</FormattedPrice></LowestCollectiblePrice><TotalNew>0</TotalNew><TotalUsed>20</TotalUsed><TotalCollectible>2</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary></Item></Items></ItemSearchResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>"""
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/test/wsdl2py/test_AmazonS3.py b/ZSI/zsi/test/wsdl2py/test_AmazonS3.py
new file mode 100755
index 0000000..506f949
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_AmazonS3.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest, time
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite, TestException
+from ZSI.schema import ElementDeclaration, GED
+from ZSI import ParsedSoap
+
+"""
+Unittest for contacting the Amazon ECommerce Service
+
+WSDL:
+
+"""
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_'))
+ return suite
+
+
+class AmazonTestCase(ServiceTestCase):
+ """Test case for AmazonS3 web service
+ """
+ name = "test_AmazonS3"
+ client_file_name = "AmazonS3_services.py"
+ types_file_name = "AmazonS3_services_types.py"
+ server_file_name = "AmazonS3_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+ self.wsdl2py_args.append('--lazy')
+
+ def test_local_import(self):
+ pass
+
+ def test_net_CreateBucket(self):
+ loc = self.client_module.AmazonS3Locator()
+ port = loc.getAmazonS3(**self.getPortKWArgs())
+
+ msg = self.client_module.CreateBucketRequest()
+ #msg.SubscriptionId = '0HP1WHME000749APYWR2'
+ msg.Bucket = "HoneyPot"
+ acl = msg.AccessControlList = msg.new_AccessControlList()
+ grant = acl.new_Grant()
+ acl.Grant = [grant]
+ grant.Grantee = grant.new_Grantee()
+ grant.Permission = grant.new_Permission("YES")
+
+ msg.AWSAccessKeyId = '0HP1WHME000749APYWR2'
+ msg.Timestamp = time.gmtime()
+ msg.Signature = 'whatever'
+
+ rsp = port.CreateBucket(msg)
+
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ZSI/zsi/test/wsdl2py/test_Attributes.py b/ZSI/zsi/test/wsdl2py/test_Attributes.py
new file mode 100644
index 0000000..2fd55fa
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_Attributes.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import os, sys, unittest, time
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import FaultException
+from ZSI.TC import _get_global_element_declaration as GED
+from ZSI.writer import SoapWriter
+from ZSI.parse import ParsedSoap
+
+"""
+Unittest for Bug Report
+[ ]
+
+XSD: test_Attributes.xsd
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AttributeTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AttributeTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AttributeTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AttributeTestCase, 'test_'))
+ return suite
+
+
+class AttributeTestCase(ServiceTestCase):
+ name = "test_Attributes"
+ types_file_name = "test_Attributes_xsd_services_types.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+ self.wsdl2py_args.append('-x')
+
+ def test_local_attribute1(self):
+ """
+ """
+ myDouble = 4.5
+ myInt = 9
+ myFloat = 3.0001
+ myDecimal = 8.999
+ myGDateTime = time.gmtime()
+ myAnyURI = "urn:whatever"
+ myQName = ("urn:test", "qbert")
+ myString = "whatever"
+ myHexBinary = hex(888)
+
+ pyobj = GED("urn:example", "Test1").pyclass()
+ # Test serialize/parse
+ pyobj.set_attribute_myDecimal(myDecimal)
+ pyobj.set_attribute_myDouble(myDouble)
+ pyobj.set_attribute_myFloat(myFloat)
+ pyobj.set_attribute_myInt(myInt)
+ pyobj.set_attribute_myDateTime(myGDateTime)
+
+ pyobj.set_attribute_myGDay(myGDateTime)
+ pyobj.set_attribute_myGMonth(myGDateTime)
+ pyobj.set_attribute_myGYear(myGDateTime)
+ pyobj.set_attribute_myGYearMonth(myGDateTime)
+ pyobj.set_attribute_myDate(myGDateTime)
+ pyobj.set_attribute_myTime(myGDateTime)
+
+ pyobj.set_attribute_myAnyURI(myAnyURI)
+ pyobj.set_attribute_myString(myString)
+ pyobj.set_attribute_myHexBinary(myHexBinary)
+ pyobj.set_attribute_myDuration(myGDateTime)
+
+ # Problems parsings
+ pyobj.set_attribute_myQName(myQName)
+ pyobj.set_attribute_myGMonthDay(myGDateTime)
+
+
+ #TODO:
+ #pyobj.set_attribute_myBase64Binary("")
+ #pyobj.set_attribute_myNOTATION("NOT")
+
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ soap = str(sw)
+
+ ps = ParsedSoap(soap)
+ pyobj2 = ps.Parse(pyobj.typecode)
+
+ test = pyobj2.get_attribute_myInt()
+ self.failUnlessEqual(myInt, test)
+
+ test = pyobj2.get_attribute_myDouble()
+ self.failUnlessEqual(myDouble, test)
+
+ test = pyobj2.get_attribute_myFloat()
+ self.failUnlessEqual(myFloat, test)
+
+ test = pyobj2.get_attribute_myDecimal()
+ self.failUnlessEqual(myDecimal, test)
+
+ test = pyobj2.get_attribute_myAnyURI()
+ self.failUnlessEqual(myAnyURI, test)
+
+ test = pyobj2.get_attribute_myQName()
+ self.failUnlessEqual(myQName, test)
+
+ test = pyobj2.get_attribute_myString()
+ self.failUnlessEqual(myString, test)
+
+ test = pyobj2.get_attribute_myHexBinary()
+ self.failUnlessEqual(myHexBinary, test)
+
+ # DateTime stuff
+ test = pyobj2.get_attribute_myDateTime()
+ self.failUnlessEqual(myGDateTime[:-3], test[:-3])
+
+ test = pyobj2.get_attribute_myDate()
+ self.failUnlessEqual(myGDateTime[:3], test[:3])
+
+ test = pyobj2.get_attribute_myTime()
+ self.failUnlessEqual(myGDateTime[4:5], test[4:5])
+
+ test = pyobj.get_attribute_myDuration()
+ self.failUnlessEqual(myGDateTime, test)
+
+ # Bug [ 1453421 ] Incorrect format for type gDay
+ test = pyobj2.get_attribute_myGDay()
+ self.failUnlessEqual(myGDateTime[2], test[2])
+
+ test = pyobj2.get_attribute_myGMonth()
+ self.failUnlessEqual(myGDateTime[1], test[1])
+
+ test = pyobj2.get_attribute_myGYear()
+ self.failUnlessEqual(myGDateTime[0], test[0])
+
+ test = pyobj2.get_attribute_myGYearMonth()
+ self.failUnlessEqual(myGDateTime[:2], test[:2])
+
+ # hmm? negated?
+ #test = pyobj2.get_attribute_myGMonthDay()
+ #self.failUnlessEqual(myGDateTime[1:3], test[1:3])
+
+ def test_local_empty_attribute(self):
+ # [ 1452752 ] attribute with empty value doesn't appear in parsed object
+ myString = ""
+ pyobj = GED("urn:example", "Test1").pyclass()
+ pyobj.set_attribute_myString(myString)
+
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ soap = str(sw)
+
+ print soap
+ ps = ParsedSoap(soap)
+ pyobj2 = ps.Parse(pyobj.typecode)
+
+ test = pyobj2.get_attribute_myString()
+ self.failUnlessEqual(myString, str(test))
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/ZSI/zsi/test/wsdl2py/test_Choice.py b/ZSI/zsi/test/wsdl2py/test_Choice.py
new file mode 100644
index 0000000..7bd5584
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_Choice.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import os, sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import FaultException
+from ZSI.TC import _get_global_element_declaration as GED
+from ZSI.writer import SoapWriter
+
+"""
+Unittest for Bug Report
+[ 1441574 ] ZSI assumes minOccurs(1) for all parts
+
+WSDL:
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ChoiceTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ChoiceTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ChoiceTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ChoiceTestCase, 'test_'))
+ return suite
+
+
+class ChoiceTestCase(ServiceTestCase):
+ name = "test_Choice"
+ types_file_name = "test_Choice_xsd_services_types.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+ self.wsdl2py_args.append('-x')
+
+ def test_local_choice_default_facets_legal1(self):
+ """<choice minOccurs=1 maxOccurs=1>
+ """
+ pyobj = GED("urn:example", "Easy").pyclass()
+ pyobj.Rank = 1
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ print str(sw)
+
+ def test_local_choice_maxOccurs_unbounded(self):
+ """<choice minOccurs=1 maxOccurs=unbounded>
+ """
+ pyobj = GED("urn:example", "Hard").pyclass()
+ pyobj.Name = "steve"
+ pyobj.Name.append("mark")
+ pyobj.Any = "whatever"
+ pyobj.Rank = 2
+ pyobj.Rank.append(3)
+ pyobj.Rank.append(4)
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ print str(sw)
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/ZSI/zsi/test/wsdl2py/test_DerivedTypes.py b/ZSI/zsi/test/wsdl2py/test_DerivedTypes.py
new file mode 100644
index 0000000..909f12b
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_DerivedTypes.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import os, sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import EvaluateException, FaultException
+from ZSI.writer import SoapWriter
+from ZSI.parse import ParsedSoap
+from ZSI.TC import _get_type_definition as GTD
+from ZSI.TC import _get_global_element_declaration as GED
+
+"""
+Unittest
+
+WSDL: derivedTypes.
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(DTTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(DTTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(DTTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(DTTestCase, 'test_'))
+ return suite
+
+
+class DTTestCase(ServiceTestCase):
+ name = "test_DerivedTypes"
+ client_file_name = None
+ types_file_name = "test_DerivedTypes_xsd_services_types.py"
+ server_file_name = None
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-x')
+ self.wsdl2py_args.append('-b')
+
+ def test_local_ged_substitution(self):
+ """This test is designed to fail, trying to dump
+ a GED in via type substitution.
+ """
+ self.types_module
+ pyobj = GED('urn:test', 'test').pyclass()
+
+ # use GED of a derived type
+ pyobj.Actor = sub = GED('urn:test', 'MiddleActor').pyclass()
+ sub.Element1 = 'foo'
+ sub.Element2 = 'bar'
+
+ sw = SoapWriter()
+ self.failUnlessRaises(TypeError, sw.serialize, pyobj)
+
+ def test_local_type_substitution_test2(self):
+ """test extension of extension"""
+
+ attr1 = 'aone'
+ attr2 = 'atwo'
+ attr3 = 'athree'
+ self.types_module
+ pyobj = GED('urn:test', 'test2').pyclass()
+
+ # Test maxOccurs>1 for substitution
+ #
+ pyobj.Actor = [GTD('urn:test', 'TopActor')(None).pyclass()]
+ sub1 = pyobj.Actor[0]
+ sub1.Element1 = 'one'
+ sub1.Element2 = 'two'
+ sub1.Element3 = 'three'
+ sub1.set_attribute_attr1(attr1)
+ sub1.set_attribute_attr2(attr2)
+ sub1.set_attribute_attr3(attr3)
+
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ xml = str(sw)
+ ps = ParsedSoap(xml)
+ pyobj2 = ps.Parse(pyobj.typecode)
+ sub2 = pyobj2.Actor[0]
+
+ self.failUnless(sub2.get_attribute_attr1() == attr1, 'bad attribute 1')
+ self.failUnless(sub2.get_attribute_attr2() == attr2, 'bad attribute 2')
+ self.failUnless(sub2.get_attribute_attr3() == attr3, 'bad attribute 3')
+
+ self.failUnless(sub2.Element1 == sub1.Element1, 'bad element 1')
+ self.failUnless(sub2.Element2 == sub1.Element2, 'bad element 2')
+ self.failUnless(sub2.Element3 == sub1.Element3, 'bad element 3')
+
+ # check parsed out correct type
+ self.failUnless(isinstance(sub2.typecode, sub1.typecode.__class__),
+ 'local element actor "%s" must be an instance of "%s"'%
+ (sub2.typecode, sub1.typecode.__class__))
+
+ # check local element is derived from base
+ base = GTD('urn:test', 'BaseActor')
+ self.failUnless(isinstance(sub2.typecode, base),
+ 'local element actor must be a derived type of "%s"'%
+ base)
+
+
+ def test_local_type_substitution2(self):
+ """test extension of extension"""
+
+ attr1 = 'aone'
+ attr2 = 'atwo'
+ attr3 = 'athree'
+ self.types_module
+ pyobj = GED('urn:test', 'test').pyclass()
+
+ # [ 1489129 ] Unexpected subsitution error message
+ # try to parse before type ever initialized
+ """
+ ps = ParsedSoap(MSG1)
+ pyobj0 = ps.Parse(pyobj.typecode)
+ sub0 = pyobj0.Actor
+ self.failUnless(sub0.get_attribute_attr1() == attr1, 'bad attribute1')
+ self.failUnless(sub0.get_attribute_attr2() == attr2, 'bad attribute2')
+ """
+
+ # [ 1489090 ] Derived type attributes don't populate the attr dictionary
+ # [ 1489677 ] Derivation from derived type missing derived element
+ #
+ pyobj.Actor = sub1 = GTD('urn:test', 'TopActor')(None).pyclass()
+ sub1.Element1 = 'one'
+ sub1.Element2 = 'two'
+ sub1.Element3 = 'three'
+ sub1.set_attribute_attr1(attr1)
+ sub1.set_attribute_attr2(attr2)
+ sub1.set_attribute_attr3(attr3)
+
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ xml = str(sw)
+ ps = ParsedSoap(xml)
+ pyobj2 = ps.Parse(pyobj.typecode)
+ sub2 = pyobj2.Actor
+
+ self.failUnless(sub2.get_attribute_attr1() == attr1, 'bad attribute 1')
+ self.failUnless(sub2.get_attribute_attr2() == attr2, 'bad attribute 2')
+ self.failUnless(sub2.get_attribute_attr3() == attr3, 'bad attribute 3')
+
+ self.failUnless(sub2.Element1 == sub1.Element1, 'bad element 1')
+ self.failUnless(sub2.Element2 == sub1.Element2, 'bad element 2')
+ self.failUnless(sub2.Element3 == sub1.Element3, 'bad element 3')
+
+ # check parsed out correct type
+ self.failUnless(isinstance(sub2.typecode, sub1.typecode.__class__),
+ 'local element actor "%s" must be an instance of "%s"'%
+ (sub2.typecode, sub1.typecode.__class__))
+
+ # check local element is derived from base
+ base = GTD('urn:test', 'BaseActor')
+ self.failUnless(isinstance(sub2.typecode, base),
+ 'local element actor must be a derived type of "%s"'%
+ base)
+
+ def test_local_parse_missing_type_substitution(self):
+ """attempt to substitute an unregistered/unknown type """
+ attr1 = 'myclass'
+ attr2 = 'whatever'
+ self.types_module
+ pyobj = GED('urn:test', 'test').pyclass()
+
+ ps = ParsedSoap(NO_SUB_MSG)
+ self.failUnlessRaises(EvaluateException, ps.Parse, pyobj.typecode)
+
+ def test_local_type_substitution1(self):
+ """test extension. Parse known instance, serialize an equivalent, Parse it back. """
+ attr1 = 'myclass'
+ attr2 = 'whatever'
+ self.types_module
+ pyobj = GED('urn:test', 'test').pyclass()
+
+ # [ 1489129 ] Unexpected subsitution error message
+ # try to parse before type ever initialized
+ ps = ParsedSoap(MSG1)
+ pyobj0 = ps.Parse(pyobj.typecode)
+ sub0 = pyobj0.Actor
+ self.failUnless(sub0.get_attribute_attr1() == attr1, 'bad attribute1')
+ self.failUnless(sub0.get_attribute_attr2() == attr2, 'bad attribute2')
+
+ # [ 1489090 ] Derived type attributes don't populate the attr dictionary
+ #
+ pyobj.Actor = sub1 = GTD('urn:test', 'MiddleActor')(None).pyclass()
+ sub1.Element1 = 'foo'
+ sub1.Element2 = 'bar'
+ sub1.set_attribute_attr1(attr1)
+ sub1.set_attribute_attr2(attr2)
+
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ xml = str(sw)
+ ps = ParsedSoap(xml)
+ pyobj2 = ps.Parse(pyobj.typecode)
+ sub2 = pyobj2.Actor
+
+ self.failUnless(sub2.get_attribute_attr1() == attr1, 'bad attribute class')
+ self.failUnless(sub2.get_attribute_attr2() == attr2, 'bad attribute name')
+
+ # check parsed out correct type
+ self.failUnless(isinstance(sub2.typecode, sub1.typecode.__class__),
+ 'local element actor "%s" must be an instance of "%s"'%
+ (sub2.typecode, sub1.typecode.__class__))
+
+ # check local element is derived from base
+ base = GTD('urn:test', 'BaseActor')
+ self.failUnless(isinstance(sub2.typecode, base),
+ 'local element actor must be a derived type of "%s"'%
+ base)
+
+
+MSG1 = """<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="urn:test"><ns1:test><actor attr1="myclass" attr2="whatever" xsi:type="ns1:MiddleActor"><element1 xsi:type="xsd:string">foo</element1><element2 xsi:type="xsd:string">bar</element2></actor></ns1:test></SOAP-ENV:Body></SOAP-ENV:Envelope>"""
+
+NO_SUB_MSG = """<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="urn:test"><ns1:test><actor attr1="myclass" attr2="whatever" xsi:type="ns1:Bogus"><element1 xsi:type="xsd:string">foo</element1><element2 xsi:type="xsd:string">bar</element2></actor></ns1:test></SOAP-ENV:Body></SOAP-ENV:Envelope>"""
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/ZSI/zsi/test/wsdl2py/test_Echo.py b/ZSI/zsi/test/wsdl2py/test_Echo.py
new file mode 100644
index 0000000..9573aa0
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_Echo.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import os, sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import FaultException
+"""
+Unittest
+
+WSDL: ../../samples/Echo/Echo.wsdl
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(EchoTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(EchoTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(EchoTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(EchoTestCase, 'test_'))
+ return suite
+
+
+class EchoTestCase(ServiceTestCase):
+ name = "test_Echo"
+ client_file_name = "EchoServer_services.py"
+ types_file_name = "EchoServer_services_types.py"
+ server_file_name = "EchoServer_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_local_Echo(self):
+ msg = self.client_module.EchoRequest()
+ rsp = self.client_module.EchoResponse()
+
+ def test_dispatch_Echo(self):
+ loc = self.client_module.EchoServerLocator()
+ port = loc.getEchoServer(**self.getPortKWArgs())
+
+ msg = self.client_module.EchoRequest()
+ msg.EchoIn = 'bla bla bla'
+ rsp = port.Echo(msg)
+ self.failUnless(rsp.EchoResult == msg.EchoIn, "Bad Echo")
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/ZSI/zsi/test/wsdl2py/test_InfoBil.py b/ZSI/zsi/test/wsdl2py/test_InfoBil.py
new file mode 100644
index 0000000..181736c
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_InfoBil.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import os, sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import FaultException
+"""
+Unittest
+
+WSDL: http://javatest2.infodata.se/webservices/services/Infobil?wsdl
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(TestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(TestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(TestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(TestCase, 'test_'))
+ return suite
+
+
+class TestCase(ServiceTestCase):
+ name = "test_InfoBil"
+ client_file_name = "InfoBilServiceService_services.py"
+ types_file_name = "InfoBilServiceService_services_types.py"
+ server_file_name = "InfoBilServiceService_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_local_import(self):
+ pass
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/ZSI/zsi/test/wsdl2py/test_MapPoint.py b/ZSI/zsi/test/wsdl2py/test_MapPoint.py
new file mode 100644
index 0000000..1f1ee79
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_MapPoint.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+"""
+Unittest for contacting the Map Point Service.
+
+WSDL:
+"""
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(MapPointTest, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(MapPointTest, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(MapPointTest, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(MapPointTest, 'test_'))
+ return suite
+
+
+
+class MapPointTest(ServiceTestCase):
+ """Test case for OPCService Web service
+
+ """
+ name = "test_MapPoint"
+ client_file_name = "CommonService_services.py"
+ types_file_name = "CommonService_services_types.py"
+ server_file_name = "CommonService_services_server.py"
+
+ def test_net_GetVersionInfo(self):
+ """expect this to fail cause i'm not doing http authentication.
+ """
+ loc = self.client_module.CommonServiceLocator()
+ port = loc.getCommonServiceSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.GetVersionInfoSoapIn()
+ try:
+ rsp = port.GetVersionInfo(msg)
+ except RuntimeError:
+ # RuntimeError: HTTP Digest Authorization Failed
+ pass
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/ZSI/zsi/test/wsdl2py/test_OpcDaGateway.py b/ZSI/zsi/test/wsdl2py/test_OpcDaGateway.py
new file mode 100644
index 0000000..ac9b883
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_OpcDaGateway.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+import ZSI
+from ZSI import FaultException
+"""
+Unittest for contacting the OPC XML-DA Service.
+
+WSDL: http://tswinc.us/XMLDADemo/ts_sim/OpcDaGateway.asmx?WSDL
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(OPCServiceTest, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(OPCServiceTest, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(OPCServiceTest, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(OPCServiceTest, 'test_'))
+ return suite
+
+
+class OPCServiceTest(ServiceTestCase):
+ """Test case for OPCService Web service
+
+ def GetProperties(self, request):
+ def Subscribe(self, request):
+ def SubscriptionPolledRefresh(self, request):
+ def SubscriptionCancel(self, request):
+ def GetStatus(self, request):
+ def Browse(self, request):
+ def Read(self, request):
+ def Write(self, request):
+ """
+ name = "test_OpcDaGateway"
+ client_file_name = "OpcXmlDaSrv_services.py"
+ types_file_name = "OpcXmlDaSrv_services_types.py"
+ server_file_name = "OpcXmlDaSrv_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_local_anyType(self):
+ """serialize an int via anyType, then parse it back.
+ """
+ import time
+ pyobj = self.client_module.ReadSoapOut()
+ pyobj.RItemList = pyobj.new_RItemList()
+ item = pyobj.RItemList.new_Items()
+ pyobj.RItemList.Items = [item,]
+ item.typecode.ofwhat[1].processContents = 'lax'
+ item.Value = 123
+ s = str(ZSI.SoapWriter().serialize(pyobj))
+
+ ps = ZSI.ParsedSoap(s)
+ pyobj = ps.Parse(pyobj.typecode)
+ for item in pyobj.RItemList.Items:
+ item.Value
+
+ def test_net_Browse(self):
+ """FaultException: The item path is not known to the server.
+ """
+ loc = self.client_module.OpcXmlDaSrvLocator()
+ port = loc.getOpcXmlDaSrvSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.BrowseSoapIn()
+ msg._PropertyNames=['Static']
+ msg._attrs = {'ItemPath':'Static'}
+
+ self.failUnless(\
+ getattr(msg.typecode, 'attribute_typecode_dict', None) is not None,
+ )
+
+ self.failUnlessRaises(FaultException, port.Browse, msg)
+
+
+# def hangs_test_net_Read(self):
+# """FaultException: The item path is not known to the server.
+# """
+# msg = self.getInputMessageInstance('Read')
+#
+# #msg = ReadSoapIn()
+# op = msg.new_Options()
+# msg.Options = op
+# op.set_attribute_ReturnItemTime(True)
+# op.set_attribute_ReturnItemName(True)
+# op.set_attribute_ClientRequestHandle("")
+# op.set_attribute_LocaleID('en-us')
+#
+# item_list = msg.new_ItemList()
+# msg.ItemList = item_list
+# item_list.set_attribute_MaxAge(1000)
+#
+# item = item_list.new_Items()
+# item_list.Items = item
+# item.set_attribute_ItemPath("")
+# item.set_attribute_ItemName("Staic.Analog Types.Int")
+# item.set_attribute_ClientItemHandle("")
+#
+# self.failUnless(\
+# getattr(msg.typecode, 'attribute_typecode_dict', None) is not None,
+# )
+#
+# self._ports[0].Read(msg)
+#
+
+# def hangs_test_net_GetProperties(self):
+# """sending an empty GetProperties request,
+# receiving empty response.
+#
+# <GetPropertiesResult RcvTime="2005-12-16T13:23:55.8593750-05:00"
+# ReplyTime="2005-12-16T13:24:00.1093750-05:00"
+# RevisedLocaleID="en-us" ServerState="running" />
+# """
+# operationName = 'GetProperties'
+# msg = self.getInputMessageInstance(operationName)
+# msg._ItemIDs
+# msg._PropertyNames
+# response = self._ports[0].GetProperties(msg)
+# result = response._GetPropertiesResult
+#
+# # not sure these attributes are required but check for them.
+# self.failUnless(isinstance(getattr(result, '_attrs', None), dict))
+# for k in ['RcvTime','ReplyTime','RevisedLocaleID','ServerState']:
+# self.failUnless(result._attrs.has_key(k))
+#
+# self.failUnless(len(response._PropertyLists) == 0)
+# self.failUnless(len(response._Errors) == 0)
+
+
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/ZSI/zsi/test/wsdl2py/test_Sabre.py b/ZSI/zsi/test/wsdl2py/test_Sabre.py
new file mode 100644
index 0000000..8cc4b62
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_Sabre.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import FaultException
+"""
+Unittest for contacting
+
+WSDL: http://webservices.sabre.com/wsdl/sabreXML1.0.00/res/SessionCreateRQ.wsdl
+"""
+
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ServiceTest, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ServiceTest, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ServiceTest, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ServiceTest, 'test_'))
+ return suite
+
+
+class ServiceTest(ServiceTestCase):
+ """Test case for Sabre Web service
+
+ """
+ name = "test_Sabre"
+ client_file_name = "SessionCreateRQService_services.py"
+ types_file_name = "SessionCreateRQService_services_types.py"
+ server_file_name = "SessionCreateRQService_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_net_SessionCreate(self):
+ """
+_________________________________ Mon Jan 2 13:41:22 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="http://www.opentravel.org/OTA/2002/11"><ns1:SessionCreateRQ><ns1:POS><ns1:Source PseudoCityCode="SF"></ns1:Source></ns1:POS></ns1:SessionCreateRQ></SOAP-ENV:Body></SOAP-ENV:Envelope>
+_________________________________ Mon Jan 2 13:41:22 2006 RESPONSE:
+Server: Netscape-Enterprise/6.0
+Date: Mon, 02 Jan 2006 21:41:21 GMT
+Content-length: 1568
+Content-type: text/xml; charset="utf-8"
+Soapaction: ""
+
+<?xml version="1.0" encoding="UTF-8"?>
+<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"><soap-env:Header><wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext"/></soap-env:Header><soap-env:Body><soap-env:Fault><faultcode>soap-env:Client.ConversationIdRequired</faultcode><faultstring>Conversation id required</faultstring><detail><StackTrace>com.sabre.universalservices.base.session.SessionException: errors.session.USG_CONVERSATION_ID_REQUIRED
+ at com.sabre.universalservices.gateway.control.SecurityInterceptor.executeOnRequest(SecurityInterceptor.java:111)
+ at com.sabre.universalservices.base.interceptor.Interceptor.execute(Interceptor.java:113)
+ at com.sabre.universalservices.base.interceptor.InterceptorChain.applyInterceptors(InterceptorChain.java:32)
+ at com.sabre.universalservices.base.interceptor.InterceptorManager.process(InterceptorManager.java:116)
+ at com.sabre.universalservices.gateway.control.WSGateway.onMessage(WSGateway.java:297)
+ at com.sabre.universalservices.gateway.control.WSGateway.handleRequest(WSGateway.java:208)
+ at com.sabre.universalservices.gateway.control.WSGateway.doPost(WSGateway.java:156)
+ at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
+ at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
+ at com.iplanet.server.http.servlet.NSServletRunner.invokeServletService(NSServletRunner.java:919)
+ at com.iplanet.server.http.servlet.NSServletRunner.Service(NSServletRunner.java:483)
+</StackTrace></detail></soap-env:Fault></soap-env:Body></soap-env:Envelope>
+E
+ """
+ loc = self.client_module.SessionCreateRQServiceLocator()
+ port = loc.getSessionCreatePortType(**self.getPortKWArgs())
+
+ msg = self.client_module.GetSessionCreateInput()
+ msg.POS = msg.new_POS()
+ msg.POS.Source = msg.POS.new_Source()
+ msg.POS.Source.set_attribute_PseudoCityCode("SF")
+
+ self.failUnlessRaises(FaultException, port.SessionCreateRQ, msg)
+ #response = self._ports[0].SessionCreateRQ(msg)
+ #response.Success
+ #response.Warnings
+ #response.ConversationId
+ #response.Errors
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/ZSI/zsi/test/wsdl2py/test_TerraService.py b/ZSI/zsi/test/wsdl2py/test_TerraService.py
new file mode 100755
index 0000000..b9b264a
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_TerraService.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python
+
+############################################################################
+# David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import ServiceTestCase, ServiceTestSuite
+
+import re
+from ZSI import EvaluateException
+"""
+Unittest for contacting the TerraService Web service.
+
+WSDL: http://terraservice.net/TerraService.asmx?WSDL
+"""
+
+CONFIG_FILE = 'config.txt'
+CONFIG_SECTION = 'complex_types'
+SERVICE_NAME = 'TerraService'
+PORT_NAME = 'TerraServiceSoap'
+EXCEPTION_STRING_SERIALIZE = r"Serializing ConvertPlaceToLonLatPt xmlns=\"http://terraserver-usa.com/terraserver/\"._place, Exception Serializing place xmlns=\"http://terraserver-usa.com/terraserver/\"._City, AttributeError 'int' object has no attribute \'replace\'"
+
+SERIALIZE_PATTERN = re.compile(EXCEPTION_STRING_SERIALIZE)
+
+class TerraServiceTest(ServiceTestCase):
+ """Test case for TerraService Web service
+ """
+ name = "test_TerraService"
+
+ def test_ConvertPlaceToLonLatPt(self):
+ operationName = 'ConvertPlaceToLonLatPt'
+ request = self.getInputMessageInstance(operationName)
+ request._place = self._moduleDict[self._typeModuleName].ns1.Place_Def()
+ request._place._City = 'Oak Harbor'
+ request._place._State = 'Washington'
+ request._place._Country = 'United States'
+ response = self.RPC(operationName, request)
+
+
+
+ def test_ConvertLonLatPtToNearestPlace(self):
+ operationName = 'ConvertLonLatPtToNearestPlace'
+ request = self.getInputMessageInstance(operationName)
+ request._place = self._moduleDict[self._typeModuleName].ns1.Place_Def()
+ request._point = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._point._Lon = -122.643
+ request._point._Lat = 48.297
+ response = self.RPC(operationName, request)
+
+ def test_ConvertLonLatPtToUtmPt(self):
+ operationName = 'ConvertLonLatPtToUtmPt'
+ request = self.getInputMessageInstance(operationName)
+ request._point = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._point._Lon = -122.643
+ request._point._Lat = 48.297
+ response = self.RPC(operationName, request)
+
+ def test_ConvertUtmPtToLonLatPt(self):
+ operationName = 'ConvertUtmPtToLonLatPt'
+ request = self.getInputMessageInstance(operationName)
+ request._utm = self._moduleDict[self._typeModuleName].ns1.UtmPt_Def()
+ request._utm._X = 526703.512403
+ request._utm._Y = 5348595.96493
+ request._utm._Zone = 10
+ response = self.RPC(operationName, request)
+
+ def test_CountPlacesInRect(self):
+ operationName = 'CountPlacesInRect'
+ request = self.getInputMessageInstance(operationName)
+ request._upperleft = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._upperleft._Lon = -122.647
+ request._upperleft._Lat = 48.293
+ request._lowerright = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._lowerright._Lon = request._upperleft._Lon + 1.0
+ request._lowerright._Lat = request._upperleft._Lon - 1.0
+ request._ptype = "HillMountain"
+ response = self.RPC(operationName, request)
+
+ def test_GetAreaFromPt(self):
+ operationName = 'GetAreaFromPt'
+ request = self.getInputMessageInstance(operationName)
+ request._center = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._center._Lon = -122.647
+ request._center._Lat = 48.293
+ request._theme = 'Topo'
+ request._scale = "Scale2m"
+ request._displayPixWidth = 2
+ request._displayPixHeight = 2
+ response = self.RPC(operationName, request)
+
+ def test_GetAreaFromRect(self):
+ operationName = 'GetAreaFromRect'
+ request = self.getInputMessageInstance(operationName)
+ request._upperLeft = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._upperLeft._Lon = -122.647
+ request._upperLeft._Lat = 48.293
+ request._lowerRight = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._lowerRight._Lon = request._upperLeft._Lon + 1.0
+ request._lowerRight._Lat = request._upperLeft._Lat - 1.0
+ request._theme = 'Topo'
+ request._scale = "Scale2m"
+ response = self.RPC(operationName, request)
+
+ def test_GetAreaFromTileId(self):
+ operationName = 'GetAreaFromTileId'
+ request = self.getInputMessageInstance(operationName)
+ id = self._moduleDict[self._typeModuleName].ns1.TileId_Def()
+ id._Theme = 'Topo'
+ id._Scale = "Scale2m"
+ id._Scene = 8
+ id._X = 20
+ id._y = 20
+ request._id = id
+ request._displayPixWidth = 2
+ request._displayPixHeight = 2
+ response = self.RPC(operationName, request)
+
+ def test_GetLatLonMetrics(self):
+ operationName = 'GetLatLonMetrics'
+ request = self.getInputMessageInstance(operationName)
+ request._point = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._point._Lon = -122.647
+ request._point._Lat = 48.293
+ response = self.RPC(operationName, request)
+
+ # derived type (enum) problem
+ # skipping it for now
+
+
+ # derived type (enum) problem
+ # also inconsistent timeout problem for this call
+
+
+ def test_GetPlaceListInRect(self):
+ operationName = 'GetPlaceListInRect'
+ request = self.getInputMessageInstance(operationName)
+ request._upperleft = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._upperleft._Lon = -123.0
+ request._upperleft._Lat = 44.0
+ request._lowerright = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ # needs to be small, otherwise different items
+ # returned each time
+ request._lowerright._Lon = -122.8
+ request._lowerright._Lat = 43.8
+ request._ptype = "HillMountain"
+ request._MaxItems = 3
+ response = self.RPC(operationName, request)
+
+ def test_GetTheme(self):
+ operationName = 'GetTheme'
+ request = self.getInputMessageInstance(operationName)
+ request._theme = 'Topo'
+ response = self.RPC(operationName, request)
+
+ def test_GetTile(self):
+ operationName = 'GetTile'
+ request = self.getInputMessageInstance(operationName)
+ request._id = self._moduleDict[self._typeModuleName].ns1.TileId_Def()
+ request._id._Theme = 'Topo'
+ request._id._Scale = 'Scale2m'
+ request._id._Scene = 8
+ request._id._X = 20
+ request._id._Y = 20
+ response = self.RPC(operationName, request)
+
+ def test_GetTileMetaFromLonLatPt(self):
+ operationName = 'GetTileMetaFromLonLatPt'
+ request = self.getInputMessageInstance(operationName)
+ request._theme = 'Topo'
+ request._point = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._point._Lon = -122.64
+ request._point._Lat = 48.29
+ request._scale = "Scale4m"
+ response = self.RPC(operationName, request)
+
+ def test_GetTileMetaFromTileId(self):
+ operationName = 'GetTileMetaFromTileId'
+ request = self.getInputMessageInstance(operationName)
+ request._id = self._moduleDict[self._typeModuleName].ns1.TileId_Def()
+ request._id._Theme = 'Topo'
+ request._id._Scale = 'Scale2m'
+ request._id._Scene = 8
+ request._id._X = 20
+ request._id._Y = 20
+ response = self.RPC(operationName, request)
+
+
+class TerraServiceTestFailures(ServiceTestCase):
+ name = "test_TerraService"
+
+ def test_ConvertPlaceToLonLatPt_x1(self):
+ """
+ This test should fail
+ """
+ operationName = 'ConvertPlaceToLonLatPt'
+ request = self.getInputMessageInstance(operationName)
+ request._place = self._moduleDict[self._typeModuleName].ns1.Place_Def()
+ request._place._City = 1
+ request._place._State = 'Washington'
+ request._place._Country = 'United States'
+
+ try:
+ response = self.RPC(operationName, request)
+
+ except Exception, msg:
+ exceptionString = str(msg)
+ if SERIALIZE_PATTERN.match(exceptionString):
+ pass
+ else:
+ raise
+
+ def test_GetPlaceFacts(self):
+ operationName = 'GetPlaceFacts'
+ request = self.getInputMessageInstance(operationName)
+ request._place = self._moduleDict[self._typeModuleName].ns1.Place_Def()
+ request._place._City = 'Seattle'
+ request._place._State = 'Washington'
+ request._place._Country = 'United States'
+ try:
+ response = self.RPC(operationName, request)
+ except EvaluateException, ex:
+ pass
+
+ def test_GetPlaceList(self):
+ operationName = 'GetPlaceList'
+ request = self.getInputMessageInstance(operationName)
+ request._placeName = 'New York'
+ request._MaxItems = 5
+ request._imagePresence = 0
+ try:
+ response = self.RPC(operationName, request)
+ except EvaluateException, ex:
+ pass
+
+def makeTestSuite():
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(TerraServiceTest, 'test_'))
+ suite.addTest(unittest.makeSuite(TerraServiceTestFailures, 'test_'))
+ return suite
+
+
+if __name__ == "__main__" :
+ unittest.TestProgram(defaultTest="makeTestSuite")
diff --git a/ZSI/zsi/test/wsdl2py/test_ThreatService.py b/ZSI/zsi/test/wsdl2py/test_ThreatService.py
new file mode 100755
index 0000000..2e00733
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_ThreatService.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+
+
+"""
+Unittest for contacting the threatService Web service.
+
+WSDL: http://www.boyzoid.com/threat.cfc?wsdl
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(HomelandTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(HomelandTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(HomelandTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(HomelandTestCase, 'test_'))
+ return suite
+
+
+class HomelandTestCase(ServiceTestCase):
+ """Test case for ZipCodeResolver Web service
+ """
+ name = "test_ThreatService"
+ client_file_name = "Current_Homeland_Security_Threat_Level_services.py"
+ types_file_name = "Current_Homeland_Security_Threat_Level_services_types.py"
+ server_file_name = None
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_net_threatLevel(self):
+ loc = self.client_module.Current_Homeland_Security_Threat_LevelLocator()
+ port = loc.getthreat(**self.getPortKWArgs())
+
+ msg = self.client_module.threatLevelRequest()
+ rsp = port.threatLevel(msg)
+ for item in rsp.ThreatLevelReturn.Item:
+ item.Key
+ item.Value
+
+
+
+if __name__ == "__main__" :
+ main()
diff --git a/ZSI/zsi/test/wsdl2py/test_WhiteMesa.py b/ZSI/zsi/test/wsdl2py/test_WhiteMesa.py
new file mode 100755
index 0000000..b74647e
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_WhiteMesa.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite, TestException
+
+"""
+Unittest for contacting the WhiteMesa web service for rpc/literal tests.
+
+WSDL: http://www.whitemesa.net/wsdl/test-rpc-lit.wsdl
+
+"""
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(WhiteMesaTest, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(WhiteMesaTest, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(WhiteMesaTest, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(WhiteMesaTest, 'test_'))
+ return suite
+
+
+class WhiteMesaTest(ServiceTestCase):
+ """Test case for ZipCodeResolver Web service
+ """
+ name = "test_WhiteMesa"
+ client_file_name = "RPC_Literal_TestDefinitions_services.py"
+ types_file_name = "RPC_Literal_TestDefinitions_services_types.py"
+ server_file_name = "RPC_Literal_TestDefinitions_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_local_EchoBoolean(self):
+ from ZSI.writer import SoapWriter
+ msg = self.client_module.echoBooleanRequest()
+ msg._inputBoolean = True
+ sw = SoapWriter()
+ sw.serialize(msg)
+
+ def test_net_EchoBoolean(self):
+ msg = self.client_module.echoBooleanRequest()
+ msg._inputBoolean = True
+
+ loc = self.client_module.WhiteMesaSoapRpcLitTestSvcLocator()
+ port = loc.getSoapTestPortTypeRpc(**self.getPortKWArgs())
+ rsp = port.echoBoolean(msg)
+
+ self.failUnless(msg._inputBoolean == rsp._return,
+ "EchoBoolean Failed")
+
+ def test_dispatch_EchoBoolean(self):
+ self.test_net_EchoBoolean()
+
+
+if __name__ == "__main__" :
+ main()
diff --git a/ZSI/zsi/test/wsdl2py/test_XMethodsQuery.py b/ZSI/zsi/test/wsdl2py/test_XMethodsQuery.py
new file mode 100755
index 0000000..d9b5d3c
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_XMethodsQuery.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+
+############################################################################
+# David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+
+"""
+Unittest for contacting the XMethodsQuery Web service.
+
+WSDL: http://www.xmethods.net/wsdl/query.wsdl
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(XMethodsQueryTest, 'test_dispatch', suiteClass=ServiceTestSuite))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(XMethodsQueryTest, 'test_local', suiteClass=ServiceTestSuite))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(XMethodsQueryTest, 'test_net', suiteClass=ServiceTestSuite))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(XMethodsQueryTest, 'test_', suiteClass=ServiceTestSuite))
+ return suite
+
+
+class XMethodsQueryTest(ServiceTestCase):
+ """Test case for XMethodsQuery Web service
+ """
+ name = "test_XMethodsQuery"
+ client_file_name = "XMethodsQuery_services.py"
+ types_file_name = "XMethodsQuery_services_types.py"
+ server_file_name = "XMethodsQuery_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_net_getAllServiceNames(self):
+ loc = self.client_module.XMethodsQueryLocator()
+ port = loc.getXMethodsQuerySoapPortType(**self.getPortKWArgs())
+
+ msg = self.client_module.getAllServiceNames2SoapIn()
+ rsp = port.getAllServiceNames(msg)
+ return rsp
+
+ def test_net_getAllServiceSummaries(self):
+ loc = self.client_module.XMethodsQueryLocator()
+ port = loc.getXMethodsQuerySoapPortType(**self.getPortKWArgs())
+
+ msg = self.client_module.getAllServiceSummaries1SoapIn()
+ rsp = port.getAllServiceSummaries(msg)
+ return rsp
+
+ def test_net_getServiceDetail(self):
+ loc = self.client_module.XMethodsQueryLocator()
+ port = loc.getXMethodsQuerySoapPortType(**self.getPortKWArgs())
+
+ msg = self.client_module.getServiceDetail4SoapIn()
+ msg._id = 'uuid:A29C0D6C-5529-0D27-A91A-8E02D343532B'
+ rsp = port.getServiceDetail(msg)
+ return rsp
+
+ def test_net_getServiceNamesByPublisher(self):
+ loc = self.client_module.XMethodsQueryLocator()
+ port = loc.getXMethodsQuerySoapPortType(**self.getPortKWArgs())
+
+ msg = self.client_module.getServiceNamesByPublisher3SoapIn()
+ msg._publisherID = 'xmethods.net'
+ rsp = port.getServiceNamesByPublisher(msg)
+ return rsp
+
+ def test_net_getServiceSummariesByPublisher(self):
+ loc = self.client_module.XMethodsQueryLocator()
+ port = loc.getXMethodsQuerySoapPortType(**self.getPortKWArgs())
+
+ msg = self.client_module.getServiceSummariesByPublisher0SoapIn()
+ msg._publisherID = 'xmethods.net'
+ rsp = port.getServiceSummariesByPublisher(msg)
+ return rsp
+
+
+if __name__ == "__main__" :
+ main()
diff --git a/ZSI/zsi/test/wsdl2py/test_ZipCodeResolver.py b/ZSI/zsi/test/wsdl2py/test_ZipCodeResolver.py
new file mode 100755
index 0000000..2f6fd37
--- /dev/null
+++ b/ZSI/zsi/test/wsdl2py/test_ZipCodeResolver.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+
+############################################################################
+# David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+
+"""
+Unittest for contacting the ZipCodeResolver Web service.
+
+WSDL: http://webservices.eraserver.net/zipcoderesolver/zipcoderesolver.asmx?WSDL
+
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ZipCodeResolverTest, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ZipCodeResolverTest, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ZipCodeResolverTest, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ZipCodeResolverTest, 'test_'))
+ return suite
+
+
+class ZipCodeResolverTest(ServiceTestCase):
+ """Test case for ZipCodeResolver Web service
+ """
+ name = "test_ZipCodeResolver"
+ client_file_name = "ZipCodeResolver_services.py"
+ types_file_name = "ZipCodeResolver_services_types.py"
+ #server_file_name = "ZipCodeResolver_services_server.py"
+
+ def test_net_CorrectedAddressHtml(self):
+ loc = self.client_module.ZipCodeResolverLocator()
+ port = loc.getZipCodeResolverSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.CorrectedAddressHtmlSoapIn()
+ msg._address = '636 Colusa Avenue'
+ msg._city = 'Berkeley'
+ msg._state = 'California'
+ rsp = port.CorrectedAddressHtml(msg)
+
+ def test_net_CorrectedAddressXml(self):
+ loc = self.client_module.ZipCodeResolverLocator()
+ port = loc.getZipCodeResolverSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.CorrectedAddressXmlSoapIn()
+ msg._address = '636 Colusa Avenue'
+ msg._city = 'Berkeley'
+ msg._state = 'California'
+ rsp = port.CorrectedAddressXml(msg)
+
+ def test_net_FullZipCode(self):
+ loc = self.client_module.ZipCodeResolverLocator()
+ port = loc.getZipCodeResolverSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.FullZipCodeSoapIn()
+ msg._address = '636 Colusa Avenue'
+ msg._city = 'Berkeley'
+ msg._state = 'California'
+ rsp = port.FullZipCode(msg)
+
+ def test_net_ShortZipCode(self):
+ loc = self.client_module.ZipCodeResolverLocator()
+ port = loc.getZipCodeResolverSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.ShortZipCodeSoapIn()
+ msg._address = '636 Colusa Avenue'
+ msg._city = 'Berkeley'
+ msg._state = 'California'
+ rsp = port.ShortZipCode(msg)
+
+ def test_net_VersionInfo(self):
+ loc = self.client_module.ZipCodeResolverLocator()
+ port = loc.getZipCodeResolverSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.VersionInfoSoapIn()
+ rsp = port.VersionInfo(msg)
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/doc/.cvsignore b/doc/.cvsignore
new file mode 100644
index 0000000..d920906
--- /dev/null
+++ b/doc/.cvsignore
@@ -0,0 +1,6 @@
+zsi.ps
+zsi.html
+zsi.css
+zsi.pdf
+zsixsd.tex
+version.tex
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..4044fe9
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,65 @@
+# $Header$
+
+# put the path to where the Python source was untar'd here (not where it
+# is installed)
+PYSRCDIR= /Library/Frameworks/Python.framework/Versions/2.4
+MKHOWTO= PATH=$$PATH:$(HOME)/Desktop/Python/PythonDocTools/tools/ mkhowto
+#PAPER= a4
+PAPER= letter
+
+M_CHAPTERS= c01-intro.tex c02-samples.tex \
+ c03-except.tex c04-utils.tex c05-parse.tex c06-tc.tex \
+ c07-writer.tex c08-fault.tex c09-resolve.tex \
+ c10-dispatch.tex
+
+G_CHAPTERS= guide01-intro.tex guide02-wsdl2py.tex
+
+M= zsi
+G= guide
+
+SOURCES= $M.tex version.tex $(M_CHAPTERS)
+GSOURCES= $G.tex version.tex $(G_CHAPTERS)
+
+
+all: ps pdf html
+pdf: $M.pdf $G.pdf
+ps: $M.ps $G.ps
+html: $M.html $G.html
+.PHONY: all pdf ps html
+
+$M.ps: $(SOURCES)
+ $(MKHOWTO) --dvi $M.tex
+ dvips -N0 -t $(PAPER) -o $@ $M.dvi
+ rm $M.dvi
+
+$G.ps: $(GSOURCES)
+ $(MKHOWTO) --dvi $G.tex
+ dvips -N0 -t $(PAPER) -o $@ $G.dvi
+ rm $G.dvi
+
+$M.pdf: $(SOURCES)
+ $(MKHOWTO) --pdf $M.tex
+
+$G.pdf: $(GSOURCES)
+ $(MKHOWTO) --pdf $G.tex
+
+$M.html: $(SOURCES)
+ $(MKHOWTO) --html --split=1 $M.tex
+ mv $M/$M.html $M/$M.css .
+ rm -rf $M zsi.l2h
+
+$G.html: $(GSOURCES)
+ $(MKHOWTO) --html --split=1 $G.tex
+ mv $G/$G.html $G/$G.css .
+ rm -rf $G zsi.l2h
+
+#zsixsd.tex: zsi.xsd
+# sed -e '1,/-->/d' zsi.xsd | expand >$@
+
+clean:
+ rm -f *.aux *.idx *.ilg *.ind *.log *.toc *.bkm *.syn *.pla *.how
+clobber: clean
+ rm -f $M.ps $M.pdf $M.html $M.l2h $M.css $G.ps $G.pdf $G.html zsixsd.tex
+realclean: clobber
+distclean: clobber
+.PHONY: clean clobber realclean distclean
diff --git a/doc/app-dispatch-dict.tex b/doc/app-dispatch-dict.tex
new file mode 100644
index 0000000..124d701
--- /dev/null
+++ b/doc/app-dispatch-dict.tex
@@ -0,0 +1,23 @@
+\chapter{CGI Script Struct}
+
+\section{Intro} This is an example of a simple web service CGI Script. The service
+returns and expects SOAP Structs (python \class{dict}). A sample soap trace is
+provided below. In this example the CGI script is dispatched as a \emph{rpc}
+service.
+
+\subsection{rpc wrapper} The wrapper element of the request is the dispatch key
+to the callback function, the child elements are passes as a \class{list} or
+\class{dict} of values to the callback function. The callback function is
+expected to return a \class{list} or \class{dict} of values, the response
+wrapper is by default set to the request wrapper name appended \emph{Response}.
+
+\section{CGI Script}
+\input{dispatch-rpc-hello-struct}
+
+\section{client test script}
+\input{client-hello-struct}
+
+\section{SOAP Trace}
+\subsection{hello}
+\begin{verbatim}
+\end{verbatim}
diff --git a/doc/app-dispatch.tex b/doc/app-dispatch.tex
new file mode 100644
index 0000000..379ee23
--- /dev/null
+++ b/doc/app-dispatch.tex
@@ -0,0 +1,231 @@
+\chapter{CGI Script Array}
+
+\section{Intro} This is an example of a simple web service CGI Script. The
+service returns and expects SOAP Arrays (python \class{list}). A sample soap
+trace is provided below. In this example the CGI script is dispatched as a
+\emph{rpc} service.
+
+\subsection{rpc wrapper} The wrapper element of the request is the dispatch key
+to the callback function, the child elements are passes as a \class{list} or
+\class{dict} of values to the callback function. The callback function is
+expected to return a \class{list} or \class{dict} of values, the response
+wrapper is by default set to the request wrapper name appended \emph{Response}.
+
+\section{CGI Script}
+
+\input{dispatch-rpc-hello-array}
+
+\section{client test script}
+\begin{verbatim}
+#!/usr/bin/env python
+# client.py
+import sys
+from ZSI.client import Binding
+b = Binding(url='http://127.0.0.1/cgi-bin/simple', tracefile=sys.stdout)
+print b.hello()
+try:
+ print b.hello(1)
+except Exception, ex:
+ print "Fault: ", ex
+
+print b.echo("whatever", "hi", 1, 2)
+print b.sum(*[2*i for i in range(5)])
+print b.average(*[2*i for i in range(5)])
+\end{verbatim}
+
+\section{SOAP Trace}
+\subsection{hello}
+\begin{verbatim}
+$ ./client.py
+Hello: _________________________________ Wed Oct 4 17:36:33 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <hello SOAP-ENC:arrayType="xsd:anyType[0]"></hello>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+_________________________________ Wed Oct 4 17:36:34 2006 RESPONSE:
+200
+
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <helloResponse SOAP-ENC:arrayType="xsd:anyType[1]">
+ <element id="o671b0" xsi:type="xsd:string">Hello, world</element>
+ </helloResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+[u'Hello, world']
+\end{verbatim}
+
+\subsection{hello fault}
+\begin{verbatim}
+_________________________________ Wed Oct 4 17:36:34 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <hello SOAP-ENC:arrayType="xsd:anyType[1]">
+ <element id="o1803988" xsi:type="xsd:int">1</element>
+ </hello>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+_________________________________ Wed Oct 4 17:36:35 2006 RESPONSE:
+500
+
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <SOAP-ENV:Fault>
+ <faultcode>SOAP-ENV:Server</faultcode>
+ <faultstring>Processing Failure</faultstring>
+ <detail>
+ <ZSI:FaultDetail>
+<ZSI:string>exceptions:TypeError hello() takes no arguments (1 given)</ZSI:string>
+<ZSI:trace>build/bdist.darwin-8.8.0-Power_Macintosh/egg/ZSI/dispatch.py:86:_Dispatch</ZSI:trace>
+ </ZSI:FaultDetail>
+ </detail>
+ </SOAP-ENV:Fault>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+Fault: Processing Failure
+exceptions:TypeError
+hello() takes no arguments (1 given)
+[trace: build/bdist.darwin-8.8.0-Power_Macintosh/egg/ZSI/dispatch.py:86:_Dispatch]
+\end{verbatim}
+
+\subsection{echo}
+\begin{verbatim}
+_________________________________ Wed Oct 4 17:36:35 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <echo SOAP-ENC:arrayType="xsd:anyType[4]">
+ <element id="o644c0" xsi:type="xsd:string">whatever</element>
+ <element id="o644e0" xsi:type="xsd:string">hi</element>
+ <element id="o1803988" xsi:type="xsd:int">1</element>
+ <element id="o180397c" xsi:type="xsd:int">2</element>
+ </echo>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+_________________________________ Wed Oct 4 17:36:36 2006 RESPONSE:
+200
+
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <echoResponse SOAP-ENC:arrayType="xsd:anyType[4]">
+ <element id="o4f4290" xsi:type="xsd:string">whatever</element>
+ <element id="o4f4338" xsi:type="xsd:string">hi</element>
+ <element id="o1803988" xsi:type="xsd:int">1</element>
+ <element id="o180397c" xsi:type="xsd:int">2</element>
+ </echoResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+[u'whatever', u'hi', 1, 2]
+\end{verbatim}
+
+\subsection{sum}
+\begin{verbatim}
+_________________________________ Wed Oct 4 17:36:36 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <sum SOAP-ENC:arrayType="xsd:anyType[5]">
+ <element id="o1803994" xsi:type="xsd:int">0</element>
+ <element id="o180397c" xsi:type="xsd:int">2</element>
+ <element id="o1803964" xsi:type="xsd:int">4</element>
+ <element id="o180394c" xsi:type="xsd:int">6</element>
+ <element id="o1803934" xsi:type="xsd:int">8</element>
+ </sum>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+_________________________________ Wed Oct 4 17:36:37 2006 RESPONSE:
+200
+
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <sumResponse SOAP-ENC:arrayType="xsd:anyType[1]">
+ <element id="o18038a4" xsi:type="xsd:int">20</element>
+ </sumResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+[20]
+\end{verbatim}
+
+\subsection{average}
+\begin{verbatim}
+_________________________________ Wed Oct 4 17:36:37 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <average SOAP-ENC:arrayType="xsd:anyType[5]">
+ <element id="o1803994" xsi:type="xsd:int">0</element>
+ <element id="o180397c" xsi:type="xsd:int">2</element>
+ <element id="o1803964" xsi:type="xsd:int">4</element>
+ <element id="o180394c" xsi:type="xsd:int">6</element>
+` <element id="o1803934" xsi:type="xsd:int">8</element>
+ </average>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+_________________________________ Wed Oct 4 17:36:38 2006 RESPONSE:
+200
+
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <averageResponse SOAP-ENC:arrayType="xsd:anyType[1]">
+ <element id="o1803964" xsi:type="xsd:int">4</element>
+ </averageResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+[4]
+\end{verbatim}
diff --git a/doc/app-lowlevel.tex b/doc/app-lowlevel.tex
new file mode 100644
index 0000000..a20ba04
--- /dev/null
+++ b/doc/app-lowlevel.tex
@@ -0,0 +1,227 @@
+\chapter{Complete Low Level Example}
+
+\section{Intro} This is a complete example of using the low level soap utilities
+in \ZSI{} to implement a web service.
+
+\section{code}
+\subsection{httpserver script} Minimal http server example, opens up a new
+process to do the SOAP processing.
+\begin{verbatim}
+#!/usr/bin/env python
+# file: httpserver.py
+import os
+from subprocess import Popen, PIPE
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+
+class RequestHandler(BaseHTTPRequestHandler):
+ def do_POST(self):
+ length = int(self.headers['content-length'])
+ xml_in = self.rfile.read(length)
+ p = Popen(os.path.join(os.path.curdir, 'player.py'),
+ shell=True, stdin=PIPE, stdout=PIPE)
+
+ (stdout, stderr) = p.communicate(xml_in)
+ code = 200
+ if stdout.find('Fault') >= 0: code = 500
+ self.send_response(code)
+ self.send_header('Content-type', 'text/xml; charset="utf-8"')
+ self.send_header('Content-Length', str(len(stdout)))
+ self.end_headers()
+ self.wfile.write(stdout)
+ self.wfile.flush()
+
+if __name__ == '__main__':
+ server = HTTPServer(('localhost', 8080), RequestHandler)
+ server.serve_forever()
+\end{verbatim}
+
+\subsection{typecode module}
+\begin{verbatim}
+# file: typecode.py
+# CHECK PYTHONPATH: Must be able to import
+class Player:
+ def __init__(self, *args):
+ if not len(args): return
+ self.Name = args[0]
+ self.Scores = args[1:]
+Player.typecode = TC.Struct(Player, [
+ TC.String('Name'),
+ TC.Array('Integer', TC.Integer(), 'Scores', undeclared=True),
+ ], 'GetAverage')
+class Average:
+ def __init__(self, average=None):
+ self.average = average
+Average.typecode = TC.Struct(Average, [
+ TC.Integer('average'),
+ ], 'GetAverageResponse')
+\end{verbatim}
+\subsection{player script}
+\begin{verbatim}
+#!/usr/bin/env python
+# file: player.py
+from ZSI import *
+import sys
+IN, OUT = sys.stdin, sys.stdout
+try:
+ ps = ParsedSoap(IN)
+except ParseException, e:
+ OUT.write(FaultFromZSIException(e).AsSOAP())
+ sys.exit(1)
+except Exception, e:
+ # Faulted while processing; we assume it's in the header.
+ OUT.write(FaultFromException(e, 1).AsSOAP())
+ sys.exit(1)
+
+# We are not prepared to handle any actors or mustUnderstand elements,
+# so we'll arbitrarily fault back with the first one we found.
+a = ps.WhatActorsArePresent()
+if len(a):
+ OUT.write(FaultFromActor(a[0]).AsSOAP())
+ sys.exit(1)
+mu = ps.WhatMustIUnderstand()
+if len(mu):
+ uri, localname = mu[0]
+ OUT.write(FaultFromNotUnderstood(uri, localname).AsSOAP())
+ sys.exit(1)
+
+from typecode import Player, Average
+try:
+ player = ps.Parse(Player.typecode)
+except EvaluateException, e:
+ OUT.write(FaultFromZSIException(e).AsSOAP())
+ sys.exit(1)
+
+try:
+ total = 0
+ for value in player.Scores: total = total + value
+ result = Average(total / len(player.Scores))
+ sw = SoapWriter()
+ sw.serialize(result, Average.typecode)
+ sw.close()
+ OUT.write(str(sw))
+except Exception, e:
+ OUT.write(FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP())
+ sys.exit(1)
+\end{verbatim}
+
+\subsection{client test script}
+\begin{verbatim}
+#!/usr/bin/env python2.4
+#file: client.py
+from ZSI import *
+from ZSI.wstools.Namespaces import SCHEMA
+from typecode import Player, Average
+
+if __name__ == '__main__':
+ import sys
+ from ZSI.client import Binding
+ b = Binding(url='http://localhost:8080', tracefile=sys.stdout)
+ pyobj = b.RPC(None, None, Player("Josh",10,20,30), replytype=Average)
+ print pyobj
+ print pyobj.__dict__
+\end{verbatim}
+
+\section{SOAP Trace}
+
+\subsection{GetAverage}
+\begin{verbatim}
+$./client.py
+_________________________________ Thu Oct 5 14:57:39 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <GetAverage>
+ <Name xsi:type="xsd:string">Josh</Name>
+ <Scores>
+ <element>10</element>
+ <element>20</element>
+ <element>30</element>
+ </Scores>
+ </GetAverage>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+_________________________________ Thu Oct 5 14:57:39 2006 RESPONSE:
+200
+OK
+-------
+Server: BaseHTTP/0.3 Python/2.5
+Date: Thu, 05 Oct 2006 21:57:39 GMT
+Content-type: text/xml; charset="utf-8"
+Content-Length: 431
+
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <GetAverageResponse>
+ <average>20</average>
+ </GetAverageResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+<__main__.Average instance at 0x5f9760>
+{'average': 20}
+\end{verbatim}
+
+\subsection{fault} Purposely send a incorrect \emph{Nae} element for the
+\emph{Name}.
+\begin{verbatim}
+$./client.py
+_________________________________ Thu Oct 5 14:33:25 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <GetAverage>
+ <Nae xsi:type="xsd:string">Josh</Nae>
+ <Scores>
+ <element>10</element>
+ <element>20</element>
+ <element>30</element>
+ </Scores>
+ </GetAverage>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+_________________________________ Thu Oct 5 14:33:26 2006 RESPONSE:
+500
+Internal Server Error
+
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <SOAP-ENV:Fault>
+ <faultcode>SOAP-ENV:Client</faultcode>
+ <faultstring>Unparseable message</faultstring>
+ <detail><Eoe440>&lt;ZSI:ParseFaultDetail&gt;
+&lt;ZSI:string&gt;Element "Name" missing from complexType&lt;/ZSI:string&gt;
+&lt;ZSI:trace&gt;/SOAP-ENV:Envelope/SOAP-ENV:Body/GetAverage&lt;/ZSI:trace&gt;
+&lt;/ZSI:ParseFaultDetail&gt;</Eoe440></detail>
+ </SOAP-ENV:Fault>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+Traceback (most recent call last):
+ File "./player_client.py", line 25, in ?
+ pyobj = b.RPC(None, None, Player("Josh",10,20,30), replytype=Average)
+ File "/private/var/www/htdocs/guide/client.py", line 176, in RPC
+
+ File "/private/var/www/htdocs/guide/client.py", line 420, in Receive
+
+ZSI.FaultException: Unparseable message
+<Element Node at 5f9f58: Name='Eoe440' with 0 attributes and 1 children>
+\end{verbatim}
+
diff --git a/doc/app-pickler.tex b/doc/app-pickler.tex
new file mode 100644
index 0000000..17ca9fa
--- /dev/null
+++ b/doc/app-pickler.tex
@@ -0,0 +1,212 @@
+\chapter{pickler example}
+
+\section{Intro} This is an example of a stateful mod_python web service.
+
+\section{code}
+\subsection{typecode module} Module containing complex type typecode.
+\begin{verbatim}
+# Complex type definition
+from ZSI import *
+class Person:
+ def __init__(self, name=None, age=0):
+ self.name = name
+ self.age = age
+
+Person.typecode = TC.Struct(Person,
+ [TC.String('name'),
+ TC.InonNegativeInteger('age')],
+ pname=('urn:MyApp','Person'))
+\end{verbatim}
+\subsection{pickler script} Configure appache to use this script with mod_python
+PythonHandler.
+\begin{verbatim}
+# pickler.py
+import pickle, new
+from mod_python import apache
+from ZSI import dispatch
+import MyComplexTypes
+
+# my web service that returns a complex structure
+def getPerson(name=None):
+ #fp = open('/tmp/%s.person.pickle'%Person.name, 'r')
+ fp = open('/tmp/%s.person.pickle'%name, 'r')
+ #return pickle.load(fp)
+ p = pickle.load(fp)
+ print "PERSON: ", p
+ print "typecode: ", p.typecode
+ return p
+
+# my web service that accepts a complex structure
+def savePerson(Person):
+ print "PERSON: ", Person
+ fp = open('/tmp/%s.person.pickle'%Person.name, 'w')
+ pickle.dump(Person, fp)
+ fp.close()
+ return {}
+
+mod = __import__('encodings.utf_8', globals(), locals(), '*')
+mod = __import__('encodings.utf_16_be', globals(), locals(), '*')
+
+
+handles = new.module('handles')
+handles.getPerson = getPerson
+handles.savePerson = savePerson
+def handler(req):
+ dispatch.AsHandler(modules=(handles,), request=req, typesmodule=MyComplexTypes, rpc=True)
+ return apache.OK
+
+\end{verbatim}
+
+\subsection{client: invoke savePerson}
+\subsubsection{script}
+\begin{verbatim}
+import sys
+from ZSI.client import Binding
+from MyComplexTypes import Person
+
+b = Binding(url='http://localhost/test3/pickler.py', tracefile=sys.stdout)
+person = Person('christopher', 26)
+b.savePerson(person)
+\end{verbatim}
+
+\subsubsection{SOAP Trace}
+\begin{verbatim}
+_________________________________ Wed Oct 11 13:10:05 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <savePerson xmlns:ns1="urn:MyApp">
+ <ns1:Person><name xsi:type="xsd:string">christopher</name>
+ <age xsi:type="xsd:nonNegativeInteger">26</age>
+ </ns1:Person>
+ </savePerson>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+_________________________________ Wed Oct 11 13:10:05 2006 RESPONSE:
+Server: Apache/2.0.53-dev (Unix) mod_ruby/1.2.4 Ruby/1.8.2(2004-12-25)
+mod_python/3.1.4 Python/2.4.1
+Transfer-Encoding: chunked
+Content-Type: text/xml
+
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <savePersonResponse></savePersonResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+\end{verbatim}
+
+\subsection{client: invoke getPerson 3 different ways}
+\subsubsection{script}
+\begin{verbatim}
+import sys
+import MyComplexTypes
+from ZSI.client import NamedParamBinding as NPBinding, Binding
+from ZSI import TC
+
+kw = {'url':'http://localhost/test3/pickler.py', 'tracefile':sys.stdout}
+b = NPBinding(**kw)
+rsp = b.getPerson(name='christopher')
+assert type(rsp) is dict, 'expecting a dict'
+assert rsp['Person']['name'] == 'christopher', 'wrong person'
+
+b = NPBinding(typesmodule=MyComplexTypes, **kw)
+rsp = b.getPerson(name='christopher')
+assert isinstance(rsp['Person'], MyComplexTypes.Person), (
+ 'expecting instance of %s' %MyComplexTypes.Person)
+
+b = Binding(typesmodule=MyComplexTypes, **kw)
+class Name(str):
+ typecode = TC.String("name")
+
+rsp = b.getPerson(Name('christopher'))
+assert isinstance(rsp['Person'], MyComplexTypes.Person), (
+ 'expecting instance of %s' %MyComplexTypes.Person)
+\end{verbatim}
+
+\subsubsection{SOAP Trace} All responses are exactly the same, for comparison
+the three requests are presented first and only the last response is included.
+\begin{verbatim}
+_________________________________ Wed Oct 11 13:19:00 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <getPerson>
+ <name id="o6c2a0" xsi:type="xsd:string">christopher</name>
+ </getPerson>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+** OMIT RESPONSE **
+
+_________________________________ Wed Oct 11 13:19:00 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <getPerson>
+ <name id="o6c2a0" xsi:type="xsd:string">christopher</name>
+ </getPerson>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+** OMIT RESPONSE **
+
+_________________________________ Wed Oct 11 13:19:00 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <getPerson>
+ <name xsi:type="xsd:string">christopher</name>
+ </getPerson>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+_________________________________ Wed Oct 11 13:19:00 2006 RESPONSE:
+Server: Apache/2.0.53-dev (Unix) mod_ruby/1.2.4 Ruby/1.8.2(2004-12-25)
+mod_python/3.1.4 Python/2.4.1
+Transfer-Encoding: chunked
+Content-Type: text/xml
+
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <getPersonResponse xmlns:ns1="urn:MyApp">
+ <ns1:Person>
+ <name xsi:type="xsd:string">christopher</name>
+ <age xsi:type="xsd:nonNegativeInteger">26</age>
+ </ns1:Person>
+ </getPersonResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+\end{verbatim}
+
diff --git a/doc/bibliography.tex b/doc/bibliography.tex
new file mode 100644
index 0000000..0134c96
--- /dev/null
+++ b/doc/bibliography.tex
@@ -0,0 +1,5 @@
+\chapter{Bibliography}
+\begin{thebibliography}{99}
+\bibitem{ref1} This is the first item in the Bibliography.
+\bibitem{ref2} This is the second item in the Bibliography.
+\end{thebibliography}
diff --git a/doc/blank.png b/doc/blank.png
new file mode 100644
index 0000000..2af5639
--- /dev/null
+++ b/doc/blank.png
Binary files differ
diff --git a/doc/c01-intro.tex b/doc/c01-intro.tex
new file mode 100644
index 0000000..f7e181c
--- /dev/null
+++ b/doc/c01-intro.tex
@@ -0,0 +1,98 @@
+\chapter{Introduction}
+
+\ZSI{}, the Zolera SOAP Infrastructure, is a Python package that
+provides an implementation of the SOAP specification, as described in
+\citetitle[http://www.w3.org/TR/soap]{The SOAP 1.1 Specification}.
+In particular, \ZSI{} parses and generates SOAP messages, and
+converts between native Python datatypes and SOAP syntax.
+
+\module{ZSI} requires Python 2.3 or later and PyXML version 0.8.3 or later.
+
+The \module{ZSI} project is maintained at SourceForge, at
+\url{http://pywebsvcs.sf.net}.
+\ZSI{} is discussed on the Python web services mailing list, visit
+\url{http://lists.sourceforge.net/lists/listinfo/pywebsvcs-talk}
+to subscribe.
+
+For those interested in using the \program{wsdl2py} tool see the \emph{Users
+Guide}, it contains a detailed example of how to use the code generation
+facilities in \module{ZSI}.
+
+For those interested in a high-level tutorial covering \ZSI{} and why
+Python was chosen, see the article
+\url{http://www.xml.com/pub/a/ws/2002/06/12/soap.html},
+written by Rich Salz for xml.com.
+
+SOAP-based processing typically involves several steps.
+The following list details the steps of a common processing model naturally
+supported by \ZSI{} (other models are certainly possible):
+\begin{enumerate}
+\item
+ \ZSI{} takes data from an input stream and \emph{parses} it, generating
+ a DOM-based parse tree as part of creating a \class{ParsedSoap} object.
+ At this point the major syntactic elements of a SOAP message --- the
+ \code{Header}, the \code{Body}, etc. --- are available.
+\item
+ The application does \emph{header processing}.
+ More specifically, it does local dispatch and processing based on
+ the elements in the SOAP \code{Header}.
+ The SOAP \code{actor} and \code{mustUnderstand} attributes are
+ also handled (or at least recognized) here.
+\item
+ \ZSI{} next \emph{parses} the \code{Body}, creating local Python objects
+ from the data in the SOAP message.
+ The parsing is often under the control of a list of data descriptions,
+ known as \emph{typecodes}, defined by the application because it knows
+ what type of data it is expecting.
+ In cases where the SOAP data is known to be completely self-describing,
+ the parsing can be \emph{dynamic} through the use of the \class{TC.Any}
+ class.
+\item
+ The application now \emph{dispatches} to the appropriate handler
+ in order to do its ``real work.''
+ As part of its processing it may create \emph{output objects}
+\item
+ The application creates a \class{SoapWriter} instance and outputs
+ an initial set of namespace entries and header elements.
+\item
+ Any local data to be sent back to the client is \emph{serialized}.
+ As with \code{Body} parsing, the datatypes can be described through
+ typecodes or determined dynamically (here, through introspection).
+\item
+ In the event of any processing exceptions, a \class{Fault} object
+ can be raised, created, and/or serialized.
+\end{enumerate}
+
+Note that \ZSI{} is ``transport neutral'', and provides only a simple
+I/O and dispatch framework; a more complete solution is available through
+the use of included WSDL tools (\program{wsdl2py}), but otherwise this is
+the responsibility of the application using \ZSI{}. As usage patterns
+emerge, and common application frameworks are more understood, this may
+change.
+
+
+Within this document, \code{tns} is used as the prefix for the
+application's target namespace, and the term
+\emph{element} refers to a DOM element node.)
+
+\section{How to Read this Document}
+
+Readers interested in using WSDL and clients to web services, and those
+intending on implementing web services based on existing WSDL should refer
+to the \emph{Users Guide}. Others interested in developing the simplest SOAP
+applications, or spending the least amount of time on building a web services
+infrastructure should read chapters 2, 3, and 10 of this document. Readers who
+are developing complex services, and who are familiar with XML Schema and/or
+WSDL, should read this manual in order. This will provide them with enough
+information to implement the processing model described above. They can skip
+probably skip chapters 2 and 10.
+
+\ZSI{} has the capability to process WSDL definitions and XML Schema documents
+(described in \citetitle[http://www.w3.org/TR/wsdl]{The Web Services Description
+Language} and \citetitle[http://www.w3.org/XML/Schema]{XMLSchema 1.0})
+and generate typecodes automatically. For more information see the \emph{Users
+Guide}.
+
+
+
+
diff --git a/doc/c02-samples.tex b/doc/c02-samples.tex
new file mode 100644
index 0000000..d1de776
--- /dev/null
+++ b/doc/c02-samples.tex
@@ -0,0 +1,275 @@
+\chapter{Examples}
+
+This chapter contains a number of examples to show off some of \ZSI{}'s
+features. It is broken down into client-side and server-side examples, and
+explores different implementation options \ZSI{} provides.
+
+\section{Server Side Examples}
+\subsection{Simple example}
+Using the \module{ZSI.dispatch} module, it is simple to expose Python functions
+as web services. Each function is invoked with all the input parameters
+specified in the client's SOAP request. Any value returned by the function will
+be serialized back to the client; multiple values can be returned by returning a
+tuple.
+
+The following code shows some simple services:
+
+\input{dispatch-rpc-hello-array}
+
+Each function defines a SOAP request, so if this script is installed
+as a CGI script, a SOAP message can be posted to that script's URL with any of
+\code{hello}, \code{echo}, or \code{average} as the request element,
+and the value returned by the function will be sent back. These functions
+expect and return SOAP-ENC:arrayType instances which are marshalled into python
+\class{list} instances, this script interoperates with the
+\class{client.Binding}. For more information see \emph{Appendix A}.
+
+The \ZSI{} CGI dispatcher catches exceptions and sends back a SOAP fault.
+For example, a fault will be sent if the \code{hello} function is given any
+arguments, or if the \code{average} function is given a non-integer.
+
+Here is another example but using SOAP-ENC:Struct instances which are marshalled
+into python \class{dict} instances, this script interoperates with the
+\class{client.NamedParamBinding}. For more information see \emph{Appendix B}.
+
+\input{dispatch-rpc-hello-struct}
+
+\subsection{low level soap processing example}
+
+We will now show a more complete example of a robust web service implemented at
+the SOAP layer. It takes as input a player name and array of integers, and returns
+the average. It is presented in sections, following the steps detailed above.
+A complete working example of this service is available in \emph{Appendix C}.
+
+The first section reads in a request, and parses the SOAP header.
+
+\begin{verbatim}
+from ZSI import *
+import sys
+IN, OUT = sys.stdin, sys.stdout
+try:
+ ps = ParsedSoap(IN)
+except ParseException, e:
+ OUT.write(FaultFromZSIException(e).AsSOAP())
+ sys.exit(1)
+except Exception, e:
+ # Faulted while processing; we assume it's in the header.
+ OUT.write(FaultFromException(e, 1).AsSOAP())
+ sys.exit(1)
+
+# We are not prepared to handle any actors or mustUnderstand elements,
+# so we'll arbitrarily fault back with the first one we found.
+a = ps.WhatActorsArePresent()
+if len(a):
+ OUT.write(FaultFromActor(a[0]).AsSOAP())
+ sys.exit(1)
+mu = ps.WhatMustIUnderstand()
+if len(mu):
+ uri, localname = mu[0]
+ OUT.write(FaultFromNotUnderstood(uri, localname).AsSOAP())
+ sys.exit(1)
+\end{verbatim}
+
+This section defines the mappings between Python objects and the SOAP
+data being transmitted. Recall that according to the SOAP specification, RPC
+input and output are modeled as a structure.
+
+\begin{verbatim}
+class Player:
+ def __init__(self, *args):
+ if not len(args): return
+ self.Name = args[0]
+ self.Scores = args[1:]
+Player.typecode = TC.Struct(Player, [
+ TC.String('Name'),
+ TC.Array('Integer', TC.Integer(), 'Scores', undeclared=True),
+ ], 'GetAverage')
+class Average:
+ def __init__(self, average=None):
+ self.average = average
+Average.typecode = TC.Struct(Average, [
+ TC.Integer('average'),
+ ], 'GetAverageResponse')
+\end{verbatim}
+
+This section parses the input, performs the application-level
+activity, and serializes the response.
+\begin{verbatim}
+try:
+ player = ps.Parse(Player.typecode)
+except EvaluateException, e:
+ OUT.write(FaultFromZSIException(e).AsSOAP())
+ sys.exit(1)
+
+try:
+ total = 0
+ for value in player.Scores: total = total + value
+ result = Average(total / len(player.Scores))
+ sw = SoapWriter()
+ sw.serialize(result, Average.typecode)
+ sw.close()
+ OUT.write(str(sw))
+except Exception, e:
+ OUT.write(FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP())
+ sys.exit(1)
+\end{verbatim}
+
+In the \method{serialize()} call above, the second parameter is optional, since
+\code{result} is an instance of the \class{Average} class, and the
+\code{Average.typecode} attribute is the typecode for class instances.
+
+
+\subsection{A mod_python example}
+
+The Apache module \code{mod_python} (see
+\url{http://www.modpython.org}) embeds Python within the Apache server.
+In order to expose operations within a module via mod_python, use the
+\method{dispatch.AsHandler()} function. The \method{dispatch.AsHandler()}
+function will dispatch requests to any operation defined in the module you
+pass it, which allows for multiple operations to be defined in a module.
+The only trick is to use __import__ to load the XML encodings your service
+expects. This is a required workaround to avoid the pitfalls of restricted
+execution with respect to XML parsing.
+
+The following is a complete example of a simple handler. The soap operations
+are implemented in the MyHandler module:
+
+\begin{verbatim}
+def hello():
+ return {"value":"Hello, world"}
+
+def echo(**kw):
+ return kw
+
+def sum(**kw):
+ sum = 0
+ for i in kw.values(): sum += i
+ return {"value":sum}
+
+def average(**kw):
+ d = sum(**kw)
+ d["value"] = d["value"]/len(kw)
+ return d
+\end{verbatim}
+
+Dispatching from within mod_python is achieved by passing the aforementined
+MyHandler module to \code{dispatch.AsHandler()}. The following code exposes
+the operations defined in MyHandler via SOAP:
+
+\begin{verbatim}
+from ZSI import dispatch
+from mod_python import apache
+
+import MyHandler
+mod = __import__('encodings.utf_8', globals(), locals(), '*')
+mod = __import__('encodings.utf_16_be', globals(), locals(), '*')
+
+def handler(req):
+ dispatch.AsHandler(modules=(MyHandler,), request=req)
+ return apache.OK
+\end{verbatim}
+
+
+\section{Client Side Examples}
+
+\subsection{Simple Example}
+\ZSI{} provides two ways for a client to interactive with a server:
+the \class{Binding} or \class{NamedParamBinding} class and the
+\class{ServiceProxy} class. The first is useful when the operations to be
+invoked are not defined in WSDL or when only simple Python datatypes are used;
+the \class{ServiceProxy} class can be used to parse WSDL definitions in order
+to determine how to serialize and parse the SOAP messages.
+
+During development, it is often useful to record ``packet traces'' of
+the SOAP messages being exchanged. Both the \class{Binding} and
+\class{ServiceProxy} classes provide a \code{tracefile} parameter to specify an
+output stream (such as a file) to capture messages. It can be particularly
+useful when debugging unexpected SOAP faults.
+
+The first example provided below demonstrates how to use the \class{NamedParamBinding}
+class to connect to a remote service and perform an operation.
+
+\input{client-hello-struct}
+
+\subsection{Complex Example: pickler.py}
+If the operation invoked returns a ComplexType, typecode information must
+be provided in order to tell \ZSI{} how to deserialize the response.
+Here is a sample server-side implementation (for the complete example see
+\emph{Appendix D}):
+
+\begin{verbatim}
+class Person:
+ def __init__(self, name=None, age=0):
+ self.name = name
+ self.age = age
+
+Person.typecode = TC.Struct(Person,
+ [TC.String('name'),
+ TC.InonNegativeInteger('age')],
+ 'myApp:Person')
+
+# my web service that returns a complex structure
+def getPerson(name):
+ fp = open('%s.person.pickle', % name, 'r')
+ return pickle.load(fp)
+
+# my web service that accepts a complex structure
+def savePerson(person):
+ fp = open('%s.person.pickle' % person.name, 'w')
+ pickle(person, fp)
+ fp.close()
+\end{verbatim}
+
+In order for \ZSI{} to transparently deserialize the returned complex type into
+a \class{Person} instance, a module defining the class and its typecode can be
+passed into the \class{Binding}. It is also possible to explicitly tell \ZSI{}
+what typecode to use by passing it as a parameter to the \method{Binding.Receive()}
+method.
+
+The following fragment shows both styles:
+
+\begin{verbatim}
+import sys
+from ZSI.client import Binding
+from MyComplexTypes import Person
+
+b = Binding(url='http://localhost/test3/pickler.py', tracefile=sys.stdout)
+person = Person('christopher', 26)
+rsp = b.savePerson(person)
+
+\end{verbatim}
+
+Because the returned complex type is defined in a class present in
+\var{typesmodule}, transparent deserialization is possible. When sending
+complex types to the server, it is not necessary to list the module
+in \var{typesmodule}:
+
+\begin{verbatim}
+import sys
+import MyComplexTypes
+from ZSI.client import NamedParamBinding as NPBinding, Binding
+from ZSI import TC
+
+kw = {'url':'http://localhost/test3/pickler.py', 'tracefile':sys.stdout}
+b = NPBinding(**kw)
+rsp = b.getPerson(name='christopher')
+assert type(rsp) is dict, 'expecting a dict'
+assert rsp['Person']['name'] == 'christopher', 'wrong person'
+
+b = NPBinding(typesmodule=MyComplexTypes, **kw)
+rsp = b.getPerson(name='christopher')
+assert isinstance(rsp['Person'], MyComplexTypes.Person), (
+ 'expecting instance of %s' %MyComplexTypes.Person)
+
+b = Binding(typesmodule=MyComplexTypes, **kw)
+class Name(str):
+ typecode = TC.String("name")
+
+rsp = b.getPerson(Name('christopher'))
+assert isinstance(rsp['Person'], MyComplexTypes.Person), (
+ 'expecting instance of %s' %MyComplexTypes.Person)
+
+\end{verbatim}
+
+
+
diff --git a/doc/c02a-coverage.tex b/doc/c02a-coverage.tex
new file mode 100644
index 0000000..13ee8eb
--- /dev/null
+++ b/doc/c02a-coverage.tex
@@ -0,0 +1,38 @@
+\chapter{Serialization and Deserialization -- capabilities defined}
+
+ZSI supports serialization and deserialization of most Python constructs,
+including those defined by an end user. The following charts outline the
+capabilities ZSI provides, and are described from the perspective of the
+client developer and server developer for clarity.
+
+\section{Server Side Conversions}
+\module{ZSI} provides the server side capabilities outlined below:
+
+\begin{tableiii}{l|c|c}{textrm}{Type}{Response}{Request}
+\lineiii{string}{yes}{yes}
+\lineiii{float}{unknown}{unknown}
+\lineiii{long}{unknown}{unknown}
+\lineiii{double}{unknown}{unknown}
+\lineiii{no parameters or returns}{yes}{yes}
+\lineiii{complex types}{yes}{yes}
+\lineiii{array of complex types}{yes}{unknown}
+\lineiii{tuple of complex types}{unknown}{unknown}
+\lineiii{dict of complex types}{unknown}{unknown}
+\end{tableiii}
+
+
+\section{Client Side Conversions}
+\module{ZSI} provides the client side capabilities outlined below:
+
+\begin{tableiii}{l|c|c}{textrm}{Type}{Response}{Request}
+\lineiii{string}{yes}{yes}
+\lineiii{float}{unknown}{unknown}
+\lineiii{long}{unknown}{unknown}
+\lineiii{double}{unknown}{unknown}
+\lineiii{no parameters or returns}{yes}{yes}
+\lineiii{complex types}{yes}{yes}
+\lineiii{array of complex types}{yes}{unknown}
+\lineiii{tuple of complex types}{unknown}{unknown}
+\lineiii{dict of complex types}{unknown}{unknown}
+\end{tableiii}
+
diff --git a/doc/c03-except.tex b/doc/c03-except.tex
new file mode 100644
index 0000000..eca7b20
--- /dev/null
+++ b/doc/c03-except.tex
@@ -0,0 +1,49 @@
+\chapter{Exceptions}
+
+\begin{excdesc}{ZSIException}
+Base class for all ZSI Exceptions, it is a subtype of the Python
+\exception{Exception} class.
+\end{excdesc}
+
+\begin{excdesc}{ParseException}
+\ZSI{} can raise this exception while creating a \class{ParsedSoap} object.
+It is a subtype of the \exception{ZSIException} class.
+The string form of a \exception{ParseException} object consists of a
+line of human-readable text.
+If the backtrace is available, it will be concatenated as a second line.
+\end{excdesc}
+
+The following attributes are read-only:
+
+\begin{memberdesc}{inheader}
+A boolean that indicates if the error was detected in the SOAP \code{Header}
+element.
+\end{memberdesc}
+
+\begin{memberdesc}{str}
+A text string describing the error.
+\end{memberdesc}
+
+\begin{memberdesc}{trace}
+A text string containing a backtrace to the error.
+This may be \code{None} if it was not possible, such as when there was
+a general DOM exception, or when the \code{str} text is believed to be
+sufficient.
+\end{memberdesc}
+
+\begin{excdesc}{EvaluateException}
+This exception is similar to \exception{ParseException}, except
+that \ZSI{} may raise it while converting between SOAP and local
+Python objects.
+\end{excdesc}
+
+The following attributes are read-only:
+
+\begin{memberdesc}{str}
+A text string describing the error.
+\end{memberdesc}
+
+\begin{memberdesc}{trace}
+A text backtrace, as described above for \exception{ParseException}.
+\end{memberdesc}
+
diff --git a/doc/c04-utils.tex b/doc/c04-utils.tex
new file mode 100644
index 0000000..39b53f6
--- /dev/null
+++ b/doc/c04-utils.tex
@@ -0,0 +1,128 @@
+\chapter{Utilities}
+
+\ZSI{} defines some utility methods that general applications
+may want to use.
+
+\begin{funcdesc}{Version}{}
+Returns a three-element tuple containing the numbers representing the
+major, minor, and release identifying the \ZSI{} version.
+\versionadded{1.1}
+\end{funcdesc}
+
+\section{Low-Level Utilities}
+
+\ZSI{} also defines some low-level utilities for its own use that
+start with a leading underscore and must be imported explicitly.
+They are documented here because they can be useful for developing
+new typecode classes.
+
+These functions are mostly used in in \code{parse} methods and the
+\class{ParsedSoap} class. The serialization routines use the
+\class{ElementProxy} class to encapsulate common DOM-level operations.
+
+Some \code{lambda}'s are defined so that some DOM accessors
+will return an empty list rather than \code{None}.
+This means that rather than writing:
+\begin{verbatim}
+if elt.childNodes:
+ for N in elt.childNodes:
+ ...
+\end{verbatim}
+One can write:
+\begin{verbatim}
+for N in _children(elt):
+ ...
+\end{verbatim}
+
+Other \code{lambda}'s return SOAP-related attributes from an element,
+or \code{None} if not present.
+
+
+\begin{funcdesc}{_attrs}{element}
+Returns a list of all attributes of the specified \code{element}.
+\end{funcdesc}
+
+\begin{funcdesc}{_backtrace}{elt, dom}
+This function returns a text string that traces a ``path'' from \code{dom},
+a DOM root, to \code{elt}, an element within that document, in
+XPath syntax.
+\end{funcdesc}
+
+\begin{funcdesc}{_child_elements}{element}
+Returns a list of all children elements of the specified \code{element}.
+\end{funcdesc}
+
+\begin{funcdesc}{_children}{element}
+Returns a list of all children of the specified \code{element}.
+\end{funcdesc}
+
+_copyright
+_empty_nsuri_list
+\begin{funcdesc}{_find_arraytype}{element}
+The value of the SOAP \code{arrayType} attribute.
+\versionadded{1.2}
+\end{funcdesc}
+
+\begin{funcdesc}{_find_attr}{element, name}
+The value of the unqualified \code{name} attribute.
+\end{funcdesc}
+
+\begin{funcdesc}{_find_attrNS}{element, namespaceURI, localName}
+The value of a \code{name} attribute in a namespace \code{namespaceURI}.
+\end{funcdesc}
+
+\begin{funcdesc}{_find_attrNodeNS}{element, namespaceURI, localName}
+Works just like \code{_find_attrNS}, but this function grabs the attribute Node to
+distinquish between an unspecified attribute(None) and one set to empty
+string("").
+\end{funcdesc}
+
+\begin{funcdesc}{_find_default_namespace}{element}
+Returns the value of the default namespace.
+\end{funcdesc}
+
+\begin{funcdesc}{_find_encstyle}{element}
+The value of the SOAP \code{encodingStyle} attribute.
+\end{funcdesc}
+
+\begin{funcdesc}{_find_href}{element}
+The value of the unqualified \code{href} attribute.
+\end{funcdesc}
+
+\begin{funcdesc}{_find_type}{element}
+The value of the XML Schema \code{type} attribute.
+\end{funcdesc}
+
+\begin{funcdesc}{_find_xmlns_prefix}{element, prefix}
+The value of the xmlns:prefix \code{type} attribute.
+\end{funcdesc}
+
+\begin{funcdesc}{_find_xsi_attr}{element, attribute}
+Find the attribute in any of the XMLSchema namespaces.
+\end{funcdesc}
+
+\begin{funcdesc}{_get_element_nsuri_name}{element}
+Returns a \code{(namespace,name)} tuple representing the element tag.
+\end{funcdesc}
+
+\begin{funcdesc}{_get_idstr}{obj}
+Substitute for \function{id} function. Python 2.3.x generates a \class{FutureWarning} for negative
+IDs, so we use a different prefix character to ensure uniqueness, and call abs()
+to avoid the warning.
+\end{funcdesc}
+
+\begin{funcdesc}{_get_postvalue_from_absoluteURI}{url}
+Returns POST value from \code{url}, and caches these values.
+\end{funcdesc}
+
+\begin{funcdesc}{_resolve_prefix}{element, prefix}
+ resolve \code{prefix} to a namespaceURI. If \code{None} or empty \code{str},
+ return default namespace or \code{None} if not defined.
+\end{funcdesc}
+
+\begin{funcdesc}{_valid_encoding}{elt}
+Return true if the element \code{elt} has a SOAP encoding
+that can be handled by \ZSI{}
+(currently Section 5 of the SOAP 1.1 specification or an empty encoding
+for XML).
+\end{funcdesc} \ No newline at end of file
diff --git a/doc/c05-parse.tex b/doc/c05-parse.tex
new file mode 100644
index 0000000..6973d8b
--- /dev/null
+++ b/doc/c05-parse.tex
@@ -0,0 +1,212 @@
+\chapter{The \module{ParsedSoap} module --- basic message handling}
+
+This class represents an input stream that has been parsed as a SOAP
+message.
+
+\begin{classdesc}{ParsedSoap}{input\optional{, **keywords}}
+Creates a \class{ParsedSoap} object from the provided input source.
+If \code{input} is not a string, then it must be an object with a
+\method{read()} method that supports the standard Python ``file read''
+semantics.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{envelope}}{\code{True}}{expect a SOAP Envelope}
+\lineiii{\code{keepdom}}{\code{False}}{Do not release the DOM when this
+object is destroyed. To access the DOM object, use the
+{\method{GetDomAndReader()}} method. The reader object is necessary to
+properly free the DOM structure using {\method{reader.releaseNode(dom)}}.
+\versionadded{1.2}}
+\lineiii{\code{readerclass}}{\code{None}}{Class used to create DOM-creating
+XML readers; described below.
+\versionadded{1.2}}
+\lineiii{\code{resolver}}{\code{None}}{Value for the \code{resolver}
+attribute; see below.}
+\lineiii{\code{trailers}}{\code{False}}{Allow trailing data elements
+to appear after the \code{Body}.}
+\end{tableiii}
+
+\end{classdesc}
+
+The following attributes of a \class{ParsedSoap} are read-only:
+
+\begin{memberdesc}{body}
+The root of the SOAP \code{Body} element.
+Using the \method{GetElementNSdict()} method on this attribute can be useful
+to get a dictionary to be used with the \class{SoapWriter} class.
+\end{memberdesc}
+
+\begin{memberdesc}{body_root}
+The element that contains the SOAP serialization root; that is,
+the element in the SOAP \code{Body} that ``starts off'' the data.
+\end{memberdesc}
+
+\begin{memberdesc}{data_elements}
+A (possibly empty) list of all child elements of the \code{Body} other
+than the root.
+\end{memberdesc}
+
+\begin{memberdesc}{header}
+The root of the SOAP \code{Header} element.
+Using the \method{GetElementNSdict()} method on this attribute can be useful
+to get a dictionary to be used with the \class{SoapWriter} class.
+\end{memberdesc}
+
+\begin{memberdesc}{header_elements}
+A (possibly empty) list of all elements in the SOAP \code{Header}.
+\end{memberdesc}
+
+\begin{memberdesc}{trailer_elements}
+Returns a (possibly empty) list of all elements following the \code{Body}.
+If the \code{trailers} keyword was not used when the object was
+constructed, this attribute will not be instantiated and retrieving
+it will raise an exception.
+\end{memberdesc}
+
+The following attribute may be modified:
+
+\begin{memberdesc}{resolver}
+If not \code{None},
+this attribute can be invoked to handle absolute \code{href}'s in the SOAP data.
+It will be invoked as follows:
+
+\begin{methoddesc}{resolver}{uri, tc, ps, **keywords}
+The \code{uri} parameter is the URI to resolve.
+The \code{tc} parameter is the typecode that needs to resolve \code{href}; this
+may be needed to properly interpret the content of a MIME bodypart, for example.
+The \code{ps} parameter is the \class{ParsedSoap} object that is invoking
+the resolution (this allows a single resolver instance to handle multiple
+SOAP parsers).
+
+Failure to resolve the URI should result in an exception being raised.
+If there is no content, return \code{None}; this is not the same as an
+empty string.
+If there is content, the data returned should be in a form understandable
+by the typecode.
+\end{methoddesc}
+\end{memberdesc}
+
+The following methods are available:
+
+\begin{methoddesc}{Backtrace}{elt}
+Returns a human-readable ``trace'' from the document root to the
+specified element.
+\end{methoddesc}
+
+\begin{methoddesc}{FindLocalHREF}{href, elt}
+Returns the element that has an \code{id} attribute whose value is specified
+by the \code{href} fragment identifier.
+The \code{href} \emph{must} be a fragment reference --- that is, it must
+start with a pound sign.
+This method raises an \exception{EvaluateException} exception if the
+element isn't found.
+It is mainly for use by the parsing methods in the \module{TypeCode} module.
+\end{methoddesc}
+
+\begin{methoddesc}{GetElementNSdict}{elt}
+Return a dictionary for all the namespace entries active at the
+current element. Each dictionary key will be the prefix and the value will
+be the namespace URI.
+\end{methoddesc}
+
+\begin{methoddesc}{GetMyHeaderElements}{\optional{actorlist=None}}
+Returns a list of all elements in the \code{Header} that are intended for
+\emph{this} SOAP processor.
+This includes all elements that either have no SOAP \code{actor}
+attribute, or whose value is either the special ``next actor'' value or
+in the \code{actorlist} list of URI's.
+\end{methoddesc}
+
+\begin{methoddesc}{GetDomAndReader}{}
+Returns a tuple containing the dom and reader objects, \code{(dom, reader)}.
+Unless keepdom is true, the dom and reader objects will go out of scope
+when the ParsedSoap instance is deleted. If keepdom is true, the reader
+object is needed to properly clean up the dom tree with
+\method{reader.releaseNode(dom)}.
+\end{methoddesc}
+
+\begin{methoddesc}{IsAFault}{}
+Returns true if the message is a SOAP fault.
+\end{methoddesc}
+
+\begin{methoddesc}{Parse}{how}
+Parses the SOAP \code{Body} according to the \code{how} parameter,
+and returns a Python object.
+If \code{how} is not a \class{TC.TypeCode} object, then it should be a
+Python class object that has a \code{typecode} attribute.
+\end{methoddesc}
+
+\begin{methoddesc}{ResolveHREF}{uri, tc\optional{, **keywords}}
+This method is invoked to resolve an absolute URI.
+If the typecode \code{tc} has a \code{resolver} attribute, it will use it
+to resolve the URI specified in the \code{uri} parameter,
+otherwise it will use its own \code{resolver}, or raise an
+\exception{EvaluateException} exception.
+
+Any \code{keyword} parameters will be passed to the chosen resolver.
+If no content is available, it will return \code{None}.
+If unable to resolve the URI it will raise an
+\exception{EvaluateException} exception.
+Otherwise, the resolver should return data in a form acceptable to the
+specified typecode, \code{tc}.
+(This will almost always be a file-like object holding opaque data;
+for XML, it may be a DOM tree.)
+\end{methoddesc}
+
+\begin{methoddesc}{WhatActorsArePresent}{}
+Returns a list of the values of all the SOAP \code{actor} attributes
+found in child elements of the SOAP \code{Header}.
+\end{methoddesc}
+
+\begin{methoddesc}{WhatMustIUnderstand}{}
+Returns a list of \samp{(uri, localname)} tuples for all elements in the
+SOAP \code{Header} that have the SOAP \code{mustUnderstand} attribute set
+to a non-zero value.
+\end{methoddesc}
+
+\ZSI{} supports multiple DOM implementations.
+The \code{readerclass} parameter specifies which one to use.
+The default is to use the DOM provided with the PyXML package developed
+by the Python XML SIG, provided through the \class{PyExpat.Reader} class
+in the \module{xml.dom.ext.reader} module.
+
+The specified reader class must support the following methods:
+
+\begin{methoddesc}{fromString}{string}
+Return a DOM object from a string.
+\end{methoddesc}
+
+\begin{methoddesc}{fromStream}{stream}
+Return a DOM object from a file-like stream.
+\end{methoddesc}
+
+\begin{methoddesc}{releaseNode}{dom}
+Free the specified DOM object.
+\end{methoddesc}
+
+The DOM object must support the standard Python mapping of the DOM Level 2
+specification.
+While only a small subset of specification is used, the particular
+methods and attributes used by \ZSI{} are available only
+by inspecting the source.
+
+To use the \code{cDomlette} DOM provided by the 4Suite package, use the
+\class{NonvalidatingReader} class in the \module{Ft.Xml.Domlette} module.
+Due to name changes in the 1.0 version of 4Suite, a simple adapter class
+is required to use this DOM implementation.
+
+\begin{verbatim}
+from 4Suite.Xml.Domlette import NonvalidatingReaderBase
+
+class 4SuiteAdapterReader(NonvalidatingReaderBase):
+
+ def fromString(self, str):
+ return self.parseString(str)
+
+ def fromStream(self, stream):
+ return self.parseStream(stream)
+
+ def releaseNode(self, node):
+ pass
+\end{verbatim}
diff --git a/doc/c06-tc.tex b/doc/c06-tc.tex
new file mode 100644
index 0000000..3954dac
--- /dev/null
+++ b/doc/c06-tc.tex
@@ -0,0 +1,783 @@
+\chapter{The \module{TypeCode} classes --- data conversions}
+
+The \module{TypeCode} module defines classes used for converting data
+between SOAP data and local Python objects.
+Python numeric and string types, and sequences and dictionaries, are
+supported by \ZSI{}.
+The \class{TC.TypeCode} class is the parent class of all datatypes
+understood by \ZSI{}.
+
+All typecodes classes have the prefix \code{TC.}, to avoid name clashes.
+
+\ZSI{} provides fine-grain control over the names used when parsing and
+serializing XML into local Python objects, through the use of two
+attributes: the \code{pname}, the \code{aname}. The \code{pname} specifies the
+name expected on the XML element being parsed and the name to use for the output element
+when serializing. The \code{aname} is the name to use for the analogous
+attribute in the local Python object.
+
+The \code{pname} is the parameter name. It specifies the incoming
+XML element name and the default values for the Python attribute
+and serialized names. All typecodes take the \code{pname} argument. This name can be
+specified as either a list or a string. When specified as a list, it must have
+two elements which are interpreted as a ``(namespace-URI, localname)'' pair.
+If specified this way, both the namespace and the local element name
+must match for the parse to succeed. For the Python attribute, and
+when generating output, only the ``localname'' is used. If a namespace-URI is
+specified then the full qualified name is used for output, and it is required
+for input; this \emph{requires} the namespace prefix to be specified.
+
+The \code{aname} is the attribute name. This parameter overrides
+any value implied by the \code{pname}. Typecodes nested in a \class{TC.Struct}
+or \class{TC.ComplexType} can use this parameter to specify
+the tag, dictionary key, or instance attribute to set.
+
+The \code{nsdict} parameter to the \class{SoapWriter} construct can be used to
+specify prefix to namespace-URI mappings, these are otherwise handled automatically.
+
+\section{\class{TC.TypeCode}}
+
+The \class{TypeCode} class is the parent class of all typecodes.
+
+\begin{classdesc}{TypeCode}{**keywords}
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{pname}}{\code{None}}{parameter name of the object}
+\lineiii{\code{aname}}{\code{None}}{attribute name of the object}
+\lineiii{\code{minOccurs}}{\code{1}}{schema facet minimum occurances}
+\lineiii{\code{maxOccurs}}{\code{1}}{schema facet maximum occurances}
+\lineiii{\code{nillable}}{\code{False}}{schema facet is this nillable (\code{xsi:nil="true"})}
+\lineiii{\code{typed}}{\code{True}}{Output type information (in the \code{xsi:type}
+attribute) when serializing. By special dispensation, typecodes within a
+\class{TC.Struct} object inherit this from the container.}
+\lineiii{\code{unique}}{\code{0}}{If true, the object is unique and will
+never be ``aliased'' with another object, so the \code{id} attribute
+need not be output.}
+
+\lineiii{\code{pyclass}}{\code{None}}{when parsing data, instances of this class
+can be created to store the data. Default behavior is reflective of specific
+TypeCode classes.}
+\lineiii{\code{attrs_aname}}{\code{'_attrs'}}{attribute name of the object where
+attribute values are stored. Used for serialization and parsing.}
+
+\end{tableiii}
+
+Optional elements are those which do not have to be an incoming
+message, or which have the XML Schema \code{nil} attribute set.
+When parsing the message as part of a \code{Struct}, then the Python
+instance attribute will not be set, or the element will not appear as
+a dictionary key.
+When being parsed as a simple type, the value \code{None} is returned.
+When serializing an optional element, a non-existent attribute, or a value
+of \code{None} is taken to mean not present, and the element is skipped.
+
+\end{classdesc}
+
+\begin{memberdesc}{typechecks}
+This is a class attribute.
+If true (the default) then all typecode constructors do more
+rigorous type-checking on their parameters.
+\end{memberdesc}
+
+\begin{memberdesc}{tag}
+This is a class attribute.
+Specifies the global element declaration this typecode represents, the value is
+a \samp{(namespace, name)} tuple.
+\end{memberdesc}
+
+\begin{memberdesc}{type}
+This is a class attribute.
+Specifies the global type definition this typecode represents, the value is
+a \samp{(namespace, name)} tuple.
+\end{memberdesc}
+
+\begin{memberdesc}{attribute_typecode_dict}
+This is a class attribute.
+This is a dict of \samp{(URI, NCName)} tuple keys, the values of each is a
+typecode. This is how attribute declarations other than SOAP and XMLSchema
+attribute declarations (eg. \code{xsi:type}, \code{id}, \code{href}, etc) are
+represented.
+\end{memberdesc}
+
+\begin{memberdesc}{logger}
+This is a class attribute.
+logger instance for this class.
+\end{memberdesc}
+
+The following methods are useful for defining new typecode classes;
+see the section on dynamic typing for more details. In all of the following,
+the \code{ps} parameter is a \class{ParsedSoap} object.
+
+\begin{methoddesc}{checkname}{elt, ps}
+Checks if the name and type of the element \code{elt} are
+correct and raises a \exception{EvaluateException} if not.
+Returns the element's type as a \samp{(uri, localname)} tuple if so.
+\end{methoddesc}
+
+\begin{methoddesc}{checktype}{elt, ps}
+Like \method{checkname()} except that the element name is ignored.
+This method is actually invoked by \method{checkname()} to do the
+second half of its processing, but is useful to invoke
+directly, such as when resolving multi-reference data.
+\end{methoddesc}
+
+\begin{methoddesc}{nilled}{elt, ps}
+If the element \code{elt} has data, this returns \code{False}.
+If it has no data, and the typecode is not optional, an
+\exception{EvaluateException} is raised; if it is optional,
+a \code{True} is returned.
+\end{methoddesc}
+
+\begin{methoddesc}{simple_value}{elt, ps, mixed=False}
+Returns the text content of the element \code{elt}.
+If no value is present, or the element has non-text children, an
+\exception{EvaluateException} is raised. If \code{mixed} is \code{False} if
+child elements are discovered an \exception{EvaluateException} is raised, else
+join all text nodes and return the result.
+
+\end{methoddesc}
+
+\section{\class{TC.Any} --- the basis of dynamic typing}
+
+SOAP provides a flexible set of serialization rules, ranging from
+completely self-describing to completely opaque, requiring an external
+schema. For example, the following are all possible ways of encoding an
+integer element \code{i} with a value of \code{12}:
+
+\subsection{simple data} -- requires type information
+\begin{verbatim}
+<tns:i xsi:type="SOAP-ENC:integer">12</tns:i>
+<tns:i xsi:type="xsd:nonNegativeInteger">12</tns:i>
+<SOAP-ENC:integer>12</SOAP-ENC:integer>
+<tns:i>12</tns:i>
+\end{verbatim}
+
+The first three lines are examples of \emph{typed} elements.
+If \ZSI{} is asked to parse any of the above examples, and a
+\class{TC.Any} typecode is given, it will properly create a Python
+integer for the first three, and raise a \exception{EvaluateException}
+for the fourth.
+
+\subsection{compound data} -- Struct or Array
+Compound data, such as a \code{struct}, may also be self-describing (namespace
+are omitted for clarity):
+\begin{verbatim}
+<tns:foo>
+ <tns:i xsi:type="SOAP-ENC:integer">12</tns:i>
+ <tns:name xsi:type="SOAP-ENC:string">Hello world</tns:name>
+</tns:foo>
+\end{verbatim}
+
+If this is parsed with a \class{TC.Any} typecode, either a Python \code{dict}
+is created or if \code{aslist} is True a \code{list}:
+\begin{verbatim}
+ps = ParsedSoap(xml, envelope=False)
+print ps.Parse(TC.Any())
+{ 'name': u'Hello world', 'i': 12 }
+
+print ps.Parse(TC.Any(aslist=True))
+[ 12, u'Hello world' ]
+\end{verbatim}
+Note that one preserves order, while the other preserves the element names.
+
+\subsection{class description}
+\begin{classdesc}{Any}{name\optional{, **keywords}}
+Used for parsing incoming SOAP data (that is typed), and serializing
+outgoing Python data.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{aslist}}{\code{False}}{If true, then the data is (recursively)
+treated as a list of values.
+The default is a Python dictionary, which preserves parameter names but
+loses the ordering.
+\versionadded{1.1}}
+\end{tableiii}
+
+In addition, if the Python object being serialized with an \class{Any}
+has a \code{typecode} attribute, then the \method{serialize} method of
+the typecode will be invoked to do the serialization.
+This allows objects to override the default dynamic serialization.
+\end{classdesc}
+
+Referring back to the compound XML data above, it is possible to create a new
+typecode capable of parsing elements of type \code{mytype}.
+This class would know that the \code{i} element is an integer,
+so that the explicit typing becomes optional, rather than required.
+
+\subsection{Adding new types} Most of the \class{TypeCodes} classes in
+\module{TC} are registered with \class{Any}, making an instance of itself
+available for dynamic typing. New \class{TypeCode} classes can be created and
+registered with \class{Any} by using \function{RegisterType}. In order to
+override an existing entry in the registry call \function{RegisterType} with
+\code{clobber=True}. The serialization entries are mappings between builtin
+Python types and a \class{TypeCode} instance, it is not possible to have one
+Python type map to multiple typecodes. The parsing entries are mappings
+between \code{(namespaceURI,name)} tuples, representing the \code{xsi:type}
+attribute, and a \class{TypeCode} instance. Thus, only one instance of a
+\class{TypeCode} class can represent a XML Schema type. So this mechanism is
+not appropriate for representing XML Schema element information.
+
+\begin{classdesc}{\emph{NEWTYPECODE}(TypeCode)}{...}
+The new typecode should be derived from the \class{TC.TypeCode} class, and
+\method{TypeCode.__init__()} must be invoked in the new class's constructor.
+\end{classdesc}
+
+\begin{memberdesc}{parselist}
+This is a class attribute, used when parsing incoming SOAP data.
+It should be a sequence of \samp{(uri, localname)} tuples to identify
+the datatype.
+If \code{uri} is \code{None}, it is taken to mean either the XML Schema
+namespace or the SOAP encoding namespace;
+this should only be used if adding support for additional primitive types.
+If this list is empty, then the type of the incoming SOAP data is assumed
+to be correct; an empty list also means that incoming typed data cannot
+by dynamically parsed.
+\end{memberdesc}
+
+\begin{memberdesc}{errorlist}
+This is a class attribute, used when reporting a parsing error.
+It is a text string naming the datatype that was expected.
+If not defined, \ZSI{} will create this attribute from the \code{parselist}
+attribute when it is needed.
+\end{memberdesc}
+
+\begin{memberdesc}{seriallist}
+This is a class attribute, used when serializing Python objects
+dynamically.
+It specifies what types of object instances (or Python types) this
+typecode can serialize.
+It should be a sequence, where each element is a Python class object,
+a string naming the class, or a type object from Python's \module{types}
+module (if the
+new typecode is serializing a built-in Python type).
+\end{memberdesc}
+
+\begin{methoddesc}{parse}{elt, ps}
+\ZSI{} invokes this method to
+parse the \code{elt} element and return its Python value.
+The \code{ps} parameter is the \class{ParsedSoap} object, and can be
+used for dereferencing \code{href}'s, calling \method{Backtrace()} to
+report errors, etc.
+\end{methoddesc}
+
+\begin{methoddesc}{serialize}{sw, pyobj\optional{, **keywords}}
+\ZSI{} invokes this method to output a Python object to a SOAP stream.
+The \code{sw} parameter will be a \class{SoapWriter} object, and
+the \code{pyobj} parameter is the Python object to serialize.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{attrtext}}{\code{None}}{Text (with leading space)
+to output as an attribute; this is normally used by the \class{TC.Array} class
+to pass down indexing information.}
+\lineiii{\code{name}}{\code{None}}{Name to use for serialization; defaults
+to the name specified in the typecode, or a generated name.}
+\lineiii{\code{typed}}{\emph{per-typecode}}{Whether or not to output type
+information; the default is to use the value in the typecode.}
+\end{tableiii}
+\end{methoddesc}
+
+Once the new typecode class has been defined, it should be registered with
+\ZSI{}'s dynamic type system by invoking the following function:
+
+\begin{funcdesc}{RegisterType}{class\optional{, clobber=0\optional{, **keywords}}}
+By default, it is an error to replace an existing type registration, and
+an exception will be raised.
+The \code{clobber} parameter may be given to allow replacement.
+A single instance of the \code{class} object will be created, and
+the \code{keyword} parameters are passed to the constructor.
+\end{funcdesc}
+
+If the class is not registered, then instances of the class cannot be
+processed as dynamic types.
+This may be acceptable in some environments.
+
+\section{\class{TC.SimpleType}}
+Parent class of all simple types.
+
+\begin{memberdesc}{empty_content}
+This is a class attribute.
+Value returned when tag or node is present, is not nilled, and without text
+content.
+\end{memberdesc}
+
+\section{Strings}
+
+SOAP/XMLSchema Strings are Python strings.
+
+\begin{classdesc}{String}{name\optional{, **keywords}}
+The parent type of all strings.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{resolver}}{\code{None}}{A function that can resolve an
+absolute URI and return its content as a string, as described in the
+\class{ParsedSoap} description.}
+\lineiii{\code{strip}}{\code{True}}{If true, leading and trailing whitespace
+are stripped from the content.}
+\end{tableiii}
+\end{classdesc}
+
+\begin{classdesc}{Enumeration}{value_list, name\optional{, **keywords}}
+Like \class{TC.String}, but the value must be a member of
+the \code{choices} sequence of text strings
+\end{classdesc}
+
+In addition to \class{TC.String},
+the basic string, several subtypes are provided that transparently
+handle common encodings.
+These classes create a temporary string object and pass that to
+the \method{serialize()} method.
+When doing RPC encoding, and checking for non-unique strings, the
+\class{TC.String} class must have the original Python string, as well
+as the new output.
+This is done by adding a parameter to the \method{serialize()} method:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{orig}}{\code{None}}{If deriving a new typecode from the
+string class, and the derivation creates a temporary Python string
+(such as by \class{Base64String}), than this parameter is the
+original string being serialized.}
+\end{tableiii}
+
+\begin{classdesc}{Base64String}{name\optional{, **keywords}}
+The value is encoded in Base-64.
+\end{classdesc}
+
+\begin{classdesc}{HexBinaryString}{name\optional{, **keywords}}
+Each byte is encoded as its printable version.
+\end{classdesc}
+
+\begin{classdesc}{URI}{name\optional{, **keywords}}
+The value is URL quoted (e.g., \code{\%20} for the space character).
+\end{classdesc}
+
+It is often the case that a parameter will be typed as a string for
+transport purposes, but will in fact have special syntax and processing
+requirements.
+For example, a string could be used for an XPath expression, but it is
+more convenient for the Python value to
+actually be the compiled expression. Here is how to do that:
+
+\begin{verbatim}
+import xml.xpath.pyxpath
+import xml.xpath.pyxpath.Compile as _xpath_compile
+
+class XPathString(TC.String):
+ def __init__(self, name, **kw):
+ TC.String.__init__(self, name, **kw)
+
+ def parse(self, elt, ps):
+ val = TC.String.parse(self, elt, ps)
+ try:
+ val = _xpath_compile(val)
+ except:
+ raise EvaluateException("Invalid XPath expression",
+ ps.Backtrace(elt))
+ return val
+\end{verbatim}
+
+In particular, it is common to send XML as a string, using entity
+encoding to protect the ampersand and less-than characters.
+
+\begin{classdesc}{XMLString}{name\optional{, **keywords}}
+Parses the data as a string, but returns an XML DOM object.
+For serialization, takes an XML DOM (or element node), and outputs
+it as a string.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{readerclass}}{\code{None}}{Class used to create DOM-creating
+XML readers; described in the \class{ParsedSoap} chapter.}
+\end{tableiii}
+
+\end{classdesc}
+
+\section{Integers}
+
+SOAP/XMLSchema integers are Python integers.
+
+\begin{classdesc}{Integer}{\optional{**keywords}}
+The parent type of all integers.
+This class handles any of the several types (and ranges) of SOAP integers.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{format}}{\code{\%d}}{Format string for serializing.
+\versionadded{1.2}}
+\end{tableiii}
+\end{classdesc}
+
+\begin{classdesc}{IEnumeration}{choices\optional{, **keywords}}
+Like \class{TC.Integer}, but the value must be a member of
+the \code{choices} sequence.
+\end{classdesc}
+
+A number of sub-classes are defined to handle smaller-ranged numbers.
+
+\begin{classdesc}{Ibyte}{\optional{**keywords}}
+A signed eight-bit value.
+\end{classdesc}
+
+\begin{classdesc}{IunsignedByte}{\optional{**keywords}}
+An unsigned eight-bit value.
+\end{classdesc}
+
+\begin{classdesc}{Ishort}{\optional{**keywords}}
+A signed 16-bit value.
+\end{classdesc}
+
+\begin{classdesc}{IunsignedShort}{\optional{**keywords}}
+An unsigned 16-bit value.
+\end{classdesc}
+
+\begin{classdesc}{Iint}{\optional{**keywords}}
+A signed 32-bit value.
+\end{classdesc}
+
+\begin{classdesc}{IunsignedInt}{\optional{**keywords}}
+An unsigned 32-bit value.
+\end{classdesc}
+
+\begin{classdesc}{Ilong}{\optional{**keywords}}
+An signed 64-bit value.
+\end{classdesc}
+
+\begin{classdesc}{IunsignedLong}{\optional{**keywords}}
+An unsigned 64-bit value.
+\end{classdesc}
+
+\begin{classdesc}{IpositiveInteger}{\optional{**keywords}}
+A value greater than zero.
+\end{classdesc}
+
+\begin{classdesc}{InegativeInteger}{\optional{**keywords}}
+A value less than zero.
+\end{classdesc}
+
+\begin{classdesc}{InonPositiveInteger}{\optional{**keywords}}
+A value less than or equal to zero.
+\end{classdesc}
+
+\begin{classdesc}{InonNegativeInteger}{\optional{**keywords}}
+A value greater than or equal to zero.
+\end{classdesc}
+
+\section{Floating-point Numbers}
+
+SOAP/XMLSchema floating point numbers are Python floats.
+
+\begin{classdesc}{Decimal}{\optional{**keywords}}
+The parent type of all floating point numbers.
+This class handles any of the several types (and ranges) of SOAP
+floating point numbers.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{format}}{\code{\%f}}{Format string for serializing.
+\versionadded{1.2}}
+\end{tableiii}
+\end{classdesc}
+
+\begin{classdesc}{FPEnumeration}{value_list, name\optional{, **keywords}}
+Like \class{TC.Decimal}, but the value must be a member of
+the \code{value_list} sequence.
+Be careful of round-off errors if using this class.
+\end{classdesc}
+
+Two sub-classes are defined to handle smaller-ranged numbers.
+
+\begin{classdesc}{FPfloat}{name\optional{, **keywords}}
+An IEEE single-precision 32-bit floating point value.
+\end{classdesc}
+
+\begin{classdesc}{FPdouble}{name\optional{, **keywords}}
+An IEEE double-precision 64-bit floating point value.
+\end{classdesc}
+
+\section{Dates and Times}
+
+SOAP dates and times are Python time tuples in UTC (GMT), as documented
+in the Python \module{time} module.
+Time is tricky, and processing anything other than a simple absolute time
+can be difficult.
+(Even then, timezones lie in wait to trip up the unwary.)
+A few caveats are in order:
+
+\begin{enumerate}
+
+\item
+Some date and time formats will be parsed into tuples that are
+not valid time values.
+For example, 75 minutes is a valid duration, although not a legal value
+for the minutes element of a time tuple.
+
+\item
+Fractional parts of a second may be lost when parsing, and may have
+extra trailing zero's when serializing.
+
+\item
+Badly-formed time tuples may result in non-sensical values being serialized;
+the first six values are taken directly as year, month, day, hour, minute,
+second in UTC.
+
+\item
+Although the classes \class{Duration} and \class{Gregorian} are defined, they
+are for internal use only and should not be included in any \class{TypeCode}
+you define. Instead, use the classes beginning with a lower case g in your
+typecodes.
+
+\end{enumerate}
+
+In addition, badly-formed values may result in non-sensical serializations.
+
+When serializing, an integral or floating point number is taken as
+the number of seconds since the epoch, in UTC.
+
+\begin{classdesc}{Duration}{\optional{**keywords}}
+A relative time period.
+Negative durations have all values less than zero; this makes
+it easy to add a duration to a Python time tuple.
+\end{classdesc}
+
+\begin{classdesc}{Gregorian}{\optional{**keywords}}
+An absolute time period.
+This class should not be instantiated directly; use one of the \code{gXXX}
+classes instead.
+\end{classdesc}
+
+\begin{classdesc}{gDateTime}{\optional{**keywords}}
+A date and time.
+\end{classdesc}
+
+\begin{classdesc}{gDate}{\optional{**keywords}}
+A date.
+\end{classdesc}
+
+\begin{classdesc}{gYearMonth}{\optional{**keywords}}
+A year and month.
+\end{classdesc}
+
+\begin{classdesc}{gYear}{\optional{**keywords}}
+A year.
+\end{classdesc}
+
+\begin{classdesc}{gMonthDay}{\optional{**keywords}}
+A month and day.
+\end{classdesc}
+
+\begin{classdesc}{gDay}{\optional{**keywords}}
+A day.
+\end{classdesc}
+
+\begin{classdesc}{gTime}{\optional{**keywords}}
+A time.
+\end{classdesc}
+
+\section{Boolean}
+
+SOAP Booleans are Python integers.
+
+\begin{classdesc}{Boolean}{\optional{**keywords}}
+When marshaling zero or the word ``false'' is returned as \code{0}
+and any non-zero value or the word ``true'' is returned as \code{1}.
+When serializing, the number \code{0} or \code{1} will be generated.
+\end{classdesc}
+
+\section{XML}
+
+XML is a Python DOM element node.
+If the value to be serialized is a Python string, then an \code{href}
+is generated, with the value used as the URI.
+This can be used, for example, when generating SOAP with attachments.
+Otherwise, the XML is typically put inside a wrapper element that sets
+the proper SOAP encoding style.
+
+For efficiency, incoming XML is returend as a ``pointer'' into the
+DOM tree maintained within the \class{ParsedSoap} object.
+If that object is going to go out of scope, the data will be destroyed
+and any XML objects will become empty elements.
+The class instance variable \code{copyit}, if non-zero indicates that a
+deep copy of the XML subtree will be made and returned as the value.
+Note that it is generally more efficient to keep the \class{ParsedSoap}
+object alive until the XML data is no longerneeded.
+
+\begin{classdesc}{XML}{\optional{**keywords}}
+This typecode represents a portion of an XML document embedded in a SOAP
+message.
+The value is the element node.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{copyit}}{TC.XML.copyit}{Return a copy of the parsed data.}
+\lineiii{\code{comments}}{\code{0}}{Preserve comments in output.}
+\lineiii{\code{inline}}{\code{0}}{The XML sub-tree is single-reference,
+so can be output in-place.}
+\lineiii{\code{resolver}}{\code{None}}{A function that can resolve an
+absolute URI and return its content as an element node, as described in the
+\class{ParsedSoap} description.}
+\lineiii{\code{wrapped}}{\code{1}}{If zero, the XML is output directly,
+and not within a SOAP wrapper element.
+\versionadded{1.2}}
+\end{tableiii}
+\end{classdesc}
+
+When serializing, it may be necessary to specify which namespace prefixes
+are ``active'' in the XML.
+This is done by using the \code{unsuppressedPrefixes} parameter when
+calling the \method{serialize()} method.
+(This will only work when XML is the top-level item being serialized,
+such as when using typecodes and document-style interfaces.)
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{unsuppressedPrefixes}}{[]}{An array of strings
+identifying the namespace prefixes that should be output.}
+\end{tableiii}
+
+\section{ComplexType}
+
+Represents the XMLSchema ComplexType .
+\versionadded{2.0}
+
+\begin{classdesc}{ComplexType}{pyclass, ofwhat\optional{, **keywords}}
+This class defines a compound data structure.
+If \code{pyclass} is \code{None}, then the data will be marshaled
+into a Python dictionary, and each item in the \code{ofwhat} sequence
+specifies a (possible) dictionary entry.
+Otherwise, \code{pyclass} must be a Python class object.
+The data is then marshaled into the object, and each item in the
+\code{ofwhat}
+sequence specifies an attribute of the instance to set.
+
+Note that each typecode in \code{ofwhat} must have a name.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{inorder}}{\code{False}}{Items within the structure must appear
+in the order specified in the \code{ofwhat} sequence.}
+\lineiii{\code{inline}}{\code{False}}{The structure is single-reference,
+so ZSI does not have to use \code{href/id} encodings.}
+\lineiii{\code{mutable}}{\code{False}}{If an object is going to be serialized
+multiple times, and its state may be modified between serializations,
+then this keyword should be used, otherwise a single instance will be
+serialized, with multiple references to it.
+This argument implies the \code{inline} argument.
+\versionadded{1.2}}
+\lineiii{\code{type}}{\code{None}}{A \samp{(uri, localname)} tuple that
+defines the type of the structure.
+If present, and if the input data has a \code{xsi:type} attribute, then the
+namespace-qualified value of that attribute must match the value
+specified by this parameter.
+By default, type-checking is not done for structures; matching child element
+names is usually sufficient and senders rarely provide type information.}
+\lineiii{\code{mixed}}{\code{False}}{using a mixed content model, allow text and
+element content.}
+\lineiii{\code{mixed_aname}}{\code{'_text'}}{if mixed is True, text
+content is set in this attribute (key).}
+\end{tableiii}
+
+If the \code{typed} keyword is used, then its value is assigned to
+all typecodes in the \code{ofwhat} parameter.
+If any of the typecodes in \code{ofwhat} are repeatable, then the
+\code{inorder} keyword should not be used and the \code{hasextras} parameter
+\emph{must} be used.
+
+For example, the following C structure:
+\begin{verbatim}
+struct foo {
+ int i;
+ char* text;
+};
+\end{verbatim}
+could be declared as follows:
+\begin{verbatim}
+class foo:
+ def __init__(self, name):
+ self.name = name
+ def __str__(self):
+ return str((self.name, self.i, self.text))
+
+foo.typecode = TC.Struct(foo,
+ ( TC.Integer('i'), TC.String('text') ),
+ 'foo')
+\end{verbatim}
+\end{classdesc}
+
+\section{Struct}
+
+SOAP Struct is a complex type for accessors identified by name. No element may
+have the same name as any other, nor may any element have a maxOccurs > 1.
+SOAP Structs are either Python dictionaries or instances of application-specified classes.
+
+\section{Arrays}
+
+SOAP arrays are Python lists; multi-dimensional arrays are
+lists of lists and are indistinguishable from a SOAP array of arrays.
+Arrays may be \emph{sparse}, in which case each element in the
+array is a tuple of \samp{(subscript, data)} pairs.
+If an array is not sparse, a specified \emph{fill} element will be
+used for the missing values.
+
+\strong{Currently only singly-dimensioned arrays are supported.}
+
+\begin{classdesc}{Array}{atype, ofwhat\optional{, **keywords}}
+The \code{atype} parameter is a \code{(URI,NCName)} tuple representing the SOAP
+array type. The \code{ofwhat} parameter is a typecode describing the array elements.
+\end{classdesc}
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{childnames}}{\code{None}}{Default name to use for the child
+elements.}
+\lineiii{\code{dimensions}}{\code{1}}{The number of dimensions in
+the array.}
+\lineiii{\code{fill}}{\code{None}}{The value to use when an array
+element is omitted.}
+\lineiii{\code{mutable}}{\code{False}}{If an object is going to be serialized
+multiple times, and its state may be modified between serializations,
+then this keyword should be used, otherwise a single instance will be
+serialized, with multiple references to it.}
+\lineiii{\code{nooffset}}{\code{0}}{Do not use the SOAP \code{offset}
+attribute so skip leading elements with the same value as \code{fill}.}
+\lineiii{\code{sparse}}{\code{False}}{The array is sparse.}
+\lineiii{\code{size}}{\code{None}}{An integer or list of integers that
+specifies the maximum array dimensions.}
+\lineiii{\code{undeclared}}{\code{False}}{The SOAP \samp{arrayType} attribute
+need not appear.}
+\end{tableiii}
+
+\section{Apache Datatype}
+
+The Apache SOAP project, url{http://xml.apache.org/soap/index.html},
+has defined a popular SOAP datatype in the
+\code{http://xml.apache.org/xml-soap} namespace, a
+\class{Map}.
+
+The \code{Map} type is encoded as a list of \code{item} elements.
+Each \code{item} has a \code{key} and \code{value} child element; these
+children must have SOAP type information.
+An Apache Map is either a Python dictionary or a list of two-element
+tuples.
+
+\begin{classdesc}{Apache.Map}{name\optional{, **keywords}}
+An Apache map.
+Note that the class name is dotted.
+\end{classdesc}
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{aslist}}{\code{0}}{Use a list of tuples rather than
+a dictionary.}
+\end{tableiii}
diff --git a/doc/c07-writer.tex b/doc/c07-writer.tex
new file mode 100644
index 0000000..dd6e263
--- /dev/null
+++ b/doc/c07-writer.tex
@@ -0,0 +1,96 @@
+\chapter{The \module{SoapWriter} module --- serializing data}
+
+The SoapWriter class is used to output SOAP messages.
+Note that its output is encoded as UTF-8; when transporting SOAP over
+HTTP it is therefore important to set the \code{charset} attribute
+of the \code{Content-Type} header.
+
+The \class{SoapWriter} class reserves some namespace prefixes:
+\begin{tableii}{l|l}{}{Prefix}{URI}
+\lineii{\code{SOAP-ENV}}{\code{http://schemas.xmlsoap.org/soap/envelope/}}
+\lineii{\code{SOAP-ENC}}{\code{http://schemas.xmlsoap.org/soap/encoding/}}
+\lineii{\code{ZSI}}{\code{http://www.zolera.com/schemas/ZSI/}}
+\lineii{\code{xsd}}{\code{http://www.w3.org/2001/XMLSchema}}
+\lineii{\code{xsi}}{\code{http://www.w3.org/2001/XMLSchema-instance}}
+\end{tableii}
+
+\begin{classdesc}{SoapWriter}{optional{**keywords}}
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{encodingStyle}}{None}{If not \code{None}, then
+use the specified value as the value for the SOAP \code{encodingStyle}
+attribute.
+\versionadded{1.2}}
+\lineiii{\code{envelope}}{\code{True}}{Create a SOAP Envelope
+\versionadded{1.2}}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Dictionary of namespaces to declare
+in the SOAP \code{Envelope}}
+\lineiii{\code{header}}{\code{True}}{create a SOAP \code{Header} element}
+\lineiii{\code{outputclass}}{\code{ElementProxy}}{wrapper around DOM or other
+XML library.}
+\end{tableiii}
+\end{classdesc}
+
+Creating a \class{SoapWriter} object with \code{envelope} set to \code{False}
+results in an object that can be used for serializing objects into a string.
+
+\begin{methoddesc}{serialize}{pyobj\optional{, typecode=None\optional{,
+root=None\optional{, header_pyobjs=None\optional{, **keywords}}}}}
+This method serializes the \code{pyobj} Python object as directed
+by the \code{typecode} typecode object.
+If \code{typecode} is omitted, then \code{pyobj} should be a Python
+object instance of a class that has a \code{typecode} attribute.
+It returns \code{self}, so that serializations can be chained together, or
+so that the \method{close()} method can be invoked.
+The \code{root} parameter may be used to explicitly indicate the root
+(main element) of a SOAP encoding, or indicate that the item is not the
+root.
+If specified, it should have the numeric value of zero or one.
+Any other keyword parameters are passed to the typecode's \code{serialize}
+method.
+\end{methoddesc}
+
+\begin{methoddesc}{close}{}
+Invokes all the callbacks, if any. The \function{close} operations can only
+happen once, if invoked a second time it will just return. This method will be
+invoked automatically if the object is deleted.
+\end{methoddesc}
+
+
+\begin{methoddesc}{__str__}{}
+Invokes the \function{close} method, and returns a string representation of the
+serialized object. Assumes that \function{serialize} has been invoked.
+\end{methoddesc}
+
+The following methods are primarily useful for those writing new typecodes.
+
+\begin{methoddesc}{AddCallback}{func, arg}
+Used by typecodes when serializing, allows them to add output after
+the SOAP \code{Body} is written but before the SOAP \code{Envelope} is closed.
+The function \method{func()}
+will be called with the \class{SoapWriter} object and the specified \code{arg}
+argument, which may be a tuple.
+\end{methoddesc}
+
+\begin{methoddesc}{Forget}{obj}
+Forget that \code{obj} has been seen before.
+This is useful when repeatedly serializing a mutable object.
+\end{methoddesc}
+
+\begin{methoddesc}{Known}{obj}
+If \code{obj} has been seen before (based on its Python \code{id}), return
+\code{1}. Otherwise, remember \code{obj} and return \code{0}.
+\end{methoddesc}
+
+\begin{methoddesc}{ReservedNS}{prefix, uri}
+Returns true if the specified namespace \code{prefix} and \code{uri} collide
+with those used by the implementation.
+\end{methoddesc}
+
+\begin{methoddesc}{writeNSDict}{nsdict}
+Outputs \code{nsdict} as a namespace dictionary.
+It is assumed that an XML start-element is pending on the output
+stream.
+\end{methoddesc}
+
diff --git a/doc/c08-fault.tex b/doc/c08-fault.tex
new file mode 100644
index 0000000..b5d8d82
--- /dev/null
+++ b/doc/c08-fault.tex
@@ -0,0 +1,135 @@
+\chapter{The \module{Fault} module --- reporting errors}
+
+SOAP defines a \emph{fault} message as the way for a recipient to
+indicate it was unable to process a message.
+The \ZSI{} \class{Fault} class encapsulates this.
+
+\begin{classdesc}{Fault}{code, string\optional{, **keywords}}
+The \var{code} parameter is a text string identifying the SOAP fault
+code, a namespace-qualified name.
+The class attribute \code{Fault.Client} can be used to indicate a problem with
+an incoming message, \code{Fault.Server} can be used to
+indicate a problem occurred while processing the request, or \code{Fault.MU}
+can be used to indicate a problem with the SOAP \code{mustUnderstand}
+attribute.
+The \var{string} parameter is a human-readable text string describing the
+fault.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\var{actor}}{\code{None}}{A string identifying the \code{actor}
+attribute that caused the problem (usually because it is unknown).}
+\lineiii{\var{detail}}{\code{None}}{A sequence
+of elements to output in the \code{detail} element; it may also
+be a text string, in which case it is output as-is, and should
+therefore be XML text.}
+\lineiii{\var{headerdetail}}{\code{None}}{Data, treated the same as
+the \code{detail} keyword, to be output in the SOAP header. See
+the following paragraph.}
+\end{tableiii}
+
+If the fault occurred in the SOAP \code{Header}, the specification
+requires that the detail be sent back as an element within
+the SOAP \code{Header} element.
+Unfortunately, the SOAP specification does not describe how to encode
+this; \ZSI{} defines and uses a
+\code{ZSI:detail} element, which is analogous to the SOAP \code{detail}
+element.
+\end{classdesc}
+
+The following attributes are read-only:
+
+\begin{memberdesc}{actor}
+A text string holding the value of the SOAP \code{faultactor} element.
+\end{memberdesc}
+
+\begin{memberdesc}{code}
+A text string holding the value of the SOAP \code{faultcode} element.
+\end{memberdesc}
+
+\begin{memberdesc}{detail}
+A text string or sequence of elements containing holding the value of the
+SOAP \code{detail} element, when available.
+\end{memberdesc}
+
+\begin{memberdesc}{headerdetail}
+A text string or sequence of elements containing holding the value of the
+\ZSI{} header detail element, when available.
+\end{memberdesc}
+
+\begin{memberdesc}{string}
+A text string holding the value of the SOAP \code{faultstring} element.
+\end{memberdesc}
+
+\begin{methoddesc}{AsSOAP}{\optional{, **kw}}
+This method serializes the \class{Fault} object into a SOAP message.
+The message is returned as a string.
+Any keyword arguments are passed to the \class{SoapWriter} constructor.
+\versionadded{1.1; the old \method{AsSoap()} method is still available}
+\end{methoddesc}
+
+If other data is going to be sent with the fault, the following
+two methods can be used.
+Because some data might need to be output in the SOAP \code{Header},
+serializing a fault is a two-step process.
+
+\begin{methoddesc}{DataForSOAPHeader}{}
+This method returns a text string that can be included as the
+\code{header} parameter for constructing a \class{SoapWriter} object.
+\end{methoddesc}
+
+\begin{methoddesc}{serialize}{sw}
+This method outputs the fault object onto the \var{sw} object, which is a
+\class{SoapWriter} instance.
+\end{methoddesc}
+
+Some convenience functions are available to create a \class{Fault}
+from common conditions.
+
+\begin{funcdesc}{FaultFromActor}{uri\optional{, actor}}
+This function could be used when an application receives a message
+that has a SOAP \code{Header} element directed to an actor that
+cannot be processed.
+The \var{uri} parameter identifies the actor.
+The \var{actor} parameter can be used to specify a URI that identifies the
+application, if it is not the ultimate recipient of the SOAP message.
+\end{funcdesc}
+
+\begin{funcdesc}{FaultFromException}{ex, inheader\optional{,
+ tb\optional{, actor}}}
+This function creates a \class{Fault} from a general Python exception.
+A SOAP ``server'' fault is created.
+The \var{ex} parameter should be the Python exception.
+The \var{inheader} parameter should be true if the error was
+found on a SOAP \code{Header} element.
+The optional \var{tb} parameter may be a Python traceback
+object, as returned by \samp{sys.exc_info()[2]}.
+The \var{actor} parameter can be used to specify a URI that identifies the
+application, if it is not the ultimate recipient of the SOAP message.
+\end{funcdesc}
+
+\begin{funcdesc}{FaultFromFaultMessage}{ps}
+This function creates a \class{Fault} from a \class{ParsedSoap} object
+passed in as \var{ps}.
+It should only be used if the \method{IsAFault()} method returned true.
+\end{funcdesc}
+
+\begin{funcdesc}{FaultFromNotUnderstood}{uri, localname,\optional{, actor}}
+This function could be used when an application receives a message with
+the SOAP \code{mustUnderstand} attribute that it does not understand.
+The \var{uri} and \var{localname} parameters should identify
+the unknown element.
+The \var{actor} parameter can be used to specify a URI that identifies the
+application, if it is not the ultimate recipient of the SOAP message.
+\end{funcdesc}
+
+\begin{funcdesc}{FaultFromZSIException}{ex\optional{, actor}}
+This function creates a \class{Fault} object from a \ZSI{} exception,
+\exception{ParseException} or \exception{EvaluateException}, passed in
+as \var{ex}.
+A SOAP ``client'' fault is created.
+The \var{actor} parameter can be used to specify a URI that identifies the
+application, if it is not the ultimate recipient of the SOAP message.
+\end{funcdesc}
+
diff --git a/doc/c09-resolve.tex b/doc/c09-resolve.tex
new file mode 100644
index 0000000..475ee8a
--- /dev/null
+++ b/doc/c09-resolve.tex
@@ -0,0 +1,103 @@
+\chapter{The \module{resolvers} module --- fetching remote data}
+
+The \module{resolvers} module provides some functions and classes
+that can be used as the \code{resolver} attribute for \class{TC.String}
+or \class{TC.XML} typecodes.
+They process an absolute URL, as described above, and return the
+content.
+Because the \module{resolvers} module can import a number of other
+large modules, it must be imported directly, as in
+\samp{from ZSI import resolvers}.
+
+These first two functions pass the URI directly to the \method{urlopen}
+function in the \module{urllib} module.
+Therefore, if used directly as resolvers, a client could direct the
+SOAP application to fetch any file on the network or local disk.
+Needless to say, this could pose a security risks.
+
+\begin{funcdesc}{Opaque}{uri, tc, ps\optional{, **keywords}}
+This function returns the data contained at the specified \code{uri}
+as a Python string.
+Base-64 decoding will be done if necessary.
+The \code{tc} and \code{ps} parameters are ignored; the \code{keywords}
+are passed to the \method{urlopen} method.
+\end{funcdesc}
+
+\begin{funcdesc}{XML}{uri, tc, ps\optional{, **keywords}}
+This function returns a list of the child element nodes of the XML
+document at the specified \code{uri}.
+The \code{tc} and \code{ps} parameters are ignored; the \code{keywords}
+are passed to the \method{urlopen} method.
+\end{funcdesc}
+
+The \class{NetworkResolver} class provides a simple-minded way to limit
+the URI's that will be resolved.
+
+\begin{classdesc}{NetworkResolver}{\optional{prefixes=None}}
+The \code{prefixes} parameter is a list of strings defining the allowed
+prefixes of any URI's.
+If asked to fetch the content for a URI that does start with one of
+the prefixes, it will raise an exception.
+
+In addition to \code{Opaque} and \code{XML} methods, this class
+provides a \code{Resolve} method that examines the typecode to determine
+what type of data is desired.
+\end{classdesc}
+
+If the SOAP application is given a multi-part MIME document, the
+\class{MIMEResolver} class can be used to process SOAP with Attachments.
+
+The \class{MIMEResolver} class will read the entire multipart MIME document,
+noting any \code{Content-ID} or \code{Content-Location} headers that appear
+on the headers of any of the message parts, and use them to resolve
+any \code{href} attributes that appear in the SOAP message.
+
+\begin{classdesc}{MIMEResolver}{ct, f\optional{, **keywords}}
+The \code{ct} parameter is a string that contains the value of the
+MIME \code{Content-Type} header.
+The \code{f} parameter is the input stream, which should be positioned just
+after the message headers.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{seekable}}{\code{0}}{Whether or not the input stream is
+seekable; passed to the constructor for the internal \class{multifile}
+object.
+\versionchanged[default had been 1]{2.0}}
+\lineiii{\code{next}}{\code{None}}{A resolver object that will be
+asked to resolve the URI if it is not found in the MIME document.
+\versionadded{1.1}}
+\lineiii{\code{uribase}}{\code{None}}{The base URI to be used when
+resolving relative URI's; this will typically be the value of the
+\code{Content-Location} header, if present.
+\versionadded{1.1}}
+\end{tableiii}
+\end{classdesc}
+
+In addition to to the \code{Opaque}, \code{Resolve}, and \code{XML} methods
+as described above, the following method is available:
+
+\begin{methoddesc}{GetSOAPPart}{}
+This method returns a stream containing the SOAP message text.
+\end{methoddesc}
+
+The following attributes are read-only:
+
+\begin{memberdesc}{parts}
+An array of tuples, one for each MIME bodypart found.
+Each tuple has two elements, a \class{mimetools.Message} object
+which contains the headers for the bodypart, and a
+\class{StringIO} object containing the data.
+\end{memberdesc}
+
+\begin{memberdesc}{id_dict}
+A dictionary whose keys are the values of any \code{Content-ID}
+headers, and whose value is the appropriate \code{parts} tuple.
+\end{memberdesc}
+
+\begin{memberdesc}{loc_dict}
+A dictionary whose keys are the values of any \code{Content-Location}
+headers, and whose value is the appropriate \code{parts} tuple.
+\end{memberdesc}
+
diff --git a/doc/c10-dispatch.tex b/doc/c10-dispatch.tex
new file mode 100644
index 0000000..eb38a82
--- /dev/null
+++ b/doc/c10-dispatch.tex
@@ -0,0 +1,442 @@
+\chapter{Dispatching and Invoking}
+
+\versionadded{1.1}
+
+\ZSI{} is focused on parsing and generating SOAP messages, and provides
+limited facilities for dispatching to the appropriate message handler.
+This is because \ZSI{} works within many client and server environments,
+and the dispatching styles for these different environments can be
+very different.
+
+Nevertheless, \ZSI{} includes some dispatch and invocation functions.
+To use them, they must be explicitly imported, as shown in the example
+at the start of this document.
+
+The implementation (and names) of the these classes reflects the orientation
+of using SOAP for remote procedure calls (RPC).
+
+Both client and server share a class that defines the mechanism a
+client uses to authenticate itself.
+
+\begin{classdesc}{AUTH}{}
+This class defines constants used to identify how the client
+authenticated: \code{none} if no authentication was provided;
+\code{httpbasic} if HTTP basic authentication was used, or
+\code{zsibasic} if \ZSI{} basic authentication (see below)) was used.
+\end{classdesc}
+
+The \ZSI{} schema (see the last chapter of this manual)
+defines a SOAP header element, \code{BasicAuth}, that
+contains a name and password.
+This is similar to the HTTP basic authentication header, except
+that it can be used independently from an HTTP transport.
+
+\section{Dispatching}
+
+The \module{ZSI.dispatch} module allows you to expose Python functions as a web
+service. The module provides the infrastructure to parse the request, dispatch
+to the appropriate handler, and then serialize any return value back to the
+client. The value returned by the function will be serialized back to the
+client. If an exception occurs, a SOAP fault will be sent back to the client.
+
+\subsection{Dispatch Behaviors} By default the callback is invoked with the
+pyobj representation of the body root element, and it is expected to return a
+self-describing request (w/typecode). Parsing is done via a typecode from
+typesmodule, or Any. Other keyword options are available in dispatch mechanisms
+(see below) that result in different behavior.
+
+\subsubsection{rpc} An rpc service will ignore the body root (RPC Wrapper) of
+the request, and parse all "parts" of message via individual typecodes. The
+callback function is expected to return the parts of the message in a dict or a
+list. The dispatch mechanism will try to serialize it as a Struct but if this
+is not possible it will be serialized as an Array. Parsing done via a typecode
+from typesmodule, or Any. Not compatible with \var{docstyle}.
+
+\subsubsection{docstyle} Callback is invoked with a ParsedSoap instance
+representing the request, and the return value is serialized with an XML
+typecode (DOM). The result in wrapped as an rpc-style message, with
+\emph{Response} appended to the request wrapper. Not compatible with \var{rpc}.
+
+\subsection{Special Modules} These are keyword options available to all
+dispatch mechansism (see below).
+
+\subsubsection{modules}{Dispatch is based solely on the name of the root element in the
+incoming SOAP request; the request URL is ignored. These modules will be search
+for a matching function. If no modules are specified, only the
+\module{__main__} module will be searched.}
+
+\subsubsection{typesmodule}{Used for parsing. This module should contain class
+definitions with the \code{typecode} attribute set to a \class{TypeCode}
+instance. By default, a class definition matching the root element name will be
+retrieved or the Any typecode will be used. If using \emph{rpc}, each child of
+the root element will be used to retrieve a class definition of the same name.}
+
+\subsection{Dispatch Mechanisms}
+Three dispatch mechanisms are provided: one supports standard CGI
+scripts, one runs a dedicated server based on the
+\module{BaseHTTPServer} module, and the third uses the JonPY package,
+\url{http://jonpy.sourceforge.net}, to support FastCGI.
+
+\begin{methoddesc}{AsServer}{\optional{**keywords}}
+This creates a \class{HTTPServer} object with a request handler that only
+supports the ``POST'' method.
+Dispatch is based solely on the name of the root element in the
+incoming SOAP request;
+the request URL is ignored.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{port}}{\code{80}}{Port to listen on.}
+\lineiii{\code{addr}}{\code{''}}{Address to listen on.}
+\lineiii{\code{docstyle}}{\code{False}}{Exhibit the \emph{docstyle} behavior.}
+\lineiii{\code{rpc}}{\code{False}}{Exhibit the \emph{rpc} behavior.}
+\lineiii{\code{modules}}{\code{(__main__,)}}{List of modules containing
+functions that can be invoked.}
+\lineiii{\code{typesmodule}}{\code{(__main__,)}}{This module is used for
+parsing, it contains class definitions that specify the \code{typecode}
+attribute.}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the SOAP
+\code{Envelope}}
+\end{tableiii}
+
+\end{methoddesc}
+
+
+\begin{methoddesc}{AsCGI}{\optional{**keywords}}
+This method parses the CGI input and invokes a function that has the
+same name as the top-level SOAP request element.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{rpc}}{\code{False}}{Exhibit the \emph{rpc} behavior.}
+\lineiii{\code{modules}}{\code{(__main__,)}}{List of modules containing
+functions that can be invoked.}
+\lineiii{\code{typesmodule}}{\code{(__main__,)}}{This module is used for
+parsing, it contains class definitions that specify the \code{typecode}
+attribute.}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the SOAP
+\code{Envelope}}
+\end{tableiii}
+
+\end{methoddesc}
+
+
+
+\begin{methoddesc}{AsHandler}{request=None\optional{, **keywords}}
+
+This method is used within a JonPY handler to do dispatch.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{request}}{\code{None}}{modpython HTTPRequest instance.}
+\lineiii{\code{modules}}{\code{(__main__,)}}{List of modules containing
+functions that can be invoked.}
+\lineiii{\code{docstyle}}{\code{False}}{Exhibit the \emph{docstyle} behavior.}
+\lineiii{\code{rpc}}{\code{False}}{Exhibit the \emph{rpc} behavior.}
+\lineiii{\code{typesmodule}}{\code{(__main__,)}}{This module is used for
+parsing, it contains class definitions that specify the \code{typecode}
+attribute.}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the SOAP
+\code{Envelope}}
+\end{tableiii}
+\end{methoddesc}
+
+
+\begin{methoddesc}{AsJonPy}{request=None\optional{, **keywords}}
+
+This method is used within a JonPY handler to do dispatch.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{request}}{\code{None}}{jonpy Request instance.}
+\lineiii{\code{modules}}{\code{(__main__,)}}{List of modules containing
+functions that can be invoked.}
+\lineiii{\code{docstyle}}{\code{False}}{Exhibit the \emph{docstyle} behavior.}
+\lineiii{\code{rpc}}{\code{False}}{Exhibit the \emph{rpc} behavior.}
+\lineiii{\code{typesmodule}}{\code{(__main__,)}}{This module is used for
+parsing, it contains class definitions that specify the \code{typecode}
+attribute.}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the SOAP
+\code{Envelope}}
+\end{tableiii}
+
+
+The following code shows a sample use:
+
+\begin{verbatim}
+import jon.fcgi
+from ZSI import dispatch
+import MyHandler
+
+class Handler(cgi.Handler):
+ def process(self, req):
+ dispatch.AsJonPy(modules=(MyHandler,), request=req)
+
+jon.fcgi.Server({jon.fcgi.FCGI_RESPONDER: Handler}).run()
+\end{verbatim}
+
+\end{methoddesc}
+
+\subsection{Other Dispatch Stuff}
+
+\begin{methoddesc}{GetClientBinding}{}
+More sophisticated scripts may want to use access the client binding object,
+which encapsulates all information about the client invoking the script.
+This function returns \code{None} or the binding information, an
+object of type \class{ClientBinding}, described below.
+\end{methoddesc}
+
+\begin{classdesc}{ClientBinding}{...}
+This object contains information about the client.
+It is created internally by \ZSI{}.
+\end{classdesc}
+
+\begin{methoddesc}{GetAuth}{}
+This returns a tuple containing information about the client identity.
+The first element will be one of the constants from the \code{AUTH} class
+described above.
+For HTTP or \ZSI{} basic authentication, the next two elements will be
+the name and password provided by the client.
+\end{methoddesc}
+
+\begin{methoddesc}{GetNS}{}
+Returns the namespace URI that the client is using, or an empty string.
+This can be useful for versioning.
+\end{methoddesc}
+
+\begin{methoddesc}{GetRequest}{}
+Returns the \class{ParsedSoap} object of the incoming request.
+\end{methoddesc}
+
+The following attribute is read-only:
+
+\begin{memberdesc}{environ}
+A dictionary of the environment variables.
+This is most useful when \method{AsCGI()} is used.
+\end{memberdesc}
+
+
+\section{The \module{client} module --- sending SOAP messages}
+
+\ZSI{} includes a module to connect to a SOAP server over HTTP, send requests,
+and parse the response.
+It is built on the standard Python \module{httplib} and \module{Cookie}
+modules.
+It must be explicitly imported, as in
+\samp{from ZSI.client import AUTH,Binding}.
+
+\subsection{_Binding}
+
+\begin{classdesc}{_Binding}{\optional{**keywords}}
+This class encapsulates a connection to a server, known as a \emph{binding}.
+A single binding may be used for multiple RPC calls.
+Between calls, modifiers may be used to change the URL being posted to,
+etc.
+
+Cookies are also supported; if a response comes back with a \code{Set-Cookie}
+header, it will be parsed and used in subsequent interactions.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{20em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{auth}}{\code{(AUTH.none,)}}{A tuple with authentication
+ information; the first value should be one of the constants
+ from the \class{AUTH} class.}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the
+ SOAP \code{Envelope}}
+\lineiii{\code{soapaction}}{\code{''}}{Value for the
+ \code{SOAPAction} HTTP header.}
+\lineiii{\code{readerclass}}{\code{None}}{Class used to create DOM-creating
+XML readers; see the description in the \class{ParsedSoap} class.}
+\lineiii{\code{writerclass}}{\code{None}}{ElementProxy Class used to create
+XML writers; see the description in the \class{SoapWriter} class.}
+\lineiii{\code{tracefile}}{\code{None}}{An object with a \code{write}
+ method, where packet traces will be recorded.}
+\lineiii{\code{transport}}{HTTPConnection/HTTPSConnection}{transport class}
+\lineiii{\code{transdict}}{\{\}}{keyword arguments for connection initialization}
+\lineiii{\code{url}}{n/a}{URL to post to.}
+\lineiii{\code{wsAddressURI}}{None}{URI, identifies the WS-Address specification
+to use. By default it's not used.}
+\lineiii{\code{sig_handler}}{None}{XML Signature handler, must sign and verify.}
+\end{tableiii}
+
+If using SSL, the \code{cert_file} and \code{key_file} keyword parameters may
+also be used. For details see the documentation for the \module{httplib}
+module.
+
+\end{classdesc}
+
+Once a \class{_Binding} object has been created, the following modifiers are
+available. All of them return the binding object, so that multiple modifiers
+can be chained together.
+
+\begin{methoddesc}{AddHeader}{header, value}
+Output the specified \code{header} and \code{value} with the HTTP
+headers.
+\end{methoddesc}
+
+\begin{methoddesc}{SetAuth}{style, name, password}
+The \code{style} should be one of the constants from the \code{AUTH}
+class described above.
+The remaining parameters will vary depending on the \code{style}.
+Currently only basic authentication data of name and password are
+supported.
+\end{methoddesc}
+
+\begin{methoddesc}{SetNS}{uri}
+Set the default namespace for the request to the specified \code{uri}.
+\end{methoddesc}
+
+\begin{methoddesc}{SetURL}{url}
+Set the URL where the post is made to \code{url}.
+\end{methoddesc}
+
+\begin{methoddesc}{ResetHeaders}{}
+Remove any headers that were added by \method{AddHeader()}.
+\end{methoddesc}
+
+The following attribute may also be modified:
+
+\begin{memberdesc}{trace}
+If this attribute is not \code{None}, it should be an object with a
+\code{write} method, where packet traces will be recorded.
+\end{memberdesc}
+
+Once the necessary parameters have been specified (at a minimum, the URL
+must have been given in the constructor are through \code{SetURL}),
+invocations can be made.
+
+\begin{methoddesc}{RPC}{url, opname, pyobj, replytype=None\optional{, **keywords}}
+This is the highest-level invocation method.
+It calls \method{Send()} to send \code{pyobj} to the specified \code{url}
+to perform the \code{opname} operation,
+and calls \method{Receive()} expecting to get a reply of the specified
+\code{replytype}.
+
+This method will raise a \exception{TypeError} if the response does not
+appear to be a SOAP message, or if is valid SOAP but contains a fault.
+\end{methoddesc}
+
+\begin{methoddesc}{Send}{url, opname, pyboj\optional{, **keywords}}
+This sends the specified \code{pyobj} to the specified \code{url}, invoking
+the \code{opname} method.
+The \code{url} can be \code{None} if it was specified in the \class{Binding}
+constructor or if \code{SetURL} has been called.
+See below for a shortcut version of this method.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{20em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{auth\_header}}{\code{None}}{String (containing presumably
+ serialized XML) to output as an authentication header.}
+ SOAP \code{Envelope}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the
+ SOAP \code{Envelope}}
+\lineiii{\code{requesttypecode}}{n/a}{Typecode specifying how to serialize
+ the data.}
+\lineiii{\code{soapaction}}{Obtained from the \class{Binding}}{Value for the
+ \code{SOAPAction} HTTP header.}
+\end{tableiii}
+
+\end{methoddesc}
+
+Methods are available to determine the type of response that came back:
+
+\begin{methoddesc}{IsSOAP}{}
+Returns true if the message appears to be a SOAP message.
+(Some servers return an HTML page under certain error conditions.)
+\end{methoddesc}
+
+\begin{methoddesc}{IsAFault}{}
+Returns true if the message is a SOAP fault.
+\end{methoddesc}
+
+Having determined the type of the message (or, more likely, assuming
+it was good and catching an exception if not), the following methods
+are available to actually parse the data.
+They will continue to return the same value until
+another message is sent.
+
+\begin{methoddesc}{ReceiveRaw}{}
+Returns the unparsed message body.
+\end{methoddesc}
+
+\begin{methoddesc}{ReceiveSoap}{}
+Returns a \class{ParsedSOAP} object containing the parsed message.
+Raises a \exception{TypeError} if the message wasn't SOAP.
+\end{methoddesc}
+
+\begin{methoddesc}{ReceiveFault}{}
+Returns a \class{Fault} object containing the SOAP fault message.
+Raises a \exception{TypeError} if the message did not contain a fault.
+\end{methoddesc}
+
+\begin{methoddesc}{Receive}{replytype=None}
+Parses a SOAP message.
+The \code{replytype} specifies how to parse the data.
+If it s \code{None}, dynamic parsing will be used, usually resulting
+in a Python list.
+If \code{replytype} is a Python class, then the class's \code{typecode}
+attribute will be used, otherwise \code{replytype} is taken to be
+the typecode to use for parsing the data.
+\end{methoddesc}
+
+Once a reply has been parsed (or its type examined), the following
+read-only attributes are available.
+Their values will remain unchanged until another reply is parsed.
+
+\begin{memberdesc}{reply_code}
+The HTTP reply code, a number.
+\end{memberdesc}
+
+\begin{memberdesc}{reply_headers}
+The HTTP headers, as a \class{mimetools} object.
+\end{memberdesc}
+
+\begin{memberdesc}{reply_msg}
+A text string containing the HTTP reply text.
+\end{memberdesc}
+
+\subsection{Binding}
+If an attribute is fetched other than one of those described in
+\class{_Binding}, it is taken to be the \code{opname} of a remote procedure, and
+a callable object is returned. This object dynamically parses its arguments,
+receives the reply, and parses that.
+
+\begin{classdesc}{Binding}{\optional{**keywords}}
+For other keyword arguments see \class{_Binding}.
+\begin{tableiii}{l|c|p{20em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{typesmodule}}{\code{None}}{See explanation in Dispatching}
+\end{tableiii}
+\end{classdesc}
+
+\begin{methoddesc}{opname}{*args}
+Using this shortcut requires that the \var{url} attribute is set, either
+throught the constructor or \method{SetURL()}.
+\end{methoddesc}
+
+
+\subsection{NamedParamBinding}
+If an attribute is fetched other than one of those described
+in \class{_Binding}, it is taken to be the \code{opname} of a remote procedure, and a callable
+object is returned. This object dynamically parses its arguments, receives the
+reply, and parses that.
+
+\begin{classdesc}{NamedParamBinding}{\optional{**keywords}}
+For other keyword arguments see \class{_Binding}.
+\begin{tableiii}{l|c|p{20em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{typesmodule}}{\code{None}}{See explanation in Dispatching}
+\end{tableiii}
+\end{classdesc}
+
+\begin{methoddesc}{opname}{**kwargs}
+Using this shortcut requires that the \var{url} attribute is set, either
+throught the constructor or \method{SetURL()}.
+\end{methoddesc}
+
+
+
diff --git a/doc/client-hello-struct.tex b/doc/client-hello-struct.tex
new file mode 100644
index 0000000..eee6d69
--- /dev/null
+++ b/doc/client-hello-struct.tex
@@ -0,0 +1,13 @@
+\begin{verbatim}
+
+#!/usr/bin/env python
+import sys,time
+from ZSI.client import NamedParamBinding as NPBinding
+
+b = NPBinding(url='http://127.0.0.1/cgi-bin/soapstruct', tracefile=sys.stdout)
+print "Hello: ", b.hello()
+print "Echo: ", b.echo(name="josh", year=2006, pi=3.14, time=time.gmtime())
+print "Sum: ", b.sum(one=1, two=2, three=3)
+print "Average: ", b.average(one=100, two=200, three=300, four=400)
+
+\end{verbatim}
diff --git a/doc/contents.png b/doc/contents.png
new file mode 100644
index 0000000..3429be0
--- /dev/null
+++ b/doc/contents.png
Binary files differ
diff --git a/doc/dispatch-rpc-hello-array.tex b/doc/dispatch-rpc-hello-array.tex
new file mode 100644
index 0000000..2077e83
--- /dev/null
+++ b/doc/dispatch-rpc-hello-array.tex
@@ -0,0 +1,21 @@
+\begin{verbatim}
+#!/usr/local/bin/python2.4
+# SOAP Array
+
+def hello():
+ return ["Hello, world"]
+
+def echo(*args):
+ return args
+
+def sum(*args):
+ sum = 0
+ for i in args: sum += i
+ return [sum]
+
+def average(*args):
+ return [sum(*args) / len(args)]
+
+from ZSI import dispatch
+dispatch.AsCGI(rpc=True)
+\end{verbatim}
diff --git a/doc/dispatch-rpc-hello-struct.tex b/doc/dispatch-rpc-hello-struct.tex
new file mode 100644
index 0000000..ed10b98
--- /dev/null
+++ b/doc/dispatch-rpc-hello-struct.tex
@@ -0,0 +1,22 @@
+\begin{verbatim}
+#!/usr/local/bin/python2.4
+# SOAP Struct
+
+def hello():
+ return {"value":"Hello, world"}
+
+def echo(**kw):
+ return kw
+
+def sum(**kw):
+ sum = 0
+ for i in kw.values(): sum += i
+ return {"value":sum}
+
+def average(**kw):
+ d = sum(**kw)
+ return d["value"] = d["value"]/len(kw)
+
+from ZSI import dispatch
+dispatch.AsCGI(rpc=True)
+\end{verbatim}
diff --git a/doc/examples/client/receive_response/complex/binding.wsdl b/doc/examples/client/receive_response/complex/binding.wsdl
new file mode 100644
index 0000000..5a95911
--- /dev/null
+++ b/doc/examples/client/receive_response/complex/binding.wsdl
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+
+
+<definitions name="Registration"
+ targetNamespace="http://pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://pycon.org/" location="interface.wsdl"/>
+
+ <binding name="Binding" type="tns:Registration">
+ <soap:binding style="rpc"
+ transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="GetUser">
+ <soap:operation soapAction="urn:Registration#GetUser"/>
+ <input>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Registration" use="encoded"/>
+ </input>
+ <output>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Registration" use="encoded"/>
+ </output>
+ </operation>
+ </binding>
+
+ <service name="RegistrationService">
+ <documentation>Registration web service</documentation>
+ <port name="Registration" binding="tns:Binding">
+ <soap:address location="http://127.0.0.1:8080/example.py"/>
+ </port>
+ </service>
+</definitions>
diff --git a/doc/examples/client/receive_response/complex/interface.wsdl b/doc/examples/client/receive_response/complex/interface.wsdl
new file mode 100644
index 0000000..40963ec
--- /dev/null
+++ b/doc/examples/client/receive_response/complex/interface.wsdl
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+
+<definitions name="Registration"
+ targetNamespace="http://pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:types="http://pycon.org/types"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://pycon.org/types" location="types.xsd"/>
+
+ <message name="GetUserResponse">
+ <part name="User" type="types:User"/>
+ </message>
+
+ <message name="GetUserRequest">
+ <part name="UserId" type="xsd:string"/>
+ </message>
+
+
+ <portType name="Registration">
+ <operation name="GetUser">
+ <input message="tns:GetUserRequest"/>
+ <output message="tns:GetUserResponse"/>
+ </operation>
+ </portType>
+
+</definitions>
diff --git a/doc/examples/client/receive_response/complex/manual/.cvsignore b/doc/examples/client/receive_response/complex/manual/.cvsignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/doc/examples/client/receive_response/complex/manual/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/doc/examples/client/receive_response/complex/manual/ComplexTypes.py b/doc/examples/client/receive_response/complex/manual/ComplexTypes.py
new file mode 100644
index 0000000..00918f7
--- /dev/null
+++ b/doc/examples/client/receive_response/complex/manual/ComplexTypes.py
@@ -0,0 +1,13 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'User')
diff --git a/doc/examples/client/receive_response/complex/manual/client.py b/doc/examples/client/receive_response/complex/manual/client.py
new file mode 100755
index 0000000..d1737b5
--- /dev/null
+++ b/doc/examples/client/receive_response/complex/manual/client.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+
+import ComplexTypes as MyComplexTypes
+
+import sys
+
+def main():
+ server = ServiceProxy('../binding.wsdl',
+ typesmodule=MyComplexTypes,
+ tracefile=sys.stdout)
+
+ user = server.GetUser('john_doe')
+ print ' Age: %d' % user.Age
+ print ' Name: %s' % user.Name
+ print 'UserId: %s' % user.UserId
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/client/receive_response/complex/server/.cvsignore b/doc/examples/client/receive_response/complex/server/.cvsignore
new file mode 100644
index 0000000..4086804
--- /dev/null
+++ b/doc/examples/client/receive_response/complex/server/.cvsignore
@@ -0,0 +1 @@
+Registration_services*
diff --git a/doc/examples/client/receive_response/complex/server/server.py b/doc/examples/client/receive_response/complex/server/server.py
new file mode 100755
index 0000000..3379339
--- /dev/null
+++ b/doc/examples/client/receive_response/complex/server/server.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from Registration_services import GetUserResponseWrapper
+from Registration_services_types import ns1
+
+
+def GetUser(user_id):
+ user = ns1.User_Def()
+ user._UserId = user_id
+ user._Name = "John Doe"
+ user._Age = 32
+
+ response = GetUserResponseWrapper()
+ response._User = user
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/doc/examples/client/receive_response/complex/types.xsd b/doc/examples/client/receive_response/complex/types.xsd
new file mode 100644
index 0000000..7a1f17f
--- /dev/null
+++ b/doc/examples/client/receive_response/complex/types.xsd
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+
+<schema targetNamespace="http://pycon.org/types"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:tns="http://pycon.org/types">
+
+ <complexType name="User">
+ <sequence>
+ <element name="UserId" type="string"/>
+ <element name="Name" type="string"/>
+ <element name="Age" type="integer"/>
+ </sequence>
+ </complexType>
+
+</schema>
diff --git a/doc/examples/client/receive_response/complex/wsdl2py/.cvsignore b/doc/examples/client/receive_response/complex/wsdl2py/.cvsignore
new file mode 100644
index 0000000..4086804
--- /dev/null
+++ b/doc/examples/client/receive_response/complex/wsdl2py/.cvsignore
@@ -0,0 +1 @@
+Registration_services*
diff --git a/doc/examples/client/receive_response/complex/wsdl2py/client.py b/doc/examples/client/receive_response/complex/wsdl2py/client.py
new file mode 100755
index 0000000..904bb51
--- /dev/null
+++ b/doc/examples/client/receive_response/complex/wsdl2py/client.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+import sys
+
+from Registration_services import *
+
+def main():
+ locator = RegistrationServiceLocator()
+ service = locator.getRegistration(tracefile=sys.stdout)
+
+ request = GetUserRequestWrapper()
+ request._UserId = "john_doe"
+
+ response = service.GetUser(request)
+ print ' Age: %d' % response._User._Age
+ print ' Name: %s' % response._User._Name
+ print 'UserId: %s' % response._User._UserId
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/client/receive_response/simple/ServiceProxy/client.py b/doc/examples/client/receive_response/simple/ServiceProxy/client.py
new file mode 100755
index 0000000..de19790
--- /dev/null
+++ b/doc/examples/client/receive_response/simple/ServiceProxy/client.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ server = ServiceProxy('../binding.wsdl', use_wsdl=True)
+
+ print ' Sending: %s' % MESSAGE
+ response = server.echo(Message=MESSAGE)
+ print 'Response: %s' % response['Message']
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/client/receive_response/simple/binding.wsdl b/doc/examples/client/receive_response/simple/binding.wsdl
new file mode 100644
index 0000000..07c5a3c
--- /dev/null
+++ b/doc/examples/client/receive_response/simple/binding.wsdl
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+
+
+<definitions name="Example"
+ targetNamespace="http://wsdl2py.client.pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://wsdl2py.client.pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://wsdl2py.client.pycon.org/"
+ location="interface.wsdl"/>
+
+ <binding name="Binding" type="tns:Example">
+ <soap:binding style="rpc"
+ transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="echo">
+ <soap:operation soapAction="urn:Example#echo"/>
+ <input>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Example" use="encoded"/>
+ </input>
+ <output>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Example" use="encoded"/>
+ </output>
+ </operation>
+ </binding>
+
+ <service name="ExampleService">
+ <documentation>Example web service</documentation>
+ <port name="Example" binding="tns:Binding">
+ <soap:address location="http://127.0.0.1:8080/example.py"/>
+ </port>
+ </service>
+</definitions>
diff --git a/doc/examples/client/receive_response/simple/interface.wsdl b/doc/examples/client/receive_response/simple/interface.wsdl
new file mode 100644
index 0000000..cd80bf0
--- /dev/null
+++ b/doc/examples/client/receive_response/simple/interface.wsdl
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<definitions name="Example"
+ targetNamespace="http://wsdl2py.client.pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://wsdl2py.client.pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <message name="EchoResponse">
+ <part name="Message" type="xsd:string"/>
+ </message>
+
+ <message name="EchoRequest">
+ <part name="Message" type="xsd:string"/>
+ </message>
+
+
+ <portType name="Example">
+ <operation name="echo">
+ <input message="tns:EchoRequest"/>
+ <output message="tns:EchoResponse"/>
+ </operation>
+ </portType>
+
+</definitions>
diff --git a/doc/examples/client/receive_response/simple/server/.cvsignore b/doc/examples/client/receive_response/simple/server/.cvsignore
new file mode 100644
index 0000000..6a41e63
--- /dev/null
+++ b/doc/examples/client/receive_response/simple/server/.cvsignore
@@ -0,0 +1 @@
+Example_services*
diff --git a/doc/examples/client/receive_response/simple/server/server.py b/doc/examples/client/receive_response/simple/server/server.py
new file mode 100755
index 0000000..c878cd8
--- /dev/null
+++ b/doc/examples/client/receive_response/simple/server/server.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from Example_services import EchoResponseWrapper
+
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/doc/examples/client/receive_response/simple/wsdl2py/.cvsignore b/doc/examples/client/receive_response/simple/wsdl2py/.cvsignore
new file mode 100644
index 0000000..6a41e63
--- /dev/null
+++ b/doc/examples/client/receive_response/simple/wsdl2py/.cvsignore
@@ -0,0 +1 @@
+Example_services*
diff --git a/doc/examples/client/receive_response/simple/wsdl2py/README b/doc/examples/client/receive_response/simple/wsdl2py/README
new file mode 100644
index 0000000..3b6d986
--- /dev/null
+++ b/doc/examples/client/receive_response/simple/wsdl2py/README
@@ -0,0 +1,3 @@
+To walk through how this example works:
+ 1.) wsdl2py -f ../binding.wsdl
+ 2.) Examine client.py
diff --git a/doc/examples/client/receive_response/simple/wsdl2py/client.py b/doc/examples/client/receive_response/simple/wsdl2py/client.py
new file mode 100755
index 0000000..cff9713
--- /dev/null
+++ b/doc/examples/client/receive_response/simple/wsdl2py/client.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+import Example_services
+
+MESSAGE = "Hello from Python!"
+
+
+def main():
+ locator = Example_services.ExampleServiceLocator()
+ port = locator.getExample()
+ request = Example_services.EchoRequestWrapper()
+ request._Message = MESSAGE
+
+ try:
+ print ' Sending: %s' % MESSAGE
+ response = port.echo(request)
+ print 'Response: %s' % response._Message
+ except Exception, e:
+ print e
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/client/send_request/complex/binding.wsdl b/doc/examples/client/send_request/complex/binding.wsdl
new file mode 100644
index 0000000..6ec7802
--- /dev/null
+++ b/doc/examples/client/send_request/complex/binding.wsdl
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+
+
+<definitions name="Registration"
+ targetNamespace="http://pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://pycon.org/" location="interface.wsdl"/>
+
+ <binding name="Binding" type="tns:Registration">
+ <soap:binding style="rpc"
+ transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="RegisterUser">
+ <soap:operation soapAction="urn:Registration#RegisterUser"/>
+ <input>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Registration" use="encoded"/>
+ </input>
+ <output>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Registration" use="encoded"/>
+ </output>
+ </operation>
+ </binding>
+
+ <service name="RegistrationService">
+ <documentation>Registration web service</documentation>
+ <port name="Registration" binding="tns:Binding">
+ <soap:address location="http://127.0.0.1:8080/example.py"/>
+ </port>
+ </service>
+</definitions>
diff --git a/doc/examples/client/send_request/complex/interface.wsdl b/doc/examples/client/send_request/complex/interface.wsdl
new file mode 100644
index 0000000..9545640
--- /dev/null
+++ b/doc/examples/client/send_request/complex/interface.wsdl
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+
+<definitions name="Registration"
+ targetNamespace="http://pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:types="http://pycon.org/types"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://pycon.org/types" location="types.xsd"/>
+
+ <message name="RegisterUserResponse">
+ <part name="Message" type="xsd:string"/>
+ </message>
+
+ <message name="RegisterUserRequest">
+ <part name="User" type="types:User"/>
+ </message>
+
+
+ <portType name="Registration">
+ <operation name="RegisterUser">
+ <input message="tns:RegisterUserRequest"/>
+ <output message="tns:RegisterUserResponse"/>
+ </operation>
+ </portType>
+
+</definitions>
diff --git a/doc/examples/client/send_request/complex/manual/.cvsignore b/doc/examples/client/send_request/complex/manual/.cvsignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/doc/examples/client/send_request/complex/manual/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/doc/examples/client/send_request/complex/manual/ComplexTypes.py b/doc/examples/client/send_request/complex/manual/ComplexTypes.py
new file mode 100644
index 0000000..fa62530
--- /dev/null
+++ b/doc/examples/client/send_request/complex/manual/ComplexTypes.py
@@ -0,0 +1,13 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'types:User')
diff --git a/doc/examples/client/send_request/complex/manual/client.py b/doc/examples/client/send_request/complex/manual/client.py
new file mode 100755
index 0000000..05cca14
--- /dev/null
+++ b/doc/examples/client/send_request/complex/manual/client.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+from ComplexTypes import User
+
+from ZSI import ServiceProxy
+
+import sys
+
+
+def main():
+ user = User('john_doe', 'John Doe', 25)
+ nsdict = { 'types' : 'http://pycon.org/types' }
+ registration = ServiceProxy('../binding.wsdl',
+ nsdict=nsdict,
+ tracefile=sys.stdout)
+ response = registration.RegisterUser(user)
+ print response
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/client/send_request/complex/server/.cvsignore b/doc/examples/client/send_request/complex/server/.cvsignore
new file mode 100644
index 0000000..68eb1db
--- /dev/null
+++ b/doc/examples/client/send_request/complex/server/.cvsignore
@@ -0,0 +1,2 @@
+*.pyc
+Registration_services*
diff --git a/doc/examples/client/send_request/complex/server/ComplexTypes.py b/doc/examples/client/send_request/complex/server/ComplexTypes.py
new file mode 100644
index 0000000..00918f7
--- /dev/null
+++ b/doc/examples/client/send_request/complex/server/ComplexTypes.py
@@ -0,0 +1,13 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'User')
diff --git a/doc/examples/client/send_request/complex/server/server.py b/doc/examples/client/send_request/complex/server/server.py
new file mode 100755
index 0000000..0459540
--- /dev/null
+++ b/doc/examples/client/send_request/complex/server/server.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+import ComplexTypes as MyComplexTypes
+
+from Registration_services import RegisterUserResponseWrapper
+
+
+def RegisterUser(user):
+ response = RegisterUserResponseWrapper()
+ response._Message = "OK"
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080, typesmodule=(MyComplexTypes,))
diff --git a/doc/examples/client/send_request/complex/types.xsd b/doc/examples/client/send_request/complex/types.xsd
new file mode 100644
index 0000000..7a1f17f
--- /dev/null
+++ b/doc/examples/client/send_request/complex/types.xsd
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+
+<schema targetNamespace="http://pycon.org/types"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:tns="http://pycon.org/types">
+
+ <complexType name="User">
+ <sequence>
+ <element name="UserId" type="string"/>
+ <element name="Name" type="string"/>
+ <element name="Age" type="integer"/>
+ </sequence>
+ </complexType>
+
+</schema>
diff --git a/doc/examples/client/send_request/complex/wsdl2py/.cvsignore b/doc/examples/client/send_request/complex/wsdl2py/.cvsignore
new file mode 100644
index 0000000..4086804
--- /dev/null
+++ b/doc/examples/client/send_request/complex/wsdl2py/.cvsignore
@@ -0,0 +1 @@
+Registration_services*
diff --git a/doc/examples/client/send_request/complex/wsdl2py/client.py b/doc/examples/client/send_request/complex/wsdl2py/client.py
new file mode 100755
index 0000000..c58903e
--- /dev/null
+++ b/doc/examples/client/send_request/complex/wsdl2py/client.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+from Registration_services import *
+
+import sys
+
+def main():
+ user = ns1.User_Def()
+ user._UserId = 'john_doe'
+ user._Name = 'John Doe'
+ user._Age = 25
+
+ locator = RegistrationServiceLocator()
+ registration = locator.getRegistration(tracefile=sys.stdout)
+
+ request = RegisterUserRequestWrapper()
+ request._User = user
+ response = registration.RegisterUser(request)
+ print response._Message
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/client/send_request/simple/Binding/client.py b/doc/examples/client/send_request/simple/Binding/client.py
new file mode 100755
index 0000000..57a409f
--- /dev/null
+++ b/doc/examples/client/send_request/simple/Binding/client.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+from ZSI import Binding
+
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ binding = Binding(url='http://localhost:8080/server.py')
+ print ' Sending: %s' % MESSAGE
+ response = binding.echo(MESSAGE)
+ print 'Response: %s' % MESSAGE
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/client/send_request/simple/README b/doc/examples/client/send_request/simple/README
new file mode 100644
index 0000000..bceccfc
--- /dev/null
+++ b/doc/examples/client/send_request/simple/README
@@ -0,0 +1,3 @@
+To run these examples, first get the server running. To do that, go into
+the server directory and follow the instructions in the README. Then go
+into each of the different client directories and review their instructions.
diff --git a/doc/examples/client/send_request/simple/ServiceProxy/client.py b/doc/examples/client/send_request/simple/ServiceProxy/client.py
new file mode 100755
index 0000000..de19790
--- /dev/null
+++ b/doc/examples/client/send_request/simple/ServiceProxy/client.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ server = ServiceProxy('../binding.wsdl', use_wsdl=True)
+
+ print ' Sending: %s' % MESSAGE
+ response = server.echo(Message=MESSAGE)
+ print 'Response: %s' % response['Message']
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/client/send_request/simple/binding.wsdl b/doc/examples/client/send_request/simple/binding.wsdl
new file mode 100644
index 0000000..07c5a3c
--- /dev/null
+++ b/doc/examples/client/send_request/simple/binding.wsdl
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+
+
+<definitions name="Example"
+ targetNamespace="http://wsdl2py.client.pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://wsdl2py.client.pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://wsdl2py.client.pycon.org/"
+ location="interface.wsdl"/>
+
+ <binding name="Binding" type="tns:Example">
+ <soap:binding style="rpc"
+ transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="echo">
+ <soap:operation soapAction="urn:Example#echo"/>
+ <input>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Example" use="encoded"/>
+ </input>
+ <output>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Example" use="encoded"/>
+ </output>
+ </operation>
+ </binding>
+
+ <service name="ExampleService">
+ <documentation>Example web service</documentation>
+ <port name="Example" binding="tns:Binding">
+ <soap:address location="http://127.0.0.1:8080/example.py"/>
+ </port>
+ </service>
+</definitions>
diff --git a/doc/examples/client/send_request/simple/interface.wsdl b/doc/examples/client/send_request/simple/interface.wsdl
new file mode 100644
index 0000000..cd80bf0
--- /dev/null
+++ b/doc/examples/client/send_request/simple/interface.wsdl
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<definitions name="Example"
+ targetNamespace="http://wsdl2py.client.pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://wsdl2py.client.pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <message name="EchoResponse">
+ <part name="Message" type="xsd:string"/>
+ </message>
+
+ <message name="EchoRequest">
+ <part name="Message" type="xsd:string"/>
+ </message>
+
+
+ <portType name="Example">
+ <operation name="echo">
+ <input message="tns:EchoRequest"/>
+ <output message="tns:EchoResponse"/>
+ </operation>
+ </portType>
+
+</definitions>
diff --git a/doc/examples/client/send_request/simple/server/.cvsignore b/doc/examples/client/send_request/simple/server/.cvsignore
new file mode 100644
index 0000000..6a41e63
--- /dev/null
+++ b/doc/examples/client/send_request/simple/server/.cvsignore
@@ -0,0 +1 @@
+Example_services*
diff --git a/doc/examples/client/send_request/simple/server/server.py b/doc/examples/client/send_request/simple/server/server.py
new file mode 100755
index 0000000..97c5c38
--- /dev/null
+++ b/doc/examples/client/send_request/simple/server/server.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from Example_services import *
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
+
diff --git a/doc/examples/client/send_request/simple/wsdl2py/.cvsignore b/doc/examples/client/send_request/simple/wsdl2py/.cvsignore
new file mode 100644
index 0000000..6a41e63
--- /dev/null
+++ b/doc/examples/client/send_request/simple/wsdl2py/.cvsignore
@@ -0,0 +1 @@
+Example_services*
diff --git a/doc/examples/client/send_request/simple/wsdl2py/README b/doc/examples/client/send_request/simple/wsdl2py/README
new file mode 100644
index 0000000..3b6d986
--- /dev/null
+++ b/doc/examples/client/send_request/simple/wsdl2py/README
@@ -0,0 +1,3 @@
+To walk through how this example works:
+ 1.) wsdl2py -f ../binding.wsdl
+ 2.) Examine client.py
diff --git a/doc/examples/client/send_request/simple/wsdl2py/client.py b/doc/examples/client/send_request/simple/wsdl2py/client.py
new file mode 100755
index 0000000..cff9713
--- /dev/null
+++ b/doc/examples/client/send_request/simple/wsdl2py/client.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+import Example_services
+
+MESSAGE = "Hello from Python!"
+
+
+def main():
+ locator = Example_services.ExampleServiceLocator()
+ port = locator.getExample()
+ request = Example_services.EchoRequestWrapper()
+ request._Message = MESSAGE
+
+ try:
+ print ' Sending: %s' % MESSAGE
+ response = port.echo(request)
+ print 'Response: %s' % response._Message
+ except Exception, e:
+ print e
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/server/receive_request/complex/binding.wsdl b/doc/examples/server/receive_request/complex/binding.wsdl
new file mode 100644
index 0000000..6ec7802
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/binding.wsdl
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+
+
+<definitions name="Registration"
+ targetNamespace="http://pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://pycon.org/" location="interface.wsdl"/>
+
+ <binding name="Binding" type="tns:Registration">
+ <soap:binding style="rpc"
+ transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="RegisterUser">
+ <soap:operation soapAction="urn:Registration#RegisterUser"/>
+ <input>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Registration" use="encoded"/>
+ </input>
+ <output>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Registration" use="encoded"/>
+ </output>
+ </operation>
+ </binding>
+
+ <service name="RegistrationService">
+ <documentation>Registration web service</documentation>
+ <port name="Registration" binding="tns:Binding">
+ <soap:address location="http://127.0.0.1:8080/example.py"/>
+ </port>
+ </service>
+</definitions>
diff --git a/doc/examples/server/receive_request/complex/client/.cvsignore b/doc/examples/server/receive_request/complex/client/.cvsignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/client/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/doc/examples/server/receive_request/complex/client/ComplexTypes.py b/doc/examples/server/receive_request/complex/client/ComplexTypes.py
new file mode 100644
index 0000000..fa62530
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/client/ComplexTypes.py
@@ -0,0 +1,13 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'types:User')
diff --git a/doc/examples/server/receive_request/complex/client/client.py b/doc/examples/server/receive_request/complex/client/client.py
new file mode 100755
index 0000000..16b6ff6
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/client/client.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+
+from ComplexTypes import User
+
+import sys
+
+def main():
+ user = User('john_doe', 'John Doe', 25)
+ nsdict = { 'types' : 'http://pycon.org/typs' }
+ registration = ServiceProxy('../binding.wsdl',
+ nsdict=nsdict,
+ tracefile=sys.stdout)
+ response = registration.RegisterUser(user)
+ print response
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/server/receive_request/complex/interface.wsdl b/doc/examples/server/receive_request/complex/interface.wsdl
new file mode 100644
index 0000000..9545640
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/interface.wsdl
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+
+<definitions name="Registration"
+ targetNamespace="http://pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:types="http://pycon.org/types"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://pycon.org/types" location="types.xsd"/>
+
+ <message name="RegisterUserResponse">
+ <part name="Message" type="xsd:string"/>
+ </message>
+
+ <message name="RegisterUserRequest">
+ <part name="User" type="types:User"/>
+ </message>
+
+
+ <portType name="Registration">
+ <operation name="RegisterUser">
+ <input message="tns:RegisterUserRequest"/>
+ <output message="tns:RegisterUserResponse"/>
+ </operation>
+ </portType>
+
+</definitions>
diff --git a/doc/examples/server/receive_request/complex/manual/.cvsignore b/doc/examples/server/receive_request/complex/manual/.cvsignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/manual/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/doc/examples/server/receive_request/complex/manual/ComplexTypes.py b/doc/examples/server/receive_request/complex/manual/ComplexTypes.py
new file mode 100644
index 0000000..00918f7
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/manual/ComplexTypes.py
@@ -0,0 +1,13 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'User')
diff --git a/doc/examples/server/receive_request/complex/manual/server.py b/doc/examples/server/receive_request/complex/manual/server.py
new file mode 100755
index 0000000..b7d1f48
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/manual/server.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+import ComplexTypes as MyComplexTypes
+
+
+def RegisterUser(user):
+ return "OK"
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080, typesmodule=(MyComplexTypes,))
diff --git a/doc/examples/server/receive_request/complex/types.xsd b/doc/examples/server/receive_request/complex/types.xsd
new file mode 100644
index 0000000..7a1f17f
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/types.xsd
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+
+<schema targetNamespace="http://pycon.org/types"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:tns="http://pycon.org/types">
+
+ <complexType name="User">
+ <sequence>
+ <element name="UserId" type="string"/>
+ <element name="Name" type="string"/>
+ <element name="Age" type="integer"/>
+ </sequence>
+ </complexType>
+
+</schema>
diff --git a/doc/examples/server/receive_request/complex/wsdl2py/.cvsignore b/doc/examples/server/receive_request/complex/wsdl2py/.cvsignore
new file mode 100644
index 0000000..4086804
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/wsdl2py/.cvsignore
@@ -0,0 +1 @@
+Registration_services*
diff --git a/doc/examples/server/receive_request/complex/wsdl2py/server.py b/doc/examples/server/receive_request/complex/wsdl2py/server.py
new file mode 100755
index 0000000..92e1abc
--- /dev/null
+++ b/doc/examples/server/receive_request/complex/wsdl2py/server.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+import Registration_services_types
+from Registration_services import RegisterUserResponseWrapper
+
+def RegisterUser(user):
+ response = RegisterUserResponseWrapper()
+ response._Message = "OK"
+ return response
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080, typesmodule=(Registration_services_types,))
diff --git a/doc/examples/server/receive_request/simple/CGI/README b/doc/examples/server/receive_request/simple/CGI/README
new file mode 100644
index 0000000..fe5521e
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/CGI/README
@@ -0,0 +1,3 @@
+To run the CGI server, do the following:
+ 1.) wsdl2py -f ../binding.wsdl
+ 2.) Start apache
diff --git a/doc/examples/server/receive_request/simple/CGI/cgi.py b/doc/examples/server/receive_request/simple/CGI/cgi.py
new file mode 100755
index 0000000..5fbf61a
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/CGI/cgi.py
@@ -0,0 +1,10 @@
+from Echo_services import EchoResponseWrapper
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
+
+
+from ZSI import dispatch
+dispatch.AsCGI()
diff --git a/doc/examples/server/receive_request/simple/binding.wsdl b/doc/examples/server/receive_request/simple/binding.wsdl
new file mode 100644
index 0000000..07c5a3c
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/binding.wsdl
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+
+
+<definitions name="Example"
+ targetNamespace="http://wsdl2py.client.pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://wsdl2py.client.pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://wsdl2py.client.pycon.org/"
+ location="interface.wsdl"/>
+
+ <binding name="Binding" type="tns:Example">
+ <soap:binding style="rpc"
+ transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="echo">
+ <soap:operation soapAction="urn:Example#echo"/>
+ <input>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Example" use="encoded"/>
+ </input>
+ <output>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Example" use="encoded"/>
+ </output>
+ </operation>
+ </binding>
+
+ <service name="ExampleService">
+ <documentation>Example web service</documentation>
+ <port name="Example" binding="tns:Binding">
+ <soap:address location="http://127.0.0.1:8080/example.py"/>
+ </port>
+ </service>
+</definitions>
diff --git a/doc/examples/server/receive_request/simple/client/client.py b/doc/examples/server/receive_request/simple/client/client.py
new file mode 100755
index 0000000..78bfe9e
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/client/client.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+import sys
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ server = ServiceProxy('../binding.wsdl', use_wsdl=True)
+
+ print ' Sending: %s' % MESSAGE
+ response = server.echo(Message=MESSAGE)
+ print 'Response: %s' % response['Message']
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/server/receive_request/simple/interface.wsdl b/doc/examples/server/receive_request/simple/interface.wsdl
new file mode 100644
index 0000000..cd80bf0
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/interface.wsdl
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<definitions name="Example"
+ targetNamespace="http://wsdl2py.client.pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://wsdl2py.client.pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <message name="EchoResponse">
+ <part name="Message" type="xsd:string"/>
+ </message>
+
+ <message name="EchoRequest">
+ <part name="Message" type="xsd:string"/>
+ </message>
+
+
+ <portType name="Example">
+ <operation name="echo">
+ <input message="tns:EchoRequest"/>
+ <output message="tns:EchoResponse"/>
+ </operation>
+ </portType>
+
+</definitions>
diff --git a/doc/examples/server/receive_request/simple/mod_python/.cvsignore b/doc/examples/server/receive_request/simple/mod_python/.cvsignore
new file mode 100644
index 0000000..6a41e63
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/mod_python/.cvsignore
@@ -0,0 +1 @@
+Example_services*
diff --git a/doc/examples/server/receive_request/simple/mod_python/MyHandler.py b/doc/examples/server/receive_request/simple/mod_python/MyHandler.py
new file mode 100644
index 0000000..b8a2201
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/mod_python/MyHandler.py
@@ -0,0 +1,6 @@
+from Example_services import EchoResponseWrapper
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
diff --git a/doc/examples/server/receive_request/simple/mod_python/README b/doc/examples/server/receive_request/simple/mod_python/README
new file mode 100644
index 0000000..fe5521e
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/mod_python/README
@@ -0,0 +1,3 @@
+To run the CGI server, do the following:
+ 1.) wsdl2py -f ../binding.wsdl
+ 2.) Start apache
diff --git a/doc/examples/server/receive_request/simple/mod_python/mod_python.py b/doc/examples/server/receive_request/simple/mod_python/mod_python.py
new file mode 100644
index 0000000..771265e
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/mod_python/mod_python.py
@@ -0,0 +1,10 @@
+from ZSI import dispatch
+from mod_python import apache
+
+mod = __import__('encodings.utf_8', globals(), locals(), '*')
+mod = __import__('encodings.utf_16_be', globals(), locals(), '*')
+
+import MyHandler
+def handler(req):
+ dispatch.AsHandler(modules=(MyHandler,), request=req)
+ return apache.OK
diff --git a/doc/examples/server/receive_request/simple/standalone/.cvsignore b/doc/examples/server/receive_request/simple/standalone/.cvsignore
new file mode 100644
index 0000000..6a41e63
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/standalone/.cvsignore
@@ -0,0 +1 @@
+Example_services*
diff --git a/doc/examples/server/receive_request/simple/standalone/README b/doc/examples/server/receive_request/simple/standalone/README
new file mode 100644
index 0000000..a8bc278
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/standalone/README
@@ -0,0 +1,3 @@
+To run the standalone server, do the following:
+ 1.) wsdl2py -f ../binding.wsdl
+ 2.) ./standalone.py
diff --git a/doc/examples/server/receive_request/simple/standalone/standalone.py b/doc/examples/server/receive_request/simple/standalone/standalone.py
new file mode 100755
index 0000000..c878cd8
--- /dev/null
+++ b/doc/examples/server/receive_request/simple/standalone/standalone.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from Example_services import EchoResponseWrapper
+
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/doc/examples/server/send_response/complex/binding.wsdl b/doc/examples/server/send_response/complex/binding.wsdl
new file mode 100644
index 0000000..5a95911
--- /dev/null
+++ b/doc/examples/server/send_response/complex/binding.wsdl
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+
+
+<definitions name="Registration"
+ targetNamespace="http://pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://pycon.org/" location="interface.wsdl"/>
+
+ <binding name="Binding" type="tns:Registration">
+ <soap:binding style="rpc"
+ transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="GetUser">
+ <soap:operation soapAction="urn:Registration#GetUser"/>
+ <input>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Registration" use="encoded"/>
+ </input>
+ <output>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Registration" use="encoded"/>
+ </output>
+ </operation>
+ </binding>
+
+ <service name="RegistrationService">
+ <documentation>Registration web service</documentation>
+ <port name="Registration" binding="tns:Binding">
+ <soap:address location="http://127.0.0.1:8080/example.py"/>
+ </port>
+ </service>
+</definitions>
diff --git a/doc/examples/server/send_response/complex/client/.cvsignore b/doc/examples/server/send_response/complex/client/.cvsignore
new file mode 100644
index 0000000..4086804
--- /dev/null
+++ b/doc/examples/server/send_response/complex/client/.cvsignore
@@ -0,0 +1 @@
+Registration_services*
diff --git a/doc/examples/server/send_response/complex/client/client.py b/doc/examples/server/send_response/complex/client/client.py
new file mode 100755
index 0000000..904bb51
--- /dev/null
+++ b/doc/examples/server/send_response/complex/client/client.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+import sys
+
+from Registration_services import *
+
+def main():
+ locator = RegistrationServiceLocator()
+ service = locator.getRegistration(tracefile=sys.stdout)
+
+ request = GetUserRequestWrapper()
+ request._UserId = "john_doe"
+
+ response = service.GetUser(request)
+ print ' Age: %d' % response._User._Age
+ print ' Name: %s' % response._User._Name
+ print 'UserId: %s' % response._User._UserId
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/server/send_response/complex/interface.wsdl b/doc/examples/server/send_response/complex/interface.wsdl
new file mode 100644
index 0000000..40963ec
--- /dev/null
+++ b/doc/examples/server/send_response/complex/interface.wsdl
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+
+<definitions name="Registration"
+ targetNamespace="http://pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:types="http://pycon.org/types"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://pycon.org/types" location="types.xsd"/>
+
+ <message name="GetUserResponse">
+ <part name="User" type="types:User"/>
+ </message>
+
+ <message name="GetUserRequest">
+ <part name="UserId" type="xsd:string"/>
+ </message>
+
+
+ <portType name="Registration">
+ <operation name="GetUser">
+ <input message="tns:GetUserRequest"/>
+ <output message="tns:GetUserResponse"/>
+ </operation>
+ </portType>
+
+</definitions>
diff --git a/doc/examples/server/send_response/complex/manual/.cvsignore b/doc/examples/server/send_response/complex/manual/.cvsignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/doc/examples/server/send_response/complex/manual/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/doc/examples/server/send_response/complex/manual/ComplexTypes.py b/doc/examples/server/send_response/complex/manual/ComplexTypes.py
new file mode 100644
index 0000000..7dae26a
--- /dev/null
+++ b/doc/examples/server/send_response/complex/manual/ComplexTypes.py
@@ -0,0 +1,14 @@
+from ZSI import TC
+
+class User:
+ def __init__(self, name=None, userId=None, age=-1):
+ self.Name = name
+ self.UserId = userId
+ self.Age = age
+
+User.typecode = TC.Struct(User,
+ [TC.String('Name', oname='Name'),
+ TC.String('UserId', oname='UserId'),
+ TC.Iinteger('Age', oname='Age')],
+ 'User')
+
diff --git a/doc/examples/server/send_response/complex/manual/server.py b/doc/examples/server/send_response/complex/manual/server.py
new file mode 100755
index 0000000..525ca51
--- /dev/null
+++ b/doc/examples/server/send_response/complex/manual/server.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from ComplexTypes import User
+
+def GetUser(user_id):
+ user = User(user_id, 'John Doe', 28)
+ return user
+
+if __name__ == '__main__':
+ nsdict = { 'types' : 'http://pycon.org/types' }
+ dispatch.AsServer(port=8080, nsdict=nsdict)
diff --git a/doc/examples/server/send_response/complex/types.xsd b/doc/examples/server/send_response/complex/types.xsd
new file mode 100644
index 0000000..7a1f17f
--- /dev/null
+++ b/doc/examples/server/send_response/complex/types.xsd
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+
+<schema targetNamespace="http://pycon.org/types"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:tns="http://pycon.org/types">
+
+ <complexType name="User">
+ <sequence>
+ <element name="UserId" type="string"/>
+ <element name="Name" type="string"/>
+ <element name="Age" type="integer"/>
+ </sequence>
+ </complexType>
+
+</schema>
diff --git a/doc/examples/server/send_response/complex/wsdl2py/.cvsignore b/doc/examples/server/send_response/complex/wsdl2py/.cvsignore
new file mode 100644
index 0000000..4086804
--- /dev/null
+++ b/doc/examples/server/send_response/complex/wsdl2py/.cvsignore
@@ -0,0 +1 @@
+Registration_services*
diff --git a/doc/examples/server/send_response/complex/wsdl2py/server.py b/doc/examples/server/send_response/complex/wsdl2py/server.py
new file mode 100755
index 0000000..3379339
--- /dev/null
+++ b/doc/examples/server/send_response/complex/wsdl2py/server.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+from Registration_services import GetUserResponseWrapper
+from Registration_services_types import ns1
+
+
+def GetUser(user_id):
+ user = ns1.User_Def()
+ user._UserId = user_id
+ user._Name = "John Doe"
+ user._Age = 32
+
+ response = GetUserResponseWrapper()
+ response._User = user
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/doc/examples/server/send_response/simple/binding.wsdl b/doc/examples/server/send_response/simple/binding.wsdl
new file mode 100644
index 0000000..07c5a3c
--- /dev/null
+++ b/doc/examples/server/send_response/simple/binding.wsdl
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+
+
+<definitions name="Example"
+ targetNamespace="http://wsdl2py.client.pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://wsdl2py.client.pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <import namespace="http://wsdl2py.client.pycon.org/"
+ location="interface.wsdl"/>
+
+ <binding name="Binding" type="tns:Example">
+ <soap:binding style="rpc"
+ transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="echo">
+ <soap:operation soapAction="urn:Example#echo"/>
+ <input>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Example" use="encoded"/>
+ </input>
+ <output>
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ namespace="urn:Example" use="encoded"/>
+ </output>
+ </operation>
+ </binding>
+
+ <service name="ExampleService">
+ <documentation>Example web service</documentation>
+ <port name="Example" binding="tns:Binding">
+ <soap:address location="http://127.0.0.1:8080/example.py"/>
+ </port>
+ </service>
+</definitions>
diff --git a/doc/examples/server/send_response/simple/interface.wsdl b/doc/examples/server/send_response/simple/interface.wsdl
new file mode 100644
index 0000000..cd80bf0
--- /dev/null
+++ b/doc/examples/server/send_response/simple/interface.wsdl
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<definitions name="Example"
+ targetNamespace="http://wsdl2py.client.pycon.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:tns="http://wsdl2py.client.pycon.org/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <message name="EchoResponse">
+ <part name="Message" type="xsd:string"/>
+ </message>
+
+ <message name="EchoRequest">
+ <part name="Message" type="xsd:string"/>
+ </message>
+
+
+ <portType name="Example">
+ <operation name="echo">
+ <input message="tns:EchoRequest"/>
+ <output message="tns:EchoResponse"/>
+ </operation>
+ </portType>
+
+</definitions>
diff --git a/doc/examples/server/send_response/simple/no_typecode/client.py b/doc/examples/server/send_response/simple/no_typecode/client.py
new file mode 100755
index 0000000..e65af11
--- /dev/null
+++ b/doc/examples/server/send_response/simple/no_typecode/client.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+import sys
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ server = ServiceProxy('../binding.wsdl', use_wsdl=False)
+
+ print ' Sending: %s' % MESSAGE
+ response = server.echo(MESSAGE)
+ print 'Response: %s' % response
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/server/send_response/simple/no_typecode/server.py b/doc/examples/server/send_response/simple/no_typecode/server.py
new file mode 100755
index 0000000..14abe3a
--- /dev/null
+++ b/doc/examples/server/send_response/simple/no_typecode/server.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+
+
+def echo(message):
+ return message
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/doc/examples/server/send_response/simple/wsdl2py/.cvsignore b/doc/examples/server/send_response/simple/wsdl2py/.cvsignore
new file mode 100644
index 0000000..6a41e63
--- /dev/null
+++ b/doc/examples/server/send_response/simple/wsdl2py/.cvsignore
@@ -0,0 +1 @@
+Example_services*
diff --git a/doc/examples/server/send_response/simple/wsdl2py/README b/doc/examples/server/send_response/simple/wsdl2py/README
new file mode 100644
index 0000000..f5dc428
--- /dev/null
+++ b/doc/examples/server/send_response/simple/wsdl2py/README
@@ -0,0 +1,4 @@
+To run this example, do this:
+ 1.) wsdl2py -f ../binding.wsdl
+ 2.) ./server.py
+ 3.) ./client.py
diff --git a/doc/examples/server/send_response/simple/wsdl2py/client.py b/doc/examples/server/send_response/simple/wsdl2py/client.py
new file mode 100755
index 0000000..78bfe9e
--- /dev/null
+++ b/doc/examples/server/send_response/simple/wsdl2py/client.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from ZSI import ServiceProxy
+import sys
+
+MESSAGE = "Hello from Python!"
+
+def main():
+ server = ServiceProxy('../binding.wsdl', use_wsdl=True)
+
+ print ' Sending: %s' % MESSAGE
+ response = server.echo(Message=MESSAGE)
+ print 'Response: %s' % response['Message']
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/examples/server/send_response/simple/wsdl2py/server.py b/doc/examples/server/send_response/simple/wsdl2py/server.py
new file mode 100755
index 0000000..ccaf220
--- /dev/null
+++ b/doc/examples/server/send_response/simple/wsdl2py/server.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+from ZSI import dispatch
+from Example_services import EchoResponseWrapper
+
+def echo(message):
+ response = EchoResponseWrapper()
+ response._Message = message
+ return response
+
+
+if __name__ == '__main__':
+ dispatch.AsServer(port=8080)
diff --git a/doc/guide.css b/doc/guide.css
new file mode 100644
index 0000000..06a613c
--- /dev/null
+++ b/doc/guide.css
@@ -0,0 +1,243 @@
+/*
+ * The first part of this is the standard CSS generated by LaTeX2HTML,
+ * with the "empty" declarations removed.
+ */
+
+/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */
+.math { font-family: "Century Schoolbook", serif; }
+.math i { font-family: "Century Schoolbook", serif;
+ font-weight: bold }
+.boldmath { font-family: "Century Schoolbook", serif;
+ font-weight: bold }
+
+/*
+ * Implement both fixed-size and relative sizes.
+ *
+ * I think these can be safely removed, as it doesn't appear that
+ * LaTeX2HTML ever generates these, even though these are carried
+ * over from the LaTeX2HTML stylesheet.
+ */
+small.xtiny { font-size : xx-small; }
+small.tiny { font-size : x-small; }
+small.scriptsize { font-size : smaller; }
+small.footnotesize { font-size : small; }
+big.xlarge { font-size : large; }
+big.xxlarge { font-size : x-large; }
+big.huge { font-size : larger; }
+big.xhuge { font-size : xx-large; }
+
+/*
+ * Document-specific styles come next;
+ * these are added for the Python documentation.
+ *
+ * Note that the size specifications for the H* elements are because
+ * Netscape on Solaris otherwise doesn't get it right; they all end up
+ * the normal text size.
+ */
+
+body { color: #000000;
+ background-color: #ffffff; }
+
+a:link:active { color: #ff0000; }
+a:link:hover { background-color: #bbeeff; }
+a:visited:hover { background-color: #bbeeff; }
+a:visited { color: #551a8b; }
+a:link { color: #0000bb; }
+
+h1, h2, h3, h4, h5, h6 { font-family: avantgarde, sans-serif;
+ font-weight: bold; }
+h1 { font-size: 180%; }
+h2 { font-size: 150%; }
+h3, h4 { font-size: 120%; }
+
+/* These are section titles used in navigation links, so make sure we
+ * match the section header font here, even it not the weight.
+ */
+.sectref { font-family: avantgarde, sans-serif; }
+/* And the label before the titles in navigation: */
+.navlabel { font-size: 85%; }
+
+
+/* LaTeX2HTML insists on inserting <br> elements into headers which
+ * are marked with \label. This little bit of CSS magic ensures that
+ * these elements don't cause spurious whitespace to be added.
+ */
+h1>br, h2>br, h3>br,
+h4>br, h5>br, h6>br { display: none; }
+
+code, tt { font-family: "lucida typewriter", lucidatypewriter,
+ monospace; }
+var { font-family: times, serif;
+ font-style: italic;
+ font-weight: normal; }
+
+.Unix { font-variant: small-caps; }
+
+.typelabel { font-family: lucida, sans-serif; }
+
+.navigation td { background-color: #99ccff;
+ font-weight: bold;
+ font-family: avantgarde, sans-serif;
+ font-size: 110%; }
+
+div.warning { background-color: #fffaf0;
+ border: thin solid black;
+ padding: 1em;
+ margin-left: 2em;
+ margin-right: 2em; }
+
+div.warning .label { font-family: sans-serif;
+ font-size: 110%;
+ margin-right: 0.5em; }
+
+div.note { background-color: #fffaf0;
+ border: thin solid black;
+ padding: 1em;
+ margin-left: 2em;
+ margin-right: 2em; }
+
+div.note .label { margin-right: 0.5em;
+ font-family: sans-serif; }
+
+address { font-size: 80%; }
+.release-info { font-style: italic;
+ font-size: 80%; }
+
+.titlegraphic { vertical-align: top; }
+
+.verbatim pre { color: #00008b;
+ font-family: "lucida typewriter", lucidatypewriter,
+ monospace;
+ font-size: 90%; }
+.verbatim { margin-left: 2em; }
+.verbatim .footer { padding: 0.05in;
+ font-size: 85%;
+ background-color: #99ccff;
+ margin-right: 0.5in; }
+
+.grammar { background-color: #99ccff;
+ margin-right: 0.5in;
+ padding: 0.05in; }
+.grammar-footer { padding: 0.05in;
+ font-size: 85%; }
+.grammartoken { font-family: "lucida typewriter", lucidatypewriter,
+ monospace; }
+
+.productions { background-color: #bbeeff; }
+.productions a:active { color: #ff0000; }
+.productions a:link:hover { background-color: #99ccff; }
+.productions a:visited:hover { background-color: #99ccff; }
+.productions a:visited { color: #551a8b; }
+.productions a:link { color: #0000bb; }
+.productions table { vertical-align: baseline;
+ empty-cells: show; }
+.productions > table td,
+.productions > table th { padding: 2px; }
+.productions > table td:first-child,
+.productions > table td:last-child {
+ font-family: "lucida typewriter",
+ lucidatypewriter,
+ monospace;
+ }
+/* same as the second selector above, but expressed differently for Opera */
+.productions > table td:first-child + td + td {
+ font-family: "lucida typewriter",
+ lucidatypewriter,
+ monospace;
+ vertical-align: baseline;
+ }
+.productions > table td:first-child + td {
+ padding-left: 1em;
+ padding-right: 1em;
+ }
+.productions > table tr { vertical-align: baseline; }
+
+.email { font-family: avantgarde, sans-serif; }
+.mailheader { font-family: avantgarde, sans-serif; }
+.mimetype { font-family: avantgarde, sans-serif; }
+.newsgroup { font-family: avantgarde, sans-serif; }
+.url { font-family: avantgarde, sans-serif; }
+.file { font-family: avantgarde, sans-serif; }
+.guilabel { font-family: avantgarde, sans-serif; }
+
+.realtable { border-collapse: collapse;
+ border-color: black;
+ border-style: solid;
+ border-width: 0px 0px 2px 0px;
+ empty-cells: show;
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: 0.4em;
+ padding-right: 0.4em;
+ }
+.realtable tbody { vertical-align: baseline; }
+.realtable tfoot { display: table-footer-group; }
+.realtable thead { background-color: #99ccff;
+ border-width: 0px 0px 2px 1px;
+ display: table-header-group;
+ font-family: avantgarde, sans-serif;
+ font-weight: bold;
+ vertical-align: baseline;
+ }
+.realtable thead :first-child {
+ border-width: 0px 0px 2px 0px;
+ }
+.realtable thead th { border-width: 0px 0px 2px 1px }
+.realtable td,
+.realtable th { border-color: black;
+ border-style: solid;
+ border-width: 0px 0px 1px 1px;
+ padding-left: 0.4em;
+ padding-right: 0.4em;
+ }
+.realtable td:first-child,
+.realtable th:first-child {
+ border-left-width: 0px;
+ vertical-align: baseline;
+ }
+.center { text-align: center; }
+.left { text-align: left; }
+.right { text-align: right; }
+
+.refcount-info { font-style: italic; }
+.refcount-info .value { font-weight: bold;
+ color: #006600; }
+
+/*
+ * Some decoration for the "See also:" blocks, in part inspired by some of
+ * the styling on Lars Marius Garshol's XSA pages.
+ * (The blue in the navigation bars is #99CCFF.)
+ */
+.seealso { background-color: #fffaf0;
+ border: thin solid black;
+ padding: 0pt 1em 4pt 1em; }
+
+.seealso > .heading { font-size: 110%;
+ font-weight: bold; }
+
+/*
+ * Class 'availability' is used for module availability statements at
+ * the top of modules.
+ */
+.availability .platform { font-weight: bold; }
+
+
+/*
+ * Additional styles for the distutils package.
+ */
+.du-command { font-family: monospace; }
+.du-option { font-family: avantgarde, sans-serif; }
+.du-filevar { font-family: avantgarde, sans-serif;
+ font-style: italic; }
+.du-xxx:before { content: "** ";
+ font-weight: bold; }
+.du-xxx:after { content: " **";
+ font-weight: bold; }
+
+
+/*
+ * Some specialization for printed output.
+ */
+@media print {
+ .online-navigation { display: none; }
+ }
diff --git a/doc/guide.html b/doc/guide.html
new file mode 100644
index 0000000..14560b0
--- /dev/null
+++ b/doc/guide.html
@@ -0,0 +1,814 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+<link rel="STYLESHEET" href="guide.css" type='text/css' />
+<link rel="first" href="guide.html" title='ZSI: The Zolera Soap Infrastructure
+<BR>
+User's Guide' />
+<link rel='contents' href='guide.html' title="Contents" />
+<link rel='last' href='about.html' title='About this document...' />
+<link rel='help' href='about.html' title='About this document...' />
+<meta name='aesop' content='information' />
+<title>ZSI: The Zolera Soap Infrastructure User's Guide</title>
+</head>
+<body>
+<DIV CLASS="navigation">
+<div id='top-navigation-panel' xml:id='top-navigation-panel'>
+<table align="center" width="100%" cellpadding="0" cellspacing="2">
+<tr>
+<td class='online-navigation'><img src='previous.png'
+ border='0' height='32' alt='Previous Page' width='32' /></td>
+<td class='online-navigation'><img src='up.png'
+ border='0' height='32' alt='Up One Level' width='32' /></td>
+<td class='online-navigation'><img src='next.png'
+ border='0' height='32' alt='Next Page' width='32' /></td>
+<td align="center" width="100%">ZSI: The Zolera Soap Infrastructure
+<BR>
+User's Guide</td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+</tr></table>
+<div class='online-navigation'>
+</div>
+<hr /></div>
+</DIV>
+<!--End of Navigation Panel-->
+
+<P>
+
+<div class="titlepage">
+<div class='center'>
+<h1>ZSI: The Zolera Soap Infrastructure
+<BR>
+User's Guide</h1>
+<p>
+ <span class="email"></span>
+</p>
+<p><strong>Release 2.0.0</strong><br />
+<strong>October 25, 2006</strong></p>
+<p></p>
+</div>
+</div>
+
+<P>
+<DIV CLASS="centerline" ID="par722" ALIGN="CENTER">
+<strong>COPYRIGHT</strong></DIV>
+
+<P>
+Copyright &#169; 2001, Zolera Systems, Inc.
+<BR>
+All Rights Reserved.
+
+<P>
+Copyright &#169; 2002-2003, Rich Salz.
+<BR>
+All Rights Reserved.
+
+<P>
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or
+sell copies of the Software, and to permit persons to whom the Software
+is furnished to do so, provided that the above copyright notice(s) and
+this permission notice appear in all copies of the Software and that
+both the above copyright notice(s) and this permission notice appear in
+supporting documentation.
+
+<P>
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THIS SOFTWARE.
+
+<P>
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+<P>
+<BR>
+<BR>
+<BR>
+<BR>
+
+<P>
+<DIV CLASS="centerline" ID="par723" ALIGN="CENTER">
+<strong>Acknowledgments</strong></DIV>
+
+<P>
+We are grateful to the members of the <code>soapbuilders</code>
+mailing list (see <a class="url" href="http://groups.yahoo.com/soapbuilders">http://groups.yahoo.com/soapbuilders</a>),
+Fredrik Lundh for his <code>soaplib</code> package (see
+<a class="url" href="http://www.secretlabs.com/downloads/index.htm#soap">http://www.secretlabs.com/downloads/index.htm#soap</a>),
+Cayce Ullman and Brian Matthews for their <code>SOAP.py</code> package
+(see <a class="url" href="http://sourceforge.net/projects/pywebsvcs">http://sourceforge.net/projects/pywebsvcs</a>).
+
+<P>
+We are particularly grateful to Brian Lloyd and the Zope Corporation
+(<a class="url" href="http://www.zope.com">http://www.zope.com</a>) for letting us incorporate his ZOPE
+WebServices package and documentation into <tt class="module">ZSI</tt>.
+
+<H3>Abstract:</H3>
+<DIV CLASS="ABSTRACT">
+
+<tt class="module">ZSI</tt>, the Zolera SOAP Infrastructure, is a Python package that
+provides an implementation of SOAP messaging, as described in
+<em class="citetitle"><a
+ href="http://www.w3.org/TR/soap"
+ title="The SOAP 1.1 Specification"
+ >The SOAP 1.1 Specification</a></em>.
+In particular, <tt class="module">ZSI</tt> parses and generates SOAP messages, and
+converts between native Python datatypes and SOAP syntax.
+It can also be used to build applications using
+<em class="citetitle"><a
+ href="http://www.w3.org/TR/SOAP-attachments"
+ title="SOAP Messages with
+Attachments"
+ >SOAP Messages with
+Attachments</a></em>.
+<tt class="module">ZSI</tt> is ``transport neutral'', and provides only a simple
+I/O and dispatch framework; a more complete solution is the
+responsibility of the application using <tt class="module">ZSI</tt>.
+As usage patterns emerge, and common application frameworks are
+more understood, this may change.
+
+<P>
+<tt class="module">ZSI</tt> requires Python 2.0 or later and PyXML version 0.6.6 or later.
+
+<P>
+The <tt class="module">ZSI</tt> homepage is at <a class="url" href="http://pywebsvcs.sf.net/">http://pywebsvcs.sf.net/</a>.
+
+<P>
+</DIV>
+<P>
+
+<P>
+<BR><h2><A NAME="SECTION001000000000000000000">
+Contents</A>
+</h2>
+<!--Table of Contents-->
+
+<UL CLASS="TofC">
+<LI><A href="guide.html#SECTION002000000000000000000">1. Introduction</a>
+<UL>
+<LI><A href="guide.html#SECTION002100000000000000000">1.1 How to Read this Document</a>
+</ul>
+<LI><A href="guide.html#SECTION003000000000000000000">2. WSDL/XMLSchema python code generation</a>
+<UL>
+<LI><A href="guide.html#SECTION003100000000000000000">2.1 wsdl2py</a>
+<UL>
+<LI><A href="guide.html#SECTION003110000000000000000">2.1.1 Command Line Flags</a>
+<LI><A href="guide.html#SECTION003120000000000000000">2.1.2 Basics of Code Generation</a>
+<LI><A href="guide.html#SECTION003130000000000000000">2.1.3 Typecode Extensions</a>
+</ul>
+<LI><A href="guide.html#SECTION003200000000000000000">2.2 Code Generation from WSDL and XML Schema</a>
+<UL>
+<LI><A href="guide.html#SECTION003210000000000000000">2.2.1 Example Use of Generated Code</a>
+</ul>
+<LI><A href="guide.html#SECTION003300000000000000000">2.3 Advanced Usage Patterns</a>
+</ul></ul>
+<!--End of Table of Contents-->
+
+<H1><A NAME="SECTION002000000000000000000">
+1. Introduction</A>
+</H1>
+
+<P>
+<tt class="module">ZSI</tt>, the Zolera SOAP Infrastructure, is a Python package that
+provides an implementation of the SOAP specification, as described in
+<em class="citetitle"><a
+ href="http://www.w3.org/TR/soap"
+ title="The SOAP 1.1 Specification"
+ >The SOAP 1.1 Specification</a></em>.
+This guide demonstrates <tt class="module">ZSI</tt>'s typecode generation facilities, these
+typecodes are then used to parse and generates SOAP messages, and
+converts between native Python datatypes and SOAP syntax.
+
+<P>
+<tt class="module">ZSI</tt> requires Python 2.3 or later and PyXML version 0.8.3 or later.
+
+<P>
+The <tt class="module">ZSI</tt> project is maintained at SourceForge, at
+<a class="url" href="http://pywebsvcs.sf.net">http://pywebsvcs.sf.net</a>.
+<tt class="module">ZSI</tt> is discussed on the Python web services mailing list, visit
+<a class="url" href="http://lists.sourceforge.net/lists/listinfo/pywebsvcs-talk">http://lists.sourceforge.net/lists/listinfo/pywebsvcs-talk</a>to subscribe.
+
+<P>
+For a low-level treatment of typecodes, and a description of SOAP-based
+processing see the ZSI manual.
+
+<P>
+Within this document, <code>tns</code> is used as the prefix for the
+application's target namespace, and the term
+<em>element</em> refers to a DOM element node.)
+
+<P>
+
+<H1><A NAME="SECTION002100000000000000000">
+1.1 How to Read this Document</A>
+</H1>
+
+<P>
+
+<H1><A NAME="SECTION003000000000000000000">
+2. WSDL/XMLSchema python code generation</A>
+</H1>
+
+<P>
+Handling XML Schema (see <em class="citetitle"><a
+ href="http://www.w3.org/XML/Schema"
+ title="XML Schema specification"
+ >XML Schema specification</a></em>)
+is one of the more difficult aspects of using WSDL (see
+<em class="citetitle"><a
+ href="http://www.w3.org/TR/wsdl"
+ title="The Web Services Description Language"
+ >The Web Services Description Language</a></em>.
+Using wsdl2py generates a module with stub code for the client interface,
+and a "types" module that contains typecode representations of the XML Schema types and elements. The generated typecodes are registered in a global schema instance, and once the "types" module is imported by an application all the global elements declarations and type definitions are available everywhere (see section ??).
+
+<P>
+
+<H1><A NAME="SECTION003100000000000000000">
+2.1 wsdl2py</A>
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION003110000000000000000">
+2.1.1 Command Line Flags</A>
+</H2>
+
+<P>
+
+<H3><A NAME="SECTION003111000000000000000">
+2.1.1.1 General Flags</A>
+</H3>
+<DL>
+<DT><STRONG>-h, --help</STRONG></DT>
+<DD>Display the help message and available command line
+flags that can be passed to wsdl2py.
+</DD>
+<DT><STRONG>-f FILE, --file=FILE</STRONG></DT>
+<DD>Create bindings for the WSDL which is located at
+the local file path.
+</DD>
+<DT><STRONG>-u URL, --url=URL</STRONG></DT>
+<DD>Create bindings for the remote WSDL which is located
+at the provided URL.
+</DD>
+<DT><STRONG>-x, --schema</STRONG></DT>
+<DD>Just process a schema (xsd) file and generate the types
+mapping file.
+</DD>
+<DT><STRONG>-d, --debug</STRONG></DT>
+<DD>Output verbose debugging messages during code generation.
+</DD>
+<DT><STRONG>-o OUTPUT_DIR, --output-dir=OUTPUT_DIR</STRONG></DT>
+<DD>Write generated files to OUTPUT_DIR.
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION003112000000000000000">
+2.1.1.2 Typecode Extensions (Stable) </A>
+</H3>
+<DL>
+<DT><STRONG>-b, --complexType (more in subsection&nbsp;<A NAME="subsubsection:complexType"></A>)</STRONG></DT>
+<DD>Generate convenience functions for complexTypes. This includes getters,
+setters, factory methods, and properties. ** Do NOT use with -simple-naming **
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION003113000000000000000">
+2.1.1.3 Development Extensions (Unstable) </A>
+</H3>
+<DL>
+<DT><STRONG>-a, --address</STRONG></DT>
+<DD>WS-Addressing support. The WS-Addressing schema must be
+included in the corresponding WSDL.
+</DD>
+<DT><STRONG>-w, --twisted</STRONG></DT>
+<DD>Generate a twisted.web client. Dependencies:
+python<code>&gt;=</code>2.4, Twisted<code>&gt;=</code>2.0.0, TwistedWeb<code>&gt;=</code>0.5.0
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION003114000000000000000">
+2.1.1.4 Customizations (Unstable) </A>
+</H3>
+<DL>
+<DT><STRONG>-e, --extended</STRONG></DT>
+<DD>Do extended code generation.
+</DD>
+<DT><STRONG>-z ANAME, --aname=ANAME</STRONG></DT>
+<DD>Use a custom function, ANAME, for attribute name
+creation.
+</DD>
+<DT><STRONG>-t TYPES, --types=TYPES</STRONG></DT>
+<DD>Dump the generated type mappings to a file
+named, ``TYPES.py''.
+</DD>
+<DT><STRONG>-s, --simple-naming</STRONG></DT>
+<DD>Simplify the generated naming.
+</DD>
+<DT><STRONG>-c CLIENTCLASSSUFFIX, --clientClassSuffix=CLIENTCLASSSUFFIX</STRONG></DT>
+<DD>The suffic
+to use for service client class. (default ``SOAP'')
+</DD>
+<DT><STRONG>-m PYCLASSMAPMODULE, --pyclassMapModule=PYCLASSMAPMODULE</STRONG></DT>
+<DD>Use the
+existing existing type mapping file to determine the ``pyclass'' objects to be
+used. The module should contain an attribute, ``mapping'', which is a
+dictionary of form, schemaTypeName: (moduleName.py, className).
+</DD>
+</DL>
+
+<P>
+
+<H2><A NAME="SECTION003120000000000000000"></A>
+<A NAME="subsection:Basics_of_Code_Generation"></A>
+<BR>
+2.1.2 Basics of Code Generation
+</H2>
+
+<P>
+
+<H3><A NAME="SECTION003121000000000000000">
+2.1.2.1 client stub module</A>
+</H3>
+Using only the <I>General Flags</I> options one can generate a
+<B>client stub module</B> from a WSDL description, consisting of
+representations of the WSDL information items <I>service</I>, <I>binding</I>,
+<I>portType</I>, and <I>message</I>.
+
+<P>
+These four items are represented by three abstractions, consisting of a
+<I><B>Locator</B></I> class, <I><B>PortType</B></I> class, and several
+<I><B>Message</B></I> classes. The <I><B>Locator</B></I> will have two methods for each <I>service port</I> declared in
+the WSDL definition. One method returns the address specified in the <I>binding</I>, and the other is a factory method for returning a <I><B>PortType</B></I>
+instance. Each <I><B>Message</B></I> class represents the aspects of the <I>binding</I>
+at the operation level and below, and any type information specified by <I>message part</I> items.
+
+<P>
+
+<H3><A NAME="SECTION003122000000000000000">
+2.1.2.2 types module</A>
+</H3>
+The <B>types module</B> is generated with the <B>client module</B> but
+it can be created independently. This is especially useful for dealing with
+schema definitions that aren't specified inside of a WSDL document.
+
+<P>
+The module level class defintions each represent a unique namespace, they are
+simply wrappers for the contents of individual namespaces. The inner classes
+are the typecode representations of global <I>type definitions</I> (suffix <I><B>_Def</B></I>),
+and <I>element declarations</I> (suffix <I><B>_Dec</B></I>).
+
+<P>
+
+<H3><A NAME="SECTION003123000000000000000">
+2.1.2.3 understanding the generated typecodes</A>
+</H3>
+The generated inner typecode classes come in two flavors, as mentioned above.
+<I>element declarations</I> can be serialized into XML, generally <I>type
+definitions</I> cannot. In very simple terms, the <I>name</I> attribute of an <I>element declaration</I> is serialized into an XML tag, but <I>type
+definitions</I> lack this information so they cannot be directly serialized into an
+XML instance. Most <I>element declaration</I>s declare a <I>type</I> attribute,
+this must reference a <I>type definition</I>. Considering the above scenario, a
+generated <I>TypeCode</I> class representing an <I>element declaration</I> will
+subclass the generated <I>TypeCode</I> class representing the <I>type
+definition</I>.
+
+<P>
+
+<H4><A NAME="SECTION003123100000000000000">
+2.1.2.3.1 pyclass</A>
+</H4>
+All instances of generated <I>TypeCode</I> classes will have a <I>pyclass</I>
+attribute, instances of the <I>pyclass</I> can be created to store the data
+representing an <I>element declaration</I>. The <I>pyclass</I> itself has a <I>typecode</I> attribute, which is a reference to the <I>TypeCode</I> instance describing the data, thus making <I>pyclass</I> instances
+self-describing.
+When parsing an XML instance the data will be marshalled into a <I>pyclass</I>
+instance.
+
+<P>
+
+<H4><A NAME="SECTION003123200000000000000">
+2.1.2.3.2 aname</A>
+</H4>
+The <I>aname</I> is a <I>TypeCode</I> instance attribute, its value is a string representing the
+attribute name used to reference data representing an element declaration. The set
+of <I>XMLSchema</I> element names is <I>NCName</I>, this is a superset of ordinary
+ identifiers in <I>python</I>.
+
+<P>
+<em class="citetitle"><a
+ href="http://www.w3.org/TR/REC-xml-names/"
+ title="Namespaces in XML"
+ >Namespaces in XML</a></em>
+
+<P>
+<div class="verbatim"><pre>
+From Namespaces in XML
+ NCName ::= (Letter | '_') (NCNameChar)*
+ NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
+
+From Python Reference Manual (2.3 Identifiers and keywords)
+ identifier ::= (letter|"_") (letter | digit | "_")*
+
+Default set of anames
+ ANAME ::= ("_") (letter | digit | "_")*
+</pre></div>
+
+<P>
+
+<H4><A NAME="SECTION003123300000000000000">
+2.1.2.3.3 transform</A>
+</H4> <I>NCName</I> into an <I>ANAME</I>
+
+<OL>
+<LI>preprend "_"
+</LI>
+<LI>character not in set (letter <code>|</code> digit <code>|</code> "_") change to "_"
+</LI>
+</OL>
+
+<P>
+
+<H4><A NAME="SECTION003123400000000000000">
+2.1.2.3.4 Attribute Declarations: attrs_aname</A>
+</H4>
+The <I>attrs_aname</I> is a <I>TypeCode</I> instance attribute, its value is a string representing the
+attribute name used to reference a dictionary, containing data representing
+attribute declarations. The keys of this dictionary are the
+<code>(namespace,name)</code> tuples, the value of each key represents the value of
+the attribute.
+
+<P>
+
+<H4><A NAME="SECTION003123500000000000000">
+2.1.2.3.5 Mixed Text Content: mixed_aname</A>
+</H4>
+
+<P>
+
+<H2><A NAME="SECTION003130000000000000000">
+2.1.3 Typecode Extensions</A>
+</H2>
+
+<P>
+
+<H3><A NAME="SECTION003131000000000000000"></A>
+<A NAME="subsubsection:complexType"></A>
+<BR>
+2.1.3.1 -complexType
+</H3>
+The <I>complexType</I> flag provides many conveniences to the programmer. This
+option is tested and reliable, and highly recommended by the authors.
+
+<P>
+
+<H4><A NAME="SECTION003131100000000000000">
+2.1.3.1.1 low-level description</A>
+</H4>
+When enabled the <code>__metaclass__</code> attribute will be set on all generated
+<I>pyclass</I>es. The metaclass will introspect the <I>typecode</I> attribute of
+<I>pyclass</I>, and create a set of helper methods for each element
+and attribute declared in the <I>complexType</I> definition. This option simply
+adds wrappers for dealing with content, it doesn't modify the generation scheme.
+
+<P>
+<DL>
+<DT><STRONG>Getters/Setters</STRONG></DT>
+<DD>A getter and setter function is defined for each element
+of a complex type. The functions are named <code>get_element_ANAME</code> and
+<code>set_element_ANAME</code> respectively. In this example, variable <var>wsreq</var>
+has functions named <code>get_element__Options</code> and <code>set_element__Options</code>.
+ In addition to elements, getters and setters are generated for the attributes
+ of a complex type. For attributes, just the name of the attribute is used in
+ determining the method names, so get_attribute_NAME and set_attribute_NAME are
+ created.
+
+<P>
+</DD>
+<DT><STRONG>Factory Methods</STRONG></DT>
+<DD>If an element of a complex type is a complex type itself,
+then a conveniece factory method is created to get an instance of that types
+holder class. The factory method is named, <code>newANAME</code>, so <var>wsreq</var> has
+a factory method, <code>new_Options</code>.
+
+<P>
+</DD>
+<DT><STRONG>Properties</STRONG></DT>
+<DD><em class="citetitle"><a
+ href="http://www.python.org/download/releases/2.2/descrintro/#property"
+ title="Python class properties"
+ >Python class properties</a></em>
+are created for each element of the complex type. They are mapped to the
+corresponding getter and setter for that element. To avoid name collisions the
+properties are named, <code>PNAME</code>, where the first letter of the type's pname
+attribute is capitalized. In our running example, <var>wsreq</var> has class
+property, <code>Options</code>, which calls functions <code>get_element__Options</code> and
+<code>set_element__Options</code> under the hood.
+
+<P>
+</DD>
+</DL>
+
+<P>
+<div class="verbatim"><pre>
+&lt;xsd:complexType name='WolframSearchOptions'&gt;
+ &lt;xsd:sequence&gt;
+ &lt;xsd:element name='Query' minOccurs='0' maxOccurs='1' type='xsd:string'/&gt;
+ &lt;xsd:element name='Limit' minOccurs='0' maxOccurs='1' type='xsd:int'/&gt;
+ &lt;/xsd:sequence&gt;
+ &lt;xsd:attribute name='timeout' type='xsd:double' /&gt;
+&lt;/xsd:complexType&gt;
+&lt;xsd:element name='WolframSearch'&gt;
+ &lt;xsd:complexType&gt;
+ &lt;xsd:sequence&gt;
+ &lt;xsd:element name='Options' minOccurs='0' maxOccurs='1' type='ns1:WolframSearchOptions'/&gt;
+ &lt;/xsd:sequence&gt;
+ &lt;/xsd:complexType&gt;
+&lt;/xsd:element&gt;
+</pre></div>
+
+<P>
+<div class="verbatim"><pre>
+# Create a request object to operation WolframSearch
+# to be used as an example below
+from WolframSearchService_services import *
+
+port = WolframSearchServiceLocator().getWolframSearchmyPortType()
+wsreq = WolframSearchRequest()
+</pre></div>
+
+<P>
+<div class="verbatim"><pre>
+# sample usage of the generated code
+
+# get an instance of a Options holder class using factory method
+opts = wsreq.new_Options()
+wsreq.Options = opts
+
+# assign values using the properties or methods
+opts.Query = 'Newton'
+opts.set_element__Limit(10)
+
+# don't forget the attribute
+opts.set_attribute_timeout(1.0)
+
+# At this point the serialized wsreq object would resemble this:
+# &lt;WolframSearch&gt;
+# &lt;Options timeout="1.0" xsi:type="tns:WolframSearchOptions"&gt;
+# &lt;Query xsi:type="xsd:string"&gt;Newton&lt;/Query&gt;
+# &lt;Limit xsi:type="xsd:double"&gt;10.0&lt;/Limit&gt;
+# &lt;/Options&gt;
+# &lt;/WolframSearch&gt;
+
+# ready call the remote operation
+wsresp = port.WolframSearch(wsreq)
+
+# returned WolframSearchResponse type holder also has conveniences
+print 'SearchTime:', wsresp.Result.SearchTime
+</pre></div>
+
+<P>
+
+<H1><A NAME="SECTION003200000000000000000">
+2.2 Code Generation from WSDL and XML Schema</A>
+</H1>
+
+<P>
+This section covers wsdl2py, the second way ZSI provides to access WSDL
+services. Given the path to a WSDL service, two files are generated, a
+'service' file and a 'types' file, that one can then use to access the
+service. As an example, we will use the search service provided by Wolfram
+Research Inc.&#169;, <a class="url" href="http://webservices.wolfram.com/wolframsearch/">http://webservices.wolfram.com/wolframsearch/</a>,
+which provides a service for searching the popular MathWorld site,
+<a class="url" href="http://mathworld.wolfram.com/">http://mathworld.wolfram.com/</a>, among others.
+
+<P>
+<div class="verbatim"><pre>
+wsdl2py --complexType --url=http://webservices.wolfram.com/services/SearchServices/WolframSearch2.wsdl
+</pre></div>
+
+<P>
+Run the above command to generate the service and type files. wsdl2py uses
+the <I>name</I> attribute of the <I>wsdl:service</I> element to name the resulting files.
+In this example, the service name is <I>WolframSearchService</I>. Therefore the files
+<I>WolframSearchService_services.py</I> and <I>WolframSearchService_services_types.py</I>
+should be generated.
+
+<P>
+The 'service' file contains locator, portType, and message classes.
+A locator instance is used to get an instance of a portType class,
+which is a remote proxy object. Message instances are sent and received
+through the methods of the portType instance.
+
+<P>
+The 'types' file contains class representations of the definitions and
+declarations defined by all schema instances imported by the WSDL definition.
+XML Schema attributes, wildcards, and derived types are not fully
+handled.
+
+<P>
+
+<H2><A NAME="SECTION003210000000000000000">
+2.2.1 Example Use of Generated Code</A>
+</H2>
+
+<P>
+The following shows how to call a proxy method for <I>WolframSearch</I>. It
+assumes wsdl2py has already been run as shown in the section above. The example
+will be explained in greater detail below.
+
+<P>
+<div class="verbatim"><pre>
+# import the generated class stubs
+from WolframSearchService_services import *
+
+# get a port proxy instance
+loc = WolframSearchServiceLocator()
+port = loc.getWolframSearchmyPortType()
+
+# create a new request
+req = WolframSearchRequest()
+req.Options = req.new_Options()
+req.Options.Query = 'newton'
+
+# call the remote method
+resp = port.WolframSearch(req)
+
+# print results
+print 'Search Time:', resp.Result.SearchTime
+print 'Total Matches:', resp.Result.TotalMatches
+for hit in resp.Result.Matches.Item:
+ print '--', hit.Title
+</pre></div>
+
+<P>
+Now each section of the code above will be explained.
+
+<P>
+<div class="verbatim"><pre>
+from WolframSearchService_services import *
+</pre></div>
+
+<P>
+We are primarily interested in the service locator that is imported. The
+binding proxy and classes for all the messages are additionally imported.
+Look at the <I>WolframSearchService_services.py</I> file for more information.
+
+<P>
+<div class="verbatim"><pre>
+loc = WolframSearchServiceLocator()
+port = loc.getWolframSearchmyPortType()
+</pre></div>
+
+<P>
+Using an instance of the locator, we fetch an instance of the port proxy
+which is used for invoking the remote methods provided by the service. In
+this case the default <I>location</I> specified in the <I>wsdlsoap:address</I>
+element is used. You can optionally pass a url to the port getter method to
+specify an alternate location to be used. The <I>portType</I> - <I>name</I>
+attribute is used to determine the method name to fetch a port proxy instance.
+In this example, the port name is <I>WolframSearchmyPortType</I>, hence the
+method of the locator for fetching the proxy is <I>getWolframSearchmyPortType</I>.
+
+<P>
+The first step in calling <I>WolframSearch</I> is to create a request object
+corresponding to the input message of the method. In this case, the name of
+the message is <I>WolframSearchRequest</I>. A class representing this message
+was imported from the service module.
+
+<P>
+<div class="verbatim"><pre>
+req = WolframSearchRequest()
+req.Options = req.new_Options()
+req.Options.Query = 'newton'
+</pre></div>
+
+<P>
+Once a request object is created we need to populate the instance with the
+information we want to use in our request. This is where the <TT>-complexType</TT>
+option we passed to wsdl2py will come in handy. This caused the creation of
+functions for getting and setting elements and attributes of the type, class
+properties for each element, and convenience functions for creating new instances
+of elements of complex types. This functionality is explained in detail in
+subsection&nbsp;<A HREF="#subsubsection:complexType">2.1.3</A>.
+
+<P>
+Once the request instance is populated, calling the remote service is easy. Using
+the port proxy we call the method we are interested in. An instance of the python
+class representing the return type is returned by this call. The <var>resp</var> object
+can be used to introspect the result of the remote call.
+
+<P>
+<div class="verbatim"><pre>
+resp = port.WolframSearch(req)
+</pre></div>
+
+<P>
+Here we see that the response message, <var>resp</var>, represents type <I>WolframSearchReturn</I>.
+This object has one element, <I>Result</I> which contains the search results for our
+search of the keyword, <TT>newton</TT>.
+
+<P>
+<div class="verbatim"><pre>
+print 'Search Time:', resp.Result.SearchTime
+...
+</pre></div>
+
+<P>
+Refer to the wsdl for <I>WolframSearchService</I> for more details on the returned information.
+
+<P>
+
+<H1><A NAME="SECTION003300000000000000000">
+2.3 Advanced Usage Patterns</A>
+</H1>
+Not done.
+
+<P>
+
+<H1><A NAME="SECTION004000000000000000000">
+About this document ...</A>
+</H1>
+ <strong>ZSI: The Zolera Soap Infrastructure
+<BR>
+User's Guide</strong>,
+October 25, 2006, Release 2.0.0
+<p> This document was generated using the <a
+ href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/">
+ <strong>LaTeX</strong>2<tt>HTML</tt></a> translator.
+</p>
+
+<p> <a
+ href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/">
+ <strong>LaTeX</strong>2<tt>HTML</tt></a> is Copyright &copy;
+ 1993, 1994, 1995, 1996, 1997, <a
+ href="http://cbl.leeds.ac.uk/nikos/personal.html">Nikos
+ Drakos</a>, Computer Based Learning Unit, University of
+ Leeds, and Copyright &copy; 1997, 1998, <a
+ href="http://www.maths.mq.edu.au/~ross/">Ross
+ Moore</a>, Mathematics Department, Macquarie University,
+ Sydney.
+</p>
+
+<p> The application of <a
+ href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/">
+ <strong>LaTeX</strong>2<tt>HTML</tt></a> to the Python
+ documentation has been heavily tailored by Fred L. Drake,
+ Jr. Original navigation icons were contributed by Christopher
+ Petrilli.
+</p>
+
+<DIV CLASS="navigation">
+<div class='online-navigation'>
+<p></p><hr />
+<table align="center" width="100%" cellpadding="0" cellspacing="2">
+<tr>
+<td class='online-navigation'><img src='previous.png'
+ border='0' height='32' alt='Previous Page' width='32' /></td>
+<td class='online-navigation'><img src='up.png'
+ border='0' height='32' alt='Up One Level' width='32' /></td>
+<td class='online-navigation'><img src='next.png'
+ border='0' height='32' alt='Next Page' width='32' /></td>
+<td align="center" width="100%">ZSI: The Zolera Soap Infrastructure
+<BR>
+User's Guide</td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+</tr></table>
+<div class='online-navigation'>
+</div>
+</div>
+<hr />
+<span class="release-info">Release 2.0.0, documentation updated on October 25, 2006.</span>
+</DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/guide.pdf b/doc/guide.pdf
new file mode 100644
index 0000000..6334390
--- /dev/null
+++ b/doc/guide.pdf
Binary files differ
diff --git a/doc/guide.ps b/doc/guide.ps
new file mode 100644
index 0000000..5b70865
--- /dev/null
+++ b/doc/guide.ps
@@ -0,0 +1,874 @@
+%!PS-Adobe-2.0
+%%Creator: dvips(k) 5.95a Copyright 2005 Radical Eye Software
+%%Title: guide.dvi
+%%Pages: 15
+%%PageOrder: Ascend
+%%BoundingBox: 0 0 612 792
+%%DocumentFonts: Helvetica Helvetica-Oblique Times-Roman Times-Bold
+%%+ CMSY10 Courier Times-Italic Helvetica-Bold Times-BoldItalic
+%%DocumentPaperSizes: Letter
+%%EndComments
+%DVIPSWebPage: (www.radicaleye.com)
+%DVIPSCommandLine: dvips -N0 -t letter -o guide.ps guide.dvi
+%DVIPSParameters: dpi=600
+%DVIPSSource: TeX output 2006.10.25:2110
+%%BeginProcSet: tex.pro 0 0
+%!
+/TeXDict 300 dict def TeXDict begin/N{def}def/B{bind def}N/S{exch}N/X{S
+N}B/A{dup}B/TR{translate}N/isls false N/vsize 11 72 mul N/hsize 8.5 72
+mul N/landplus90{false}def/@rigin{isls{[0 landplus90{1 -1}{-1 1}ifelse 0
+0 0]concat}if 72 Resolution div 72 VResolution div neg scale isls{
+landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div hsize
+mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul TR[
+matrix currentmatrix{A A round sub abs 0.00001 lt{round}if}forall round
+exch round exch]setmatrix}N/@landscape{/isls true N}B/@manualfeed{
+statusdict/manualfeed true put}B/@copies{/#copies X}B/FMat[1 0 0 -1 0 0]
+N/FBB[0 0 0 0]N/nn 0 N/IEn 0 N/ctr 0 N/df-tail{/nn 8 dict N nn begin
+/FontType 3 N/FontMatrix fntrx N/FontBBox FBB N string/base X array
+/BitMaps X/BuildChar{CharBuilder}N/Encoding IEn N end A{/foo setfont}2
+array copy cvx N load 0 nn put/ctr 0 N[}B/sf 0 N/df{/sf 1 N/fntrx FMat N
+df-tail}B/dfs{div/sf X/fntrx[sf 0 0 sf neg 0 0]N df-tail}B/E{pop nn A
+definefont setfont}B/Cw{Cd A length 5 sub get}B/Ch{Cd A length 4 sub get
+}B/Cx{128 Cd A length 3 sub get sub}B/Cy{Cd A length 2 sub get 127 sub}
+B/Cdx{Cd A length 1 sub get}B/Ci{Cd A type/stringtype ne{ctr get/ctr ctr
+1 add N}if}B/CharBuilder{save 3 1 roll S A/base get 2 index get S
+/BitMaps get S get/Cd X pop/ctr 0 N Cdx 0 Cx Cy Ch sub Cx Cw add Cy
+setcachedevice Cw Ch true[1 0 0 -1 -.1 Cx sub Cy .1 sub]{Ci}imagemask
+restore}B/D{/cc X A type/stringtype ne{]}if nn/base get cc ctr put nn
+/BitMaps get S ctr S sf 1 ne{A A length 1 sub A 2 index S get sf div put
+}if put/ctr ctr 1 add N}B/I{cc 1 add D}B/bop{userdict/bop-hook known{
+bop-hook}if/SI save N @rigin 0 0 moveto/V matrix currentmatrix A 1 get A
+mul exch 0 get A mul add .99 lt{/QV}{/RV}ifelse load def pop pop}N/eop{
+SI restore userdict/eop-hook known{eop-hook}if showpage}N/@start{
+userdict/start-hook known{start-hook}if pop/VResolution X/Resolution X
+1000 div/DVImag X/IEn 256 array N 2 string 0 1 255{IEn S A 360 add 36 4
+index cvrs cvn put}for pop 65781.76 div/vsize X 65781.76 div/hsize X}N
+/p{show}N/RMat[1 0 0 -1 0 0]N/BDot 260 string N/Rx 0 N/Ry 0 N/V{}B/RV/v{
+/Ry X/Rx X V}B statusdict begin/product where{pop false[(Display)(NeXT)
+(LaserWriter 16/600)]{A length product length le{A length product exch 0
+exch getinterval eq{pop true exit}if}{pop}ifelse}forall}{false}ifelse
+end{{gsave TR -.1 .1 TR 1 1 scale Rx Ry false RMat{BDot}imagemask
+grestore}}{{gsave TR -.1 .1 TR Rx Ry scale 1 1 false RMat{BDot}
+imagemask grestore}}ifelse B/QV{gsave newpath transform round exch round
+exch itransform moveto Rx 0 rlineto 0 Ry neg rlineto Rx neg 0 rlineto
+fill grestore}B/a{moveto}B/delta 0 N/tail{A/delta X 0 rmoveto}B/M{S p
+delta add tail}B/b{S p tail}B/c{-4 M}B/d{-3 M}B/e{-2 M}B/f{-1 M}B/g{0 M}
+B/h{1 M}B/i{2 M}B/j{3 M}B/k{4 M}B/w{0 rmoveto}B/l{p -4 w}B/m{p -3 w}B/n{
+p -2 w}B/o{p -1 w}B/q{p 1 w}B/r{p 2 w}B/s{p 3 w}B/t{p 4 w}B/x{0 S
+rmoveto}B/y{3 2 roll p a}B/bos{/SS save N}B/eos{SS restore}B end
+
+%%EndProcSet
+%%BeginProcSet: 8r.enc 0 0
+% File 8r.enc TeX Base 1 Encoding Revision 2.0 2002-10-30
+%
+% @@psencodingfile@{
+% author = "S. Rahtz, P. MacKay, Alan Jeffrey, B. Horn, K. Berry,
+% W. Schmidt, P. Lehman",
+% version = "2.0",
+% date = "30 October 2002",
+% filename = "8r.enc",
+% email = "tex-fonts@@tug.org",
+% docstring = "This is the encoding vector for Type1 and TrueType
+% fonts to be used with TeX. This file is part of the
+% PSNFSS bundle, version 9"
+% @}
+%
+% The idea is to have all the characters normally included in Type 1 fonts
+% available for typesetting. This is effectively the characters in Adobe
+% Standard encoding, ISO Latin 1, Windows ANSI including the euro symbol,
+% MacRoman, and some extra characters from Lucida.
+%
+% Character code assignments were made as follows:
+%
+% (1) the Windows ANSI characters are almost all in their Windows ANSI
+% positions, because some Windows users cannot easily reencode the
+% fonts, and it makes no difference on other systems. The only Windows
+% ANSI characters not available are those that make no sense for
+% typesetting -- rubout (127 decimal), nobreakspace (160), softhyphen
+% (173). quotesingle and grave are moved just because it's such an
+% irritation not having them in TeX positions.
+%
+% (2) Remaining characters are assigned arbitrarily to the lower part
+% of the range, avoiding 0, 10 and 13 in case we meet dumb software.
+%
+% (3) Y&Y Lucida Bright includes some extra text characters; in the
+% hopes that other PostScript fonts, perhaps created for public
+% consumption, will include them, they are included starting at 0x12.
+% These are /dotlessj /ff /ffi /ffl.
+%
+% (4) hyphen appears twice for compatibility with both ASCII and Windows.
+%
+% (5) /Euro was assigned to 128, as in Windows ANSI
+%
+% (6) Missing characters from MacRoman encoding incorporated as follows:
+%
+% PostScript MacRoman TeXBase1
+% -------------- -------------- --------------
+% /notequal 173 0x16
+% /infinity 176 0x17
+% /lessequal 178 0x18
+% /greaterequal 179 0x19
+% /partialdiff 182 0x1A
+% /summation 183 0x1B
+% /product 184 0x1C
+% /pi 185 0x1D
+% /integral 186 0x81
+% /Omega 189 0x8D
+% /radical 195 0x8E
+% /approxequal 197 0x8F
+% /Delta 198 0x9D
+% /lozenge 215 0x9E
+%
+/TeXBase1Encoding [
+% 0x00
+ /.notdef /dotaccent /fi /fl
+ /fraction /hungarumlaut /Lslash /lslash
+ /ogonek /ring /.notdef /breve
+ /minus /.notdef /Zcaron /zcaron
+% 0x10
+ /caron /dotlessi /dotlessj /ff
+ /ffi /ffl /notequal /infinity
+ /lessequal /greaterequal /partialdiff /summation
+ /product /pi /grave /quotesingle
+% 0x20
+ /space /exclam /quotedbl /numbersign
+ /dollar /percent /ampersand /quoteright
+ /parenleft /parenright /asterisk /plus
+ /comma /hyphen /period /slash
+% 0x30
+ /zero /one /two /three
+ /four /five /six /seven
+ /eight /nine /colon /semicolon
+ /less /equal /greater /question
+% 0x40
+ /at /A /B /C
+ /D /E /F /G
+ /H /I /J /K
+ /L /M /N /O
+% 0x50
+ /P /Q /R /S
+ /T /U /V /W
+ /X /Y /Z /bracketleft
+ /backslash /bracketright /asciicircum /underscore
+% 0x60
+ /quoteleft /a /b /c
+ /d /e /f /g
+ /h /i /j /k
+ /l /m /n /o
+% 0x70
+ /p /q /r /s
+ /t /u /v /w
+ /x /y /z /braceleft
+ /bar /braceright /asciitilde /.notdef
+% 0x80
+ /Euro /integral /quotesinglbase /florin
+ /quotedblbase /ellipsis /dagger /daggerdbl
+ /circumflex /perthousand /Scaron /guilsinglleft
+ /OE /Omega /radical /approxequal
+% 0x90
+ /.notdef /.notdef /.notdef /quotedblleft
+ /quotedblright /bullet /endash /emdash
+ /tilde /trademark /scaron /guilsinglright
+ /oe /Delta /lozenge /Ydieresis
+% 0xA0
+ /.notdef /exclamdown /cent /sterling
+ /currency /yen /brokenbar /section
+ /dieresis /copyright /ordfeminine /guillemotleft
+ /logicalnot /hyphen /registered /macron
+% 0xD0
+ /degree /plusminus /twosuperior /threesuperior
+ /acute /mu /paragraph /periodcentered
+ /cedilla /onesuperior /ordmasculine /guillemotright
+ /onequarter /onehalf /threequarters /questiondown
+% 0xC0
+ /Agrave /Aacute /Acircumflex /Atilde
+ /Adieresis /Aring /AE /Ccedilla
+ /Egrave /Eacute /Ecircumflex /Edieresis
+ /Igrave /Iacute /Icircumflex /Idieresis
+% 0xD0
+ /Eth /Ntilde /Ograve /Oacute
+ /Ocircumflex /Otilde /Odieresis /multiply
+ /Oslash /Ugrave /Uacute /Ucircumflex
+ /Udieresis /Yacute /Thorn /germandbls
+% 0xE0
+ /agrave /aacute /acircumflex /atilde
+ /adieresis /aring /ae /ccedilla
+ /egrave /eacute /ecircumflex /edieresis
+ /igrave /iacute /icircumflex /idieresis
+% 0xF0
+ /eth /ntilde /ograve /oacute
+ /ocircumflex /otilde /odieresis /divide
+ /oslash /ugrave /uacute /ucircumflex
+ /udieresis /yacute /thorn /ydieresis
+] def
+
+
+%%EndProcSet
+%%BeginProcSet: texps.pro 0 0
+%!
+TeXDict begin/rf{findfont dup length 1 add dict begin{1 index/FID ne 2
+index/UniqueID ne and{def}{pop pop}ifelse}forall[1 index 0 6 -1 roll
+exec 0 exch 5 -1 roll VResolution Resolution div mul neg 0 0]FontType 0
+ne{/Metrics exch def dict begin Encoding{exch dup type/integertype ne{
+pop pop 1 sub dup 0 le{pop}{[}ifelse}{FontMatrix 0 get div Metrics 0 get
+div def}ifelse}forall Metrics/Metrics currentdict end def}{{1 index type
+/nametype eq{exit}if exch pop}loop}ifelse[2 index currentdict end
+definefont 3 -1 roll makefont/setfont cvx]cvx def}def/ObliqueSlant{dup
+sin S cos div neg}B/SlantFont{4 index mul add}def/ExtendFont{3 -1 roll
+mul exch}def/ReEncodeFont{CharStrings rcheck{/Encoding false def dup[
+exch{dup CharStrings exch known not{pop/.notdef/Encoding true def}if}
+forall Encoding{]exch pop}{cleartomark}ifelse}if/Encoding exch def}def
+end
+
+%%EndProcSet
+%%BeginFont: CMSY10
+%!PS-AdobeFont-1.1: CMSY10 1.0
+%%CreationDate: 1991 Aug 15 07:20:57
+% Copyright (C) 1997 American Mathematical Society. All Rights Reserved.
+11 dict begin
+/FontInfo 7 dict dup begin
+/version (1.0) readonly def
+/Notice (Copyright (C) 1997 American Mathematical Society. All Rights Reserved) readonly def
+/FullName (CMSY10) readonly def
+/FamilyName (Computer Modern) readonly def
+/Weight (Medium) readonly def
+/ItalicAngle -14.035 def
+/isFixedPitch false def
+end readonly def
+/FontName /CMSY10 def
+/PaintType 0 def
+/FontType 1 def
+/FontMatrix [0.001 0 0 0.001 0 0] readonly def
+/Encoding 256 array
+0 1 255 {1 index exch /.notdef put} for
+dup 13 /circlecopyrt put
+readonly def
+/FontBBox{-29 -960 1116 775}readonly def
+currentdict end
+currentfile eexec
+D9D66F633B846A97B686A97E45A3D0AA052F09F9C8ADE9D907C058B87E9B6964
+7D53359E51216774A4EAA1E2B58EC3176BD1184A633B951372B4198D4E8C5EF4
+A213ACB58AA0A658908035BF2ED8531779838A960DFE2B27EA49C37156989C85
+E21B3ABF72E39A89232CD9F4237FC80C9E64E8425AA3BEF7DED60B122A52922A
+221A37D9A807DD01161779DDE7D31FF2B87F97C73D63EECDDA4C49501773468A
+27D1663E0B62F461F6E40A5D6676D1D12B51E641C1D4E8E2771864FC104F8CBF
+5B78EC1D88228725F1C453A678F58A7E1B7BD7CA700717D288EB8DA1F57C4F09
+0ABF1D42C5DDD0C384C7E22F8F8047BE1D4C1CC8E33368FB1AC82B4E96146730
+DE3302B2E6B819CB6AE455B1AF3187FFE8071AA57EF8A6616B9CB7941D44EC7A
+71A7BB3DF755178D7D2E4BB69859EFA4BBC30BD6BB1531133FD4D9438FF99F09
+4ECC068A324D75B5F696B8688EEB2F17E5ED34CCD6D047A4E3806D000C199D7C
+515DB70A8D4F6146FE068DC1E5DE8BC5703711DA090312BA3FC00A08C453C609
+C627A8BFEF75B4DEFAF34B44B356A516B765AFCDD3F5475B1F928731D09D2170
+B97E40F12CCEDF4F6BB3756C4734F6E98D74B7E942A954B1BAAB83D4AD727FF6
+DF6DC50B2223BCB5568A73A112E4860AD490554E64E780073FF3399CB4688D33
+9E8829667CD6EAEF25E0C7D2D44F2BBFA40E999325F9561514844221B50BC8FC
+4C7AD68CA7220D69125C2AF06849A3E068D18733276F0C0A6A2936D3C2C87CDE
+59CD1AF148C44F85784A5DAD569F5FF53C061056C067CE29AEF1E3BD1FD8B0B8
+71A0A638CDAC6AEEDBD5337D4683C084BB60B1859E600F59CB4E19C5FC5C6327
+EC544A68134496A9BD0B87D83AF6FDA3CB62FBF0B54FACE1F0E6A2D84B467AFF
+0F62DB
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+cleartomark
+%%EndFont
+TeXDict begin 40258431 52099146 1000 600 600 (guide.dvi)
+@start /Fa 131[45 1[45 45 45 45 45 45 45 45 45 45 45
+45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 1[45 6[45
+45 2[45 45 45 45 45 45 45 45 45 2[45 2[45 45 45 45 1[45
+2[45 45 45 1[45 6[45 45 45 45 45 45 45 45 1[45 45 45
+45 3[45 45 34[{TeXBase1Encoding ReEncodeFont}63 74.7198
+/Courier rf /Fb 134[37 4[23 32 32 1[42 42 7[42 28 37
+1[37 1[42 12[51 3[51 2[74 51 7[60 68[{TeXBase1Encoding ReEncodeFont}16
+83.022 /Times-BoldItalic rf /Fc 134[50 50 3[28 50 33
+1[55 55 55 83 22 2[22 1[55 28 55 55 50 1[55 11[72 61
+7[55 4[78 61 66 1[72 66 14[55 55 55 2[28 46[{
+TeXBase1Encoding ReEncodeFont}29 99.6264 /Helvetica rf
+/Fd 134[60 1[86 60 66 33 60 40 1[66 66 66 100 27 2[27
+66 66 33 66 66 60 1[66 8[80 113 1[86 1[80 86 1[80 2[100
+66 3[86 93 2[86 86 1[80 13[66 66 66 2[33 46[{
+TeXBase1Encoding ReEncodeFont}37 119.552 /Helvetica rf
+/Fe 168[113 2[73 4[93 86 8[80 69[{TeXBase1Encoding ReEncodeFont}5
+119.552 /Helvetica-Bold rf /Ff 105[46 27[42 42 42 60
+42 46 23 42 28 1[46 46 46 69 18 2[18 46 46 23 46 46 42
+46 46 8[55 78 1[60 51 55 60 1[55 2[69 46 3[60 65 51 55
+60 60 1[55 6[23 46 46 46 46 46 46 46 46 46 1[23 23 28
+3[28 28 40[{TeXBase1Encoding ReEncodeFont}55 83.022 /Helvetica
+rf /Fg 134[37 37 55 37 42 23 32 32 42 42 42 42 60 23
+2[23 42 42 23 37 42 37 42 42 8[51 69 2[46 42 51 1[51
+60 55 69 46 4[60 51 51 60 55 1[51 6[28 8[42 2[21 43[42
+2[{TeXBase1Encoding ReEncodeFont}43 83.022 /Times-Italic
+rf /Fh 133[37 37 37 54 37 42 21 37 25 1[42 42 42 62 17
+37 17 17 42 42 21 42 42 37 42 42 38[21 10[21 21 25 9[42
+35[{TeXBase1Encoding ReEncodeFont}30 74.7198 /Helvetica
+rf /Fi 131[50 2[50 50 50 1[50 50 50 50 1[50 50 50 50
+50 2[50 1[50 1[50 50 50 50 50 1[50 4[50 5[50 50 2[50
+50 50 50 3[50 3[50 3[50 2[50 50 14[50 50 50 2[50 50 40[{
+TeXBase1Encoding ReEncodeFont}38 83.022 /Courier rf /Fj
+242[83 13[{}1 83.022 /CMSY10 rf /Fk 105[42 1[37 37 24[37
+42 42 60 42 42 23 32 28 42 42 42 42 65 23 42 23 23 42
+42 28 37 42 37 42 37 6[51 60 60 78 60 60 51 46 55 60
+46 60 60 74 51 60 32 28 60 60 46 51 60 55 55 60 1[37
+3[23 23 42 42 42 42 42 42 42 42 42 42 23 21 28 21 1[42
+28 28 28 35[46 46 2[{TeXBase1Encoding ReEncodeFont}78
+83.022 /Times-Roman rf /Fl 104[83 28[37 42 42 60 1[46
+28 32 37 1[46 42 46 69 23 46 1[23 46 42 28 37 46 37 46
+42 7[60 60 83 1[60 55 46 60 1[51 65 60 78 55 2[32 65
+65 51 55 60 60 1[60 3[47 9[42 42 42 1[23 1[28 21 2[28
+28 37[46 2[{TeXBase1Encoding ReEncodeFont}54 83.022 /Times-Bold
+rf /Fm 139[28 1[33 2[50 9[44 1[44 50 18[72 24[50 50 2[50
+1[50 3[25 44[{TeXBase1Encoding ReEncodeFont}12 99.6264
+/Times-Roman rf /Fn 140[72 6[32 6[80 3[80 14[104 31[80
+1[80 1[40 46[{TeXBase1Encoding ReEncodeFont}8 143.462
+/Helvetica-Oblique rf /Fo 134[103 3[115 57 103 69 1[115
+115 115 172 46 2[46 115 115 57 115 115 103 1[115 6[126
+1[138 195 1[149 126 138 3[161 149 172 115 2[57 1[161
+1[138 149 149 8[57 10[57 7[46 39[{TeXBase1Encoding ReEncodeFont}36
+206.559 /Helvetica rf end
+%%EndProlog
+%%BeginSetup
+%%Feature: *Resolution 600dpi
+TeXDict begin
+%%BeginPaperSize: Letter
+letter
+%%EndPaperSize
+ end
+%%EndSetup
+%%Page: 1 1
+TeXDict begin 1 0 bop 0 83 3901 9 v 652 446 a Fo(ZSI:)58
+b(The)g(Zoler)n(a)e(Soap)h(Infr)n(astr)s(ucture)2717
+695 y(User')-10 b(s)57 b(Guide)3015 925 y Fn(Release)38
+b(2.0.0)3206 4150 y Fm(October)24 b(25,)h(2006)p eop
+end
+%%Page: 2 2
+TeXDict begin 2 1 bop 1694 83 a Fl(COPYRIGHT)0 230 y
+Fk(Cop)o(yright)380 227 y(c)357 230 y Fj(\015)20 b Fk(2001,)f(Zolera)g
+(Systems,)i(Inc.)0 330 y(All)g(Rights)f(Reserv)o(ed.)0
+476 y(Cop)o(yright)380 473 y(c)357 476 y Fj(\015)g Fk(2002-2003,)d
+(Rich)j(Salz.)0 576 y(All)h(Rights)f(Reserv)o(ed.)0 723
+y(Permission)27 b(is)h(hereby)d(granted,)i(free)g(of)g(char)o(ge,)g(to)
+g(an)o(y)f(person)g(obtaining)g(a)h(cop)o(y)f(of)h(this)h(softw)o(are)f
+(and)f(associated)h(docu-)0 823 y(mentation)20 b(\002les)j(\(the)e
+(\224Softw)o(are\224\),)g(to)g(deal)h(in)f(the)h(Softw)o(are)f(without)
+g(restriction,)g(including)f(without)g(limitation)i(the)f(rights)g(to)0
+922 y(use,)k(cop)o(y)-5 b(,)24 b(modify)-5 b(,)23 b(mer)o(ge,)h
+(publish,)g(distrib)n(ute,)h(and/or)d(sell)k(copies)e(of)g(the)g(Softw)
+o(are,)g(and)g(to)h(permit)e(persons)h(to)g(whom)g(the)0
+1022 y(Softw)o(are)i(is)i(furnished)c(to)j(do)f(so,)j(pro)o(vided)24
+b(that)i(the)h(abo)o(v)o(e)e(cop)o(yright)f(notice\(s\))i(and)g(this)h
+(permission)f(notice)g(appear)f(in)i(all)0 1121 y(copies)g(of)g(the)g
+(Softw)o(are)g(and)g(that)g(both)f(the)i(abo)o(v)o(e)d(cop)o(yright)h
+(notice\(s\))g(and)h(this)g(permission)g(notice)f(appear)g(in)i
+(supporting)0 1221 y(documentation.)0 1368 y(THE)e(SOFTW)-10
+b(ARE)26 b(IS)g(PR)m(O)l(VIDED)g(\224AS)g(IS\224,)h(WITHOUT)e(W)-10
+b(ARRANTY)27 b(OF)f(ANY)g(KIND,)g(EXPRESS)g(OR)h(IMPLIED,)0
+1468 y(INCLUDING)f(B)o(UT)h(NO)m(T)g(LIMITED)e(T)o(O)h(THE)g(W)-10
+b(ARRANTIES)27 b(OF)h(MERCHANT)-8 b(ABILITY)d(,)26 b(FITNESS)g(FOR)i(A)
+f(P)-8 b(AR-)0 1567 y(TICULAR)29 b(PURPOSE)g(AND)f(NONINFRINGEMENT)g
+(OF)g(THIRD)h(P)-8 b(AR)j(TY)29 b(RIGHTS.)f(IN)h(NO)f(EVENT)g(SHALL)h
+(THE)0 1667 y(COPYRIGHT)19 b(HOLDER)g(OR)g(HOLDERS)g(INCLUDED)f(IN)h
+(THIS)g(NO)m(TICE)f(BE)h(LIABLE)f(FOR)i(ANY)e(CLAIM,)h(OR)g(ANY)0
+1766 y(SPECIAL)25 b(INDIRECT)g(OR)h(CONSEQ)o(UENTIAL)f(D)m(AMA)m(GES,)f
+(OR)i(ANY)f(D)m(AMA)m(GES)f(WHA)-9 b(TSOEVER)25 b(RESUL)-8
+b(TING)0 1866 y(FR)m(OM)27 b(LOSS)g(OF)g(USE,)f(D)m(A)-9
+b(T)h(A)27 b(OR)g(PR)m(OFITS,)f(WHETHER)h(IN)f(AN)h(A)m(CTION)f(OF)h
+(CONTRA)m(CT)-6 b(,)27 b(NEGLIGENCE)f(OR)0 1966 y(O)m(THER)20
+b(T)o(OR)-5 b(TIOUS)20 b(A)m(CTION,)g(ARISING)h(OUT)f(OF)h(OR)h(IN)e
+(CONNECTION)h(WITH)f(THE)g(USE)h(OR)g(PERFORMANCE)0 2065
+y(OF)g(THIS)f(SOFTW)-10 b(ARE.)0 2212 y(Except)27 b(as)i(contained)d
+(in)i(this)g(notice,)h(the)f(name)f(of)h(a)g(cop)o(yright)e(holder)g
+(shall)j(not)e(be)h(used)g(in)g(adv)o(ertising)e(or)h(otherwise)g(to)0
+2312 y(promote)18 b(the)j(sale,)f(use)h(or)f(other)f(dealings)g(in)i
+(this)g(Softw)o(are)e(without)h(prior)f(written)h(authorization)e(of)h
+(the)i(cop)o(yright)d(holder)-5 b(.)1625 2766 y Fl(Ackno)o(wledgments)0
+2913 y Fk(W)e(e)35 b(are)g(grateful)e(to)h(the)g(members)f(of)h(the)h
+Fi(soapbuilders)d Fk(mailing)i(list)h(\(see)g Fh(http://g)o(roups)o(.y)
+o(ahoo)m(.co)o(m/soapb)n(u)o(il)o(de)o(rs)-5 b Fk(\),)0
+3012 y(Fredrik)20 b(Lundh)g(for)g(his)i Fi(soaplib)f
+Fk(package)f(\(see)h Fh(http://www)l(.secretlabs)o(.com/do)o(wnl)o(oa)o
+(ds/in)o(de)m(x.ht)o(m#soap)-7 b Fk(\),)22 b(Cayce)f(Ullman)0
+3112 y(and)f(Brian)g(Matthe)n(ws)g(for)g(their)f Fi(SOAP.py)h
+Fk(package)f(\(see)h Fh(http://sourcef)n(orge)o(.net/p)o(roj)o(ects/p)m
+(yw)o(ebsvcs)-5 b Fk(\).)0 3259 y(W)e(e)21 b(are)f(particularly)e
+(grateful)h(to)h(Brian)g(Llo)o(yd)e(and)i(the)g(Zope)f(Corporation)e
+(\()p Fh(http://www)l(.z)o(ope)o(.com)l Fk(\))j(for)f(letting)h(us)g
+(incorporate)0 3358 y(his)h(ZOPE)f(W)-7 b(ebServices)20
+b(package)f(and)g(documentation)f(into)i Fi(ZSI)p Fk(.)p
+eop end
+%%Page: 1 3
+TeXDict begin 1 2 bop 1796 2336 a Fl(Abstract)0 2566
+y Fi(ZSI)p Fk(,)24 b(the)h(Zolera)e(SO)m(AP)j(Infrastructure,)c(is)k(a)
+f(Python)e(package)g(that)i(pro)o(vides)d(an)j(implementation)d(of)i
+(SO)m(AP)h(messaging,)g(as)0 2666 y(described)d(in)i
+Fg(The)g(SO)-5 b(AP)23 b(1.1)g(Speci\002cation)p Fk(.)33
+b(In)23 b(particular)m(,)f Fi(ZSI)i Fk(parses)g(and)f(generates)f(SO)m
+(AP)j(messages,)f(and)f(con)m(v)o(erts)f(be-)0 2766 y(tween)29
+b(nati)n(v)o(e)f(Python)f(datatypes)h(and)h(SO)m(AP)g(syntax.)50
+b(It)30 b(can)e(also)h(be)g(used)g(to)g(b)n(uild)f(applications)g
+(using)g Fg(SO)-5 b(AP)29 b(Messa)o(g)o(es)0 2865 y(with)24
+b(Attac)o(hments)p Fk(.)33 b Fi(ZSI)24 b Fk(is)g(\223transport)e
+(neutral\224,)h(and)g(pro)o(vides)f(only)g(a)i(simple)g(I/O)f(and)g
+(dispatch)g(frame)n(w)o(ork;)g(a)h(more)e(com-)0 2965
+y(plete)h(solution)g(is)h(the)g(responsibility)e(of)h(the)h
+(application)e(using)h Fi(ZSI)p Fk(.)g(As)h(usage)f(patterns)g(emer)o
+(ge,)g(and)f(common)g(application)0 3065 y(frame)n(w)o(orks)c(are)i
+(more)g(understood,)d(this)k(may)f(change.)0 3211 y Fi(ZSI)g
+Fk(requires)f(Python)g(2.0)h(or)g(later)g(and)g(PyXML)g(v)o(ersion)f
+(0.6.6)f(or)i(later)-5 b(.)0 3358 y(The)20 b Fi(ZSI)g
+Fk(homepage)e(is)j(at)g Fh(http://p)n(yw)o(ebsvcs)o(.sf)n(.net/)l
+Fk(.)p eop end
+%%Page: 2 4
+TeXDict begin 2 3 bop eop end
+%%Page: 1 5
+TeXDict begin 1 4 bop 2764 747 a Fo(CONTENTS)0 1594 y
+Fl(1)83 b(Intr)o(oduction)3281 b(1)125 1694 y Fk(1.1)110
+b(Ho)n(w)20 b(to)h(Read)f(this)h(Document)42 b(.)g(.)f(.)g(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)143
+b(1)0 1876 y Fl(2)83 b(WSDL/XMLSchema)21 b(python)f(code)g(generation)
+2143 b(3)125 1976 y Fk(2.1)110 b(wsdl2p)o(y)21 b(.)42
+b(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)
+f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)143
+b(3)125 2076 y(2.2)110 b(Code)20 b(Generation)f(from)g(WSDL)i(and)f
+(XML)g(Schema)39 b(.)j(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)143
+b(7)125 2175 y(2.3)110 b(Adv)n(anced)19 b(Usage)h(P)o(atterns)75
+b(.)41 b(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)g(.)143 b(9)p 0 5549 3901 4 v 3882
+5649 a Ff(i)p eop end
+%%Page: 2 6
+TeXDict begin 2 5 bop 0 5549 3901 4 v 0 5649 a Ff(ii)p
+eop end
+%%Page: 1 7
+TeXDict begin 1 6 bop 0 83 3901 9 v 3503 230 a Ff(CHAPTER)3641
+427 y Fe(ONE)p 0 515 V 2821 978 a Fo(Introduction)0 1457
+y Fi(ZSI)p Fk(,)18 b(the)g(Zolera)g(SO)m(AP)h(Infrastructure,)d(is)j(a)
+f(Python)f(package)g(that)i(pro)o(vides)d(an)i(implementation)e(of)i
+(the)g(SO)m(AP)i(speci\002cation,)0 1556 y(as)32 b(described)d(in)i
+Fg(The)g(SO)-5 b(AP)31 b(1.1)g(Speci\002cation)p Fk(.)54
+b(This)31 b(guide)f(demonstrates)g Fi(ZSI)p Fk(')-5 b(s)31
+b(typecode)e(generation)g(f)o(acilities,)34 b(these)0
+1656 y(typecodes)24 b(are)h(then)g(used)f(to)i(parse)f(and)f(generates)
+h(SO)m(AP)h(messages,)g(and)f(con)m(v)o(erts)e(between)h(nati)n(v)o(e)h
+(Python)f(datatypes)g(and)0 1756 y(SO)m(AP)d(syntax.)0
+1902 y Fi(ZSI)f Fk(requires)f(Python)g(2.3)h(or)g(later)g(and)g(PyXML)g
+(v)o(ersion)f(0.8.3)f(or)i(later)-5 b(.)0 2049 y(The)16
+b Fi(ZSI)h Fk(project)f(is)i(maintained)d(at)j(SourceF)o(or)o(ge,)c(at)
+j Fh(http://p)n(yw)o(ebsvcs)o(.sf)n(.net)m Fk(.)24 b
+Fi(ZSI)17 b Fk(is)g(discussed)g(on)f(the)h(Python)f(web)h(services)0
+2149 y(mailing)i(list,)j(visit)e Fh(http://lists)o(.sourcef)n(orge)o
+(.net)o(/li)o(sts/listi)o(nf)n(o)o(/p)m(yw)o(ebsvcs-talk)15
+b Fk(to)20 b(subscribe.)0 2296 y(F)o(or)g(a)g(lo)n(w-le)n(v)o(el)f
+(treatment)h(of)g(typecodes,)e(and)i(a)g(description)f(of)h(SO)m
+(AP-based)g(processing)f(see)h(the)h(ZSI)f(manual.)0
+2443 y(W)m(ithin)h(this)g(document,)d Fi(tns)j Fk(is)g(used)g(as)g(the)
+g(pre\002x)f(for)g(the)g(application')-5 b(s)20 b(tar)o(get)g
+(namespace,)f(and)h(the)h(term)f Fg(element)g Fk(refers)g(to)0
+2542 y(a)h(DOM)f(element)g(node.\))0 2870 y Fd(1.1)121
+b(Ho)n(w)34 b(to)g(Read)h(this)e(Document)p 0 5549 3901
+4 v 3854 5649 a Ff(1)p eop end
+%%Page: 2 8
+TeXDict begin 2 7 bop 0 5549 3901 4 v 0 5649 a Ff(2)p
+eop end
+%%Page: 3 9
+TeXDict begin 3 8 bop 0 83 3901 9 v 3503 230 a Ff(CHAPTER)3623
+427 y Fe(TW)n(O)p 0 515 V 887 978 a Fo(WSDL/XMLSchema)57
+b(p)-6 b(ython)57 b(code)2926 1227 y(gener)n(ation)0
+1717 y Fk(Handling)28 b(XML)h(Schema)f(\(see)i Fg(XML)f(Sc)o(hema)f
+(speci\002cation)p Fk(\))f(is)j(one)f(of)g(the)g(more)f(dif)n(\002cult)
+h(aspects)g(of)g(using)f(WSDL)i(\(see)0 1817 y Fg(The)24
+b(W)-8 b(eb)24 b(Services)g(Description)f(Langua)o(g)o(e)p
+Fk(.)34 b(Using)24 b(wsdl2p)o(y)f(generates)g(a)h(module)e(with)i(stub)
+g(code)f(for)g(the)h(client)g(interf)o(ace,)0 1916 y(and)d(a)h
+(\224types\224)f(module)f(that)i(contains)f(typecode)e(representations)
+h(of)h(the)h(XML)f(Schema)g(types)h(and)f(elements.)28
+b(The)21 b(generated)0 2016 y(typecodes)i(are)h(re)o(gistered)e(in)j(a)
+f(global)g(schema)f(instance,)i(and)e(once)h(the)g(\224types\224)f
+(module)g(is)i(imported)e(by)g(an)h(application)f(all)0
+2116 y(the)d(global)g(elements)f(declarations)g(and)h(type)g
+(de\002nitions)f(are)h(a)n(v)n(ailable)g(e)n(v)o(erywhere)e(\(see)i
+(section)g(??\).)0 2443 y Fd(2.1)121 b(wsdl2p)l(y)0 2693
+y Fc(2.1.1)100 b(Command)29 b(Line)g(Flags)0 2895 y Ff(Gener)o(al)24
+b(Flags)0 3084 y Fl(-h,)c(\227help)42 b Fk(Display)20
+b(the)g(help)g(message)g(and)g(a)n(v)n(ailable)f(command)g(line)h
+(\003ags)h(that)f(can)g(be)g(passed)g(to)g(wsdl2p)o(y)-5
+b(.)0 3250 y Fl(-f)20 b(FILE,)h(\227\002le=FILE)43 b
+Fk(Create)20 b(bindings)f(for)h(the)g(WSDL)h(which)f(is)h(located)e(at)
+i(the)f(local)g(\002le)h(path.)0 3416 y Fl(-u)g(URL,)f(\227url=URL)43
+b Fk(Create)20 b(bindings)f(for)g(the)i(remote)e(WSDL)i(which)e(is)j
+(located)d(at)i(the)f(pro)o(vided)e(URL.)0 3582 y Fl(-x,)i(\227schema)
+41 b Fk(Just)21 b(process)f(a)g(schema)g(\(xsd\))f(\002le)i(and)f
+(generate)f(the)h(types)g(mapping)e(\002le.)0 3748 y
+Fl(-d,)i(\227deb)n(ug)42 b Fk(Output)19 b(v)o(erbose)g(deb)n(ugging)f
+(messages)i(during)f(code)g(generation.)0 3915 y Fl(-o)h(OUTPUT)p
+441 3915 25 4 v 30 w(DIR,)g(\227output-dir=OUTPUT)p 1510
+3915 V 30 w(DIR)41 b Fk(Write)21 b(generated)e(\002les)i(to)f(OUTPUT)p
+2859 3915 V 29 w(DIR.)0 4183 y Ff(T)-10 b(ypecode)23
+b(Extensions)g(\(Stab)n(le\))0 4372 y Fl(-b,)d(\227complexT)-6
+b(ype)20 b(\(mor)o(e)f(in)i(subsection)g(\))41 b Fk(Generate)19
+b(con)m(v)o(enience)e(functions)h(for)i(comple)o(xT)-7
+b(ypes.)22 b(This)e(includes)f(getters,)208 4471 y(setters,)h(f)o
+(actory)f(methods,)g(and)h(properties.)j(**)d(Do)g(NO)m(T)g(use)h(with)
+f(\226simple-naming)d(**)0 4740 y Ff(De)n(v)n(elopment)24
+b(Extensions)f(\(Unstab)n(le\))0 4928 y Fl(-a,)d(\227addr)o(ess)41
+b Fk(WS-Addressing)19 b(support.)24 b(The)19 b(WS-Addressing)g(schema)h
+(must)g(be)h(included)d(in)j(the)f(corresponding)c(WSDL.)0
+5095 y Fl(-w)-5 b(,)21 b(\227twisted)41 b Fk(Generate)19
+b(a)i(twisted.web)e(client.)25 b(Dependencies:)f(p)o(ython)p
+Fi(>=)p Fk(2.4,)17 b(T)-7 b(wisted)p Fi(>=)p Fk(2.0.0,)18
+b(T)-7 b(wistedW)g(eb)p Fi(>=)p Fk(0.5.0)p 0 5549 3901
+4 v 3854 5649 a Ff(3)p eop end
+%%Page: 4 10
+TeXDict begin 4 9 bop 0 83 a Ff(Customizations)24 b(\(Unstab)n(le\))0
+272 y Fl(-e,)c(\227extended)41 b Fk(Do)20 b(e)o(xtended)e(code)i
+(generation.)0 438 y Fl(-z)g(AN)n(AME,)i(\227aname=AN)n(AME)42
+b Fk(Use)21 b(a)f(custom)g(function,)e(AN)m(AME,)i(for)f(attrib)n(ute)h
+(name)g(creation.)0 604 y Fl(-t)g(TYPES,)h(\227types=TYPES)42
+b Fk(Dump)19 b(the)i(generated)d(type)i(mappings)e(to)j(a)f(\002le)h
+(named,)e(\223TYPES.p)o(y\224.)0 770 y Fl(-s,)h(\227simple-naming)42
+b Fk(Simplify)19 b(the)h(generated)f(naming.)0 936 y
+Fl(-c)h(CLIENTCLASSSUFFIX,)i(\227clientClassSuf\002x=CLIENTCLASSSUFFIX)
+43 b Fk(The)c(suf)n(\002c)g(to)g(use)g(for)f(service)h(client)208
+1036 y(class.)25 b(\(def)o(ault)20 b(\223SO)m(AP\224\))0
+1202 y Fl(-m)h(PYCLASSMAPMODULE,)g
+(\227pyclassMapModule=PYCLASSMAPMODULE)43 b Fk(Use)24
+b(the)g(e)o(xisting)g(e)o(xisting)f(type)208 1301 y(mapping)17
+b(\002le)j(to)g(determine)e(the)h(\223p)o(yclass\224)h(objects)f(to)h
+(be)f(used.)24 b(The)c(module)e(should)g(contain)h(an)g(attrib)n(ute,)g
+(\223mapping\224,)208 1401 y(which)g(is)i(a)g(dictionary)d(of)i(form,)f
+(schemaT)-7 b(ypeName:)23 b(\(moduleName.p)o(y)-5 b(,)16
+b(className\).)0 1686 y Fc(2.1.2)100 b(Basics)28 b(of)g(Code)h(Gener)o
+(ation)0 1889 y Ff(client)24 b(stub)f(module)0 2092 y
+Fk(Using)c(only)g(the)g Fg(Gener)o(al)g(Fla)o(gs)g Fk(options)f(one)h
+(can)g(generate)f(a)i Fl(client)f(stub)i(module)e Fk(from)g(a)g(WSDL)h
+(description,)e(consisting)h(of)0 2191 y(representations)f(of)i(the)g
+(WSDL)h(information)d(items)j Fg(service)p Fk(,)g Fg(binding)p
+Fk(,)d Fg(portT)-6 b(ype)p Fk(,)19 b(and)h Fg(messa)o(g)o(e)p
+Fk(.)0 2338 y(These)30 b(four)f(items)i(are)f(represented)f(by)h(three)
+f(abstractions,)j(consisting)e(of)g(a)g Fb(Locator)g
+Fk(class,)j Fb(P)-5 b(ortT)m(ype)30 b Fk(class,)j(and)d(se)n(v)o(eral)0
+2438 y Fb(Message)c Fk(classes.)44 b(The)25 b Fb(Locator)g
+Fk(will)i(ha)n(v)o(e)f(tw)o(o)g(methods)f(for)h(each)f
+Fg(service)i(port)f Fk(declared)f(in)h(the)g(WSDL)h(de\002nition.)42
+b(One)0 2537 y(method)15 b(returns)h(the)h(address)f(speci\002ed)h(in)g
+(the)g Fg(binding)p Fk(,)e(and)h(the)h(other)f(is)i(a)f(f)o(actory)f
+(method)f(for)h(returning)f(a)i Fb(P)-5 b(ortT)m(ype)16
+b Fk(instance.)0 2637 y(Each)23 b Fb(Message)g Fk(class)h(represents)f
+(the)h(aspects)f(of)g(the)h Fg(binding)e Fk(at)i(the)f(operation)f(le)n
+(v)o(el)h(and)f(belo)n(w)-5 b(,)23 b(and)g(an)o(y)g(type)g(information)
+0 2737 y(speci\002ed)d(by)g Fg(messa)o(g)o(e)g(part)g
+Fk(items.)0 3005 y Ff(types)i(module)0 3208 y Fk(The)h
+Fl(types)f(module)i Fk(is)g(generated)d(with)i(the)g
+Fl(client)g(module)g Fk(b)n(ut)g(it)h(can)e(be)h(created)f
+(independently)-5 b(.)30 b(This)23 b(is)h(especially)e(useful)0
+3308 y(for)e(dealing)f(with)h(schema)g(de\002nitions)f(that)h(aren')o
+(t)f(speci\002ed)h(inside)g(of)g(a)h(WSDL)g(document.)0
+3454 y(The)26 b(module)e(le)n(v)o(el)i(class)h(de\002ntions)e(each)h
+(represent)e(a)j(unique)d(namespace,)i(the)o(y)g(are)g(simply)f
+(wrappers)g(for)h(the)g(contents)f(of)0 3554 y(indi)n(vidual)18
+b(namespaces.)24 b(The)c(inner)f(classes)i(are)f(the)g(typecode)f
+(representations)f(of)i(global)f Fg(type)h(de\002nitions)e
+Fk(\(suf)n(\002x)p 3562 3554 25 4 v 50 w Fb(Def)p Fk(\),)h(and)0
+3654 y Fg(element)h(declar)o(ations)f Fk(\(suf)n(\002x)p
+954 3654 V 49 w Fb(Dec)p Fk(\).)0 3922 y Ff(understanding)24
+b(the)g(gener)o(ated)g(typecodes)0 4125 y Fk(The)18 b(generated)g
+(inner)f(typecode)h(classes)h(come)g(in)g(tw)o(o)g(\003a)n(v)n(ors,)g
+(as)g(mentioned)e(abo)o(v)o(e.)23 b Fg(element)18 b(declar)o(ations)g
+Fk(can)g(be)h(serialized)0 4224 y(into)27 b(XML,)g(generally)e
+Fg(type)i(de\002nitions)f Fk(cannot.)44 b(In)27 b(v)o(ery)e(simple)i
+(terms,)i(the)e Fg(name)f Fk(attrib)n(ute)h(of)g(an)g
+Fg(element)f(declar)o(ation)g Fk(is)0 4324 y(serialized)18
+b(into)f(an)h(XML)g(tag,)g(b)n(ut)g Fg(type)f(de\002nitions)g
+Fk(lack)g(this)i(information)c(so)j(the)o(y)f(cannot)g(be)h(directly)f
+(serialized)g(into)h(an)g(XML)0 4424 y(instance.)44 b(Most)27
+b Fg(element)g(declar)o(ation)p Fk(s)e(declare)h(a)h
+Fg(type)f Fk(attrib)n(ute,)i(this)f(must)g(reference)e(a)i
+Fg(type)g(de\002nition)p Fk(.)42 b(Considering)26 b(the)0
+4523 y(abo)o(v)o(e)17 b(scenario,)g(a)i(generated)e Fg(T)-6
+b(ypeCode)18 b Fk(class)h(representing)d(an)j Fg(element)f(declar)o
+(ation)e Fk(will)k(subclass)e(the)h(generated)d Fg(T)-6
+b(ypeCode)0 4623 y Fk(class)21 b(representing)d(the)i
+Fg(type)h(de\002nition)p Fk(.)0 4891 y Ff(p)n(yclass)81
+b Fk(All)26 b(instances)e(of)h(generated)e Fg(T)-6 b(ypeCode)24
+b Fk(classes)i(will)f(ha)n(v)o(e)f(a)h Fg(pyclass)g Fk(attrib)n(ute,)g
+(instances)g(of)g(the)f Fg(pyclass)h Fk(can)g(be)0 4991
+y(created)f(to)h(store)g(the)g(data)f(representing)f(an)i
+Fg(element)f(declar)o(ation)p Fk(.)37 b(The)25 b Fg(pyclass)g
+Fk(itself)g(has)g(a)g Fg(typecode)f Fk(attrib)n(ute,)h(which)f(is)i(a)0
+5091 y(reference)20 b(to)i(the)g Fg(T)-6 b(ypeCode)21
+b Fk(instance)h(describing)e(the)i(data,)g(thus)g(making)e
+Fg(pyclass)i Fk(instances)g(self-describing.)28 b(When)22
+b(parsing)0 5190 y(an)e(XML)g(instance)g(the)g(data)g(will)h(be)g
+(marshalled)e(into)g(a)i Fg(pyclass)f Fk(instance.)p
+0 5549 3901 4 v 0 5649 a Ff(4)1789 b(Chapter)24 b(2.)51
+b(WSDL/XMLSchema)26 b(p)n(ython)c(code)i(gener)o(ation)p
+eop end
+%%Page: 5 11
+TeXDict begin 5 10 bop 0 83 a Ff(aname)84 b Fk(The)28
+b Fg(aname)g Fk(is)i(a)f Fg(T)-6 b(ypeCode)28 b Fk(instance)g(attrib)n
+(ute,)i(its)g(v)n(alue)e(is)i(a)f(string)g(representing)e(the)h(attrib)
+n(ute)h(name)f(used)g(to)0 183 y(reference)h(data)i(representing)e(an)h
+(element)g(declaration.)56 b(The)30 b(set)i(of)e Fg(XMLSc)o(hema)f
+Fk(element)i(names)f(is)i Fg(NCName)p Fk(,)i(this)d(is)h(a)0
+282 y(superset)20 b(of)g(ordinary)e(identi\002ers)i(in)g
+Fg(python)p Fk(.)0 429 y Fg(Namespaces)g(in)g(XML)236
+667 y Fa(From)44 b(Namespaces)g(in)g(XML)236 759 y(NCName)89
+b(::=)44 b(\(Letter)g(|)h('_'\))f(\(NCNameChar\))1987
+772 y(*)236 850 y(NCNameChar)88 b(::=)45 b(Letter)e(|)i(Digit)f(|)h
+('.')f(|)h('-')f(|)h('_')f(|)h(CombiningChar)d(|)j(Extender)236
+1033 y(From)f(Python)g(Reference)g(Manual)f(\(2.3)i(Identifiers)d(and)j
+(keywords\))236 1124 y(identifier)e(::=)i(\(letter|"_"\))e(\(letter)g
+(|)i(digit)f(|)g("_"\))2478 1137 y(*)236 1307 y(Default)g(set)g(of)h
+(anames)236 1398 y(ANAME)f(::=)h(\("_"\))f(\(letter)f(|)i(digit)f(|)h
+("_"\))1941 1411 y(*)0 1810 y Ff(tr)o(ansf)n(or)r(m)82
+b Fg(NCName)22 b Fk(into)e(an)g Fg(AN)n(AME)104 2023
+y Fk(1.)41 b(preprend)17 b(\224)p 565 2023 25 4 v 30
+w(\224)104 2189 y(2.)41 b(character)18 b(not)i(in)h(set)g(\(letter)f
+Fi(|)g Fk(digit)g Fi(|)h Fk(\224)p 1433 2189 V 30 w(\224\))f(change)f
+(to)h(\224)p 1926 2189 V 30 w(\224)0 2458 y Ff(Attr)q(ib)n(ute)26
+b(Declar)o(ations:)32 b(attrs)p 1015 2458 V 30 w(aname)84
+b Fk(The)21 b Fg(attr)o(s)p 1684 2458 V 30 w(aname)f
+Fk(is)j(a)f Fg(T)-6 b(ypeCode)20 b Fk(instance)i(attrib)n(ute,)f(its)h
+(v)n(alue)f(is)i(a)f(string)f(repre-)0 2557 y(senting)e(the)h(attrib)n
+(ute)g(name)g(used)f(to)i(reference)d(a)i(dictionary)-5
+b(,)18 b(containing)g(data)i(representing)e(attrib)n(ute)i
+(declarations.)j(The)d(k)o(e)o(ys)0 2657 y(of)g(this)h(dictionary)d
+(are)i(the)g Fi(\(namespace,name\))e Fk(tuples,)i(the)g(v)n(alue)g(of)g
+(each)f(k)o(e)o(y)h(represents)f(the)i(v)n(alue)e(of)h(the)g(attrib)n
+(ute.)0 2925 y Ff(Mix)n(ed)j(T)-10 b(e)n(xt)22 b(Content:)29
+b(mix)n(ed)p 980 2925 V 30 w(aname)0 3210 y Fc(2.1.3)100
+b(T)-12 b(ypecode)29 b(Extensions)0 3413 y Ff(\226comple)n(xT)-10
+b(ype)0 3616 y Fk(The)22 b Fg(comple)n(xT)-6 b(ype)22
+b Fk(\003ag)h(pro)o(vides)d(man)o(y)i(con)m(v)o(eniences)e(to)j(the)f
+(programmer)-5 b(.)30 b(This)23 b(option)e(is)j(tested)f(and)f
+(reliable,)h(and)f(highly)0 3715 y(recommended)17 b(by)j(the)g
+(authors.)0 3984 y Ff(lo)o(w-le)n(v)n(el)30 b(descr)q(iption)83
+b Fk(When)26 b(enabled)f(the)h Fi(__metaclass__)e Fk(attrib)n(ute)i
+(will)g(be)g(set)h(on)f(all)g(generated)e Fg(pyclass)p
+Fk(es.)43 b(The)0 4083 y(metaclass)26 b(will)h(introspect)e(the)h
+Fg(typecode)e Fk(attrib)n(ute)i(of)f Fg(pyclass)p Fk(,)i(and)f(create)g
+(a)g(set)h(of)e(helper)g(methods)g(for)g(each)h(element)f(and)0
+4183 y(attrib)n(ute)19 b(declared)f(in)h(the)h Fg(comple)n(xT)-6
+b(ype)18 b Fk(de\002nition.)23 b(This)d(option)e(simply)g(adds)h
+(wrappers)g(for)f(dealing)g(with)i(content,)e(it)i(doesn')o(t)0
+4283 y(modify)f(the)h(generation)e(scheme.)0 4512 y Fl(Getters/Setters)
+40 b Fk(A)21 b(getter)g(and)g(setter)g(function)f(is)i(de\002ned)e(for)
+g(each)h(element)g(of)g(a)g(comple)o(x)f(type.)27 b(The)21
+b(functions)f(are)h(named)208 4612 y Fi(get_element_ANAME)e
+Fk(and)i Fi(set_element_ANAME)d Fk(respecti)n(v)o(ely)-5
+b(.)28 b(In)21 b(this)h(e)o(xample,)e(v)n(ariable)h Fg(wsr)m(eq)h
+Fk(has)g(func-)208 4712 y(tions)h(named)g Fi(get_element__Options)e
+Fk(and)i Fi(set_element__Options)p Fk(.)33 b(In)24 b(addition)e(to)i
+(elements,)h(getters)208 4811 y(and)17 b(setters)i(are)f(generated)e
+(for)i(the)g(attrib)n(utes)g(of)g(a)g(comple)o(x)e(type.)24
+b(F)o(or)18 b(attrib)n(utes,)g(just)h(the)f(name)f(of)h(the)g(attrib)n
+(ute)g(is)h(used)208 4911 y(in)h(determining)e(the)i(method)f(names,)h
+(so)g(get)p 1562 4911 V 29 w(attrib)n(ute)p 1867 4911
+V 29 w(N)m(AME)h(and)e(set)p 2391 4911 V 30 w(attrib)n(ute)p
+2697 4911 V 29 w(N)m(AME)i(are)f(created.)0 5077 y Fl(F)n(actory)e
+(Methods)42 b Fk(If)30 b(an)h(element)e(of)i(a)f(comple)o(x)f(type)h
+(is)i(a)e(comple)o(x)f(type)h(itself,)j(then)d(a)h(con)m(v)o(eniece)d
+(f)o(actory)i(method)f(is)208 5177 y(created)20 b(to)i(get)f(an)h
+(instance)f(of)g(that)h(types)f(holder)f(class.)30 b(The)21
+b(f)o(actory)g(method)f(is)i(named,)f Fi(newANAME)p Fk(,)f(so)i
+Fg(wsr)m(eq)g Fk(has)g(a)208 5276 y(f)o(actory)d(method,)f
+Fi(new_Options)p Fk(.)p 0 5549 3901 4 v 0 5649 a Ff(2.1.)52
+b(wsdl2p)n(y)3366 b(5)p eop end
+%%Page: 6 12
+TeXDict begin 6 11 bop 0 83 a Fl(Pr)o(operties)40 b Fg(Python)20
+b(class)i(pr)l(operties)g Fk(are)f(created)g(for)g(each)g(element)g(of)
+g(the)h(comple)o(x)d(type.)29 b(The)o(y)20 b(are)i(mapped)e(to)h(the)h
+(corre-)208 183 y(sponding)e(getter)i(and)g(setter)h(for)f(that)h
+(element.)31 b(T)-7 b(o)23 b(a)n(v)n(oid)f(name)g(collisions)g(the)h
+(properties)e(are)h(named,)g Fi(PNAME)p Fk(,)g(where)208
+282 y(the)k(\002rst)i(letter)f(of)g(the)g(type')-5 b(s)27
+b(pname)e(attrib)n(ute)i(is)h(capitalized.)44 b(In)27
+b(our)f(running)f(e)o(xample,)i Fg(wsr)m(eq)g Fk(has)g(class)h
+(property)-5 b(,)208 382 y Fi(Options)p Fk(,)32 b(which)f(calls)h
+(functions)d Fi(get_element__Options)f Fk(and)j Fi
+(set_element__Options)d Fk(under)i(the)208 482 y(hood.)236
+803 y Fa(<xsd:complexType)42 b(name='WolframSearchOptions'>)326
+894 y(<xsd:sequence>)416 985 y(<xsd:element)g(name='Query')h
+(minOccurs='0')g(maxOccurs='1')f(type='xsd:string'/>)416
+1077 y(<xsd:element)g(name='Limit')h(minOccurs='0')g(maxOccurs='1')f
+(type='xsd:int'/>)326 1168 y(</xsd:sequence>)326 1259
+y(<xsd:attribute)g(name='timeout')h(type='xsd:double')f(/>)236
+1351 y(</xsd:complexType>)236 1442 y(<xsd:element)h
+(name='WolframSearch'>)326 1533 y(<xsd:complexType>)416
+1625 y(<xsd:sequence>)505 1716 y(<xsd:element)g(name='Options')g
+(minOccurs='0')f(maxOccurs='1')h(type='ns1:WolframSearchOptions'/>)416
+1807 y(</xsd:sequence>)326 1899 y(</xsd:complexType>)236
+1990 y(</xsd:element>)236 2311 y(#)i(Create)f(a)g(request)g(object)g
+(to)g(operation)g(WolframSearch)236 2402 y(#)135 b(to)44
+b(be)h(used)f(as)g(an)h(example)e(below)236 2494 y(from)h
+(WolframSearchService_services)c(import)2119 2507 y(*)236
+2676 y(port)k(=)h(WolframSearchServiceLocator\(\).getWolfra)o(mSearc)o
+(hmyPor)o(tType\()o(\))236 2768 y(wsreq)f(=)h(WolframSearchRequest\(\))
+p 0 5549 3901 4 v 0 5649 a Ff(6)1789 b(Chapter)24 b(2.)51
+b(WSDL/XMLSchema)26 b(p)n(ython)c(code)i(gener)o(ation)p
+eop end
+%%Page: 7 13
+TeXDict begin 7 12 bop 236 174 a Fa(#)45 b(sample)f(usage)g(of)g(the)h
+(generated)e(code)236 357 y(#)i(get)f(an)h(instance)e(of)i(a)f(Options)
+g(holder)g(class)g(using)g(factory)f(method)236 448 y(opts)h(=)h
+(wsreq.new_Options\(\))236 540 y(wsreq.Options)e(=)i(opts)236
+722 y(#)g(assign)f(values)g(using)g(the)g(properties)f(or)h(methods)236
+814 y(opts.Query)f(=)i('Newton')236 905 y
+(opts.set_element__Limit\(10\))236 1088 y(#)g(don't)f(forget)g(the)g
+(attribute)236 1179 y(opts.set_attribute_timeout\(1.0\))236
+1362 y(#)h(At)f(this)h(point)f(the)g(serialized)f(wsreq)h(object)g
+(would)g(resemble)f(this:)236 1453 y(#)i(<WolframSearch>)236
+1544 y(#)135 b(<Options)43 b(timeout="1.0")g
+(xsi:type="tns:WolframSearchOptions">)236 1636 y(#)224
+b(<Query)44 b(xsi:type="xsd:string">Newton</Query>)236
+1727 y(#)224 b(<Limit)44 b(xsi:type="xsd:double">10.0</Limit>)236
+1818 y(#)135 b(</Options>)236 1910 y(#)45 b(</WolframSearch>)236
+2092 y(#)g(ready)f(call)g(the)g(remote)g(operation)236
+2183 y(wsresp)g(=)h(port.WolframSearch\(wsreq\))236 2366
+y(#)g(returned)e(WolframSearchResponse)f(type)i(holder)g(also)g(has)g
+(conveniences)236 2457 y(print)g('SearchTime:',)f
+(wsresp.Result.SearchTime)0 2906 y Fd(2.2)121 b(Code)35
+b(Gener)o(ation)g(from)e(WSDL)h(and)h(XML)e(Schema)0
+3139 y Fk(This)47 b(section)f(co)o(v)o(ers)g(wsdl2p)o(y)-5
+b(,)51 b(the)c(second)f(w)o(ay)g(ZSI)h(pro)o(vides)e(to)i(access)g
+(WSDL)h(services.)104 b(Gi)n(v)o(en)46 b(the)g(path)g(to)0
+3239 y(a)g(WSDL)h(service,)k(tw)o(o)46 b(\002les)h(are)e(generated,)50
+b(a)d(')-5 b(service')45 b(\002le)h(and)f(a)h(')o(types')f(\002le,)52
+b(that)46 b(one)f(can)h(then)f(use)h(to)g(ac-)0 3338
+y(cess)j(the)f(service.)107 b(As)49 b(an)f(e)o(xample,)53
+b(we)48 b(will)h(use)f(the)g(search)g(service)f(pro)o(vided)f(by)h(W)-7
+b(olfram)48 b(Research)f(Inc.)3819 3335 y(c)3797 3338
+y Fj(\015)o Fk(,)0 3438 y Fh(http://w)o(ebser)r(vices)o(.w)o(olfr)o
+(am.com/w)o(olfr)o(amsearch/)-6 b Fk(,)27 b(which)f(pro)o(vides)e(a)i
+(service)g(for)f(searching)g(the)h(popular)e(MathW)-7
+b(orld)25 b(site,)0 3538 y Fh(http://mathw)o(or)q(ld.w)o(olfr)o
+(am.com/)-6 b Fk(,)21 b(among)e(others.)236 3776 y Fa(wsdl2py)44
+b(--complexType)f(--url=http://webservices.wolfram.com)o(/servi)o
+(ces/Se)o(archSe)o(rvices)o(/Wolfr)o(amSear)o(ch2.ws)o(dl)0
+4062 y Fk(Run)25 b(the)f(abo)o(v)o(e)f(command)g(to)i(generate)e(the)i
+(service)f(and)g(type)g(\002les.)39 b(wsdl2p)o(y)24 b(uses)h(the)g
+Fg(name)f Fk(attrib)n(ute)g(of)g(the)h Fg(wsdl:service)0
+4162 y Fk(element)f(to)i(name)e(the)h(resulting)f(\002les.)41
+b(In)24 b(this)i(e)o(xample,)e(the)h(service)g(name)g(is)g
+Fg(W)-8 b(olfr)o(amSear)m(c)o(hService)p Fk(.)38 b(Therefore)23
+b(the)i(\002les)0 4262 y Fg(W)-8 b(olfr)o(amSear)m(c)o(hService)p
+760 4262 25 4 v 27 w(services.py)20 b Fk(and)g Fg(W)-8
+b(olfr)o(amSear)m(c)o(hService)p 2070 4262 V 27 w(services)p
+2364 4262 V 31 w(types.py)19 b Fk(should)g(be)h(generated.)0
+4409 y(The)k(')-5 b(service')24 b(\002le)h(contains)f(locator)m(,)g
+(portT)-7 b(ype,)23 b(and)h(message)h(classes.)38 b(A)25
+b(locator)f(instance)g(is)h(used)f(to)h(get)g(an)f(instance)g(of)g(a)0
+4508 y(portT)-7 b(ype)21 b(class,)k(which)e(is)h(a)f(remote)f(proxy)g
+(object.)33 b(Message)23 b(instances)g(are)g(sent)g(and)g(recei)n(v)o
+(ed)f(through)f(the)i(methods)f(of)h(the)0 4608 y(portT)-7
+b(ype)18 b(instance.)0 4755 y(The)32 b(')o(types')e(\002le)j(contains)e
+(class)i(representations)e(of)g(the)h(de\002nitions)g(and)f
+(declarations)g(de\002ned)g(by)g(all)i(schema)f(instances)0
+4854 y(imported)19 b(by)g(the)i(WSDL)f(de\002nition.)k(XML)d(Schema)e
+(attrib)n(utes,)h(wildcards,)f(and)h(deri)n(v)o(ed)e(types)i(are)g(not)
+g(fully)g(handled.)p 0 5549 3901 4 v 0 5649 a Ff(2.2.)52
+b(Code)24 b(Gener)o(ation)g(from)f(WSDL)h(and)f(XML)h(Schema)1898
+b(7)p eop end
+%%Page: 8 14
+TeXDict begin 8 13 bop 0 83 a Fc(2.2.1)100 b(Example)29
+b(Use)f(of)g(Gener)o(ated)h(Code)0 286 y Fk(The)e(follo)n(wing)e(sho)n
+(ws)i(ho)n(w)f(to)i(call)f(a)h(proxy)d(method)g(for)i
+Fg(W)-8 b(olfr)o(amSear)m(c)o(h)p Fk(.)44 b(It)27 b(assumes)g(wsdl2p)o
+(y)f(has)h(already)f(been)h(run)f(as)0 386 y(sho)n(wn)19
+b(in)i(the)f(section)g(abo)o(v)o(e.)j(The)d(e)o(xample)f(will)i(be)f(e)
+o(xplained)e(in)j(greater)e(detail)h(belo)n(w)-5 b(.)236
+624 y Fa(#)45 b(import)f(the)g(generated)f(class)h(stubs)236
+715 y(from)g(WolframSearchService_services)c(import)2119
+728 y(*)236 898 y(#)45 b(get)f(a)h(port)f(proxy)g(instance)236
+989 y(loc)h(=)f(WolframSearchServiceLocator\(\))236 1080
+y(port)g(=)h(loc.getWolframSearchmyPortType\(\))236 1263
+y(#)g(create)f(a)g(new)h(request)236 1354 y(req)g(=)f
+(WolframSearchRequest\(\))236 1446 y(req.Options)f(=)i
+(req.new_Options\(\))236 1537 y(req.Options.Query)d(=)j('newton')236
+1720 y(#)g(call)f(the)g(remote)g(method)236 1811 y(resp)g(=)h
+(port.WolframSearch\(req\))236 1994 y(#)g(print)f(results)236
+2085 y(print)g('Search)g(Time:',)g(resp.Result.SearchTime)236
+2176 y(print)g('Total)g(Matches:',)f(resp.Result.TotalMatches)236
+2268 y(for)i(hit)f(in)g(resp.Result.Matches.Item:)416
+2359 y(print)g('--',)g(hit.Title)0 2645 y Fk(No)n(w)20
+b(each)g(section)g(of)g(the)g(code)f(abo)o(v)o(e)g(will)i(be)f(e)o
+(xplained.)236 2884 y Fa(from)44 b(WolframSearchService_services)c
+(import)2119 2897 y(*)0 3170 y Fk(W)-7 b(e)22 b(are)e(primarily)f
+(interested)h(in)g(the)h(service)f(locator)f(that)i(is)g(imported.)j
+(The)c(binding)f(proxy)g(and)h(classes)h(for)f(all)h(the)f(messages)0
+3270 y(are)g(additionally)f(imported.)k(Look)c(at)i(the)f
+Fg(W)-8 b(olfr)o(amSear)m(c)o(hService)p 2045 3270 25
+4 v 27 w(services.py)20 b Fk(\002le)h(for)f(more)f(information.)236
+3508 y Fa(loc)45 b(=)f(WolframSearchServiceLocator\(\))236
+3599 y(port)g(=)h(loc.getWolframSearchmyPortType\(\))0
+3886 y Fk(Using)19 b(an)g(instance)f(of)h(the)g(locator)m(,)f(we)h
+(fetch)g(an)g(instance)f(of)h(the)g(port)f(proxy)f(which)i(is)h(used)e
+(for)h(in)m(v)n(oking)e(the)i(remote)f(methods)0 3985
+y(pro)o(vided)27 b(by)h(the)i(service.)51 b(In)29 b(this)h(case)g(the)f
+(def)o(ault)f Fg(location)g Fk(speci\002ed)h(in)h(the)f
+Fg(wsdlsoap:addr)m(ess)f Fk(element)g(is)i(used.)52 b(Y)-9
+b(ou)0 4085 y(can)25 b(optionally)e(pass)j(a)g(url)f(to)g(the)g(port)g
+(getter)g(method)f(to)h(specify)f(an)i(alternate)e(location)g(to)i(be)f
+(used.)40 b(The)24 b Fg(portT)-6 b(ype)25 b Fk(-)g Fg(name)0
+4185 y Fk(attrib)n(ute)30 b(is)h(used)f(to)g(determine)f(the)h(method)f
+(name)h(to)g(fetch)g(a)g(port)g(proxy)e(instance.)55
+b(In)30 b(this)g(e)o(xample,)h(the)g(port)e(name)h(is)0
+4284 y Fg(W)-8 b(olfr)o(amSear)m(c)o(hmyP)h(ortT)h(ype)p
+Fk(,)17 b(hence)j(the)g(method)f(of)h(the)g(locator)f(for)h(fetching)f
+(the)h(proxy)e(is)j Fg(g)o(etW)-8 b(olfr)o(amSear)m(c)o(hmyP)h(ortT)h
+(ype)p Fk(.)0 4431 y(The)19 b(\002rst)h(step)g(in)f(calling)g
+Fg(W)-8 b(olfr)o(amSear)m(c)o(h)18 b Fk(is)i(to)g(create)f(a)h(request)
+e(object)h(corresponding)d(to)k(the)f(input)g(message)g(of)g(the)g
+(method.)0 4531 y(In)g(this)h(case,)g(the)f(name)g(of)g(the)g(message)h
+(is)g Fg(W)-8 b(olfr)o(amSear)m(c)o(hRequest)p Fk(.)22
+b(A)e(class)g(representing)e(this)h(message)h(w)o(as)g(imported)e(from)
+0 4630 y(the)i(service)g(module.)236 4869 y Fa(req)45
+b(=)f(WolframSearchRequest\(\))236 4960 y(req.Options)f(=)i
+(req.new_Options\(\))236 5051 y(req.Options.Query)d(=)j('newton')0
+5338 y Fk(Once)21 b(a)h(request)f(object)g(is)h(created)f(we)h(need)f
+(to)g(populate)f(the)i(instance)f(with)h(the)f(information)e(we)j(w)o
+(ant)g(to)f(use)h(in)g(our)e(request.)p 0 5549 3901 4
+v 0 5649 a Ff(8)1789 b(Chapter)24 b(2.)51 b(WSDL/XMLSchema)26
+b(p)n(ython)c(code)i(gener)o(ation)p eop end
+%%Page: 9 15
+TeXDict begin 9 14 bop 0 83 a Fk(This)25 b(is)g(where)f(the)h
+Fi(--complexType)e Fk(option)g(we)i(passed)f(to)h(wsdl2p)o(y)f(will)h
+(come)f(in)h(handy)-5 b(.)36 b(This)25 b(caused)f(the)g(creation)g(of)0
+183 y(functions)j(for)h(getting)g(and)g(setting)g(elements)g(and)g
+(attrib)n(utes)h(of)f(the)g(type,)i(class)g(properties)d(for)h(each)g
+(element,)i(and)e(con)m(v)o(e-)0 282 y(nience)21 b(functions)g(for)g
+(creating)g(ne)n(w)h(instances)g(of)g(elements)g(of)f(comple)o(x)g
+(types.)30 b(This)22 b(functionality)e(is)j(e)o(xplained)d(in)i(detail)
+g(in)0 382 y(subsection)d(2.1.3.)0 529 y(Once)g(the)h(request)e
+(instance)h(is)i(populated,)c(calling)i(the)h(remote)e(service)h(is)i
+(easy)-5 b(.)24 b(Using)19 b(the)h(port)e(proxy)g(we)i(call)g(the)f
+(method)f(we)0 628 y(are)j(interested)g(in.)29 b(An)21
+b(instance)g(of)h(the)f(p)o(ython)f(class)i(representing)e(the)h
+(return)f(type)h(is)h(returned)e(by)h(this)h(call.)29
+b(The)21 b Fg(r)m(esp)h Fk(object)0 728 y(can)e(be)g(used)g(to)g
+(introspect)g(the)g(result)g(of)g(the)g(remote)f(call.)236
+966 y Fa(resp)44 b(=)h(port.WolframSearch\(req\))0 1253
+y Fk(Here)28 b(we)h(see)g(that)f(the)g(response)g(message,)i
+Fg(r)m(esp)p Fk(,)g(represents)d(type)h Fg(W)-8 b(olfr)o(amSear)m(c)o
+(hReturn)p Fk(.)47 b(This)28 b(object)g(has)h(one)e(element,)0
+1353 y Fg(Result)20 b Fk(which)g(contains)f(the)i(search)e(results)i
+(for)f(our)f(search)h(of)g(the)g(k)o(e)o(yw)o(ord,)e
+Fi(newton)p Fk(.)236 1591 y Fa(print)44 b('Search)g(Time:',)g
+(resp.Result.SearchTime)236 1682 y(...)0 1969 y Fk(Refer)20
+b(to)g(the)h(wsdl)f(for)g Fg(W)-8 b(olfr)o(amSear)m(c)o(hService)18
+b Fk(for)h(more)h(details)g(on)g(the)g(returned)f(information.)0
+2296 y Fd(2.3)121 b(Adv)m(anced)35 b(Usage)f(P)-5 b(atter)s(ns)0
+2529 y Fk(Not)20 b(done.)p 0 5549 3901 4 v 0 5649 a Ff(2.3.)52
+b(Adv)n(anced)23 b(Usage)g(P)m(atter)r(ns)2703 b(9)p
+eop end
+%%Trailer
+
+userdict /end-hook known{end-hook}if
+%%EOF
diff --git a/doc/guide.tex b/doc/guide.tex
new file mode 100644
index 0000000..8a66e9e
--- /dev/null
+++ b/doc/guide.tex
@@ -0,0 +1,93 @@
+% $Header$
+\def\ZSI{\module{ZSI}}
+\documentclass{manual}
+\title{ZSI: The Zolera Soap Infrastructure \\
+ User's Guide}
+\author{}
+\authoraddress{
+ \email{}
+}
+\date{May 31, 2006}% edited date, not printed date
+\input{version}
+
+% tell latex2html to load the verbatim package so we get \verbatiminput
+\ifx\htmlonly\undefined\else\usepackage{verbatim}\fi
+
+\begin{document}
+
+\maketitle
+
+
+\centerline{\strong{COPYRIGHT}}
+
+Copyright \copyright{} 2001, Zolera Systems, Inc.\\
+All Rights Reserved.
+
+Copyright \copyright{} 2002-2003, Rich Salz.\\
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or
+sell copies of the Software, and to permit persons to whom the Software
+is furnished to do so, provided that the above copyright notice(s) and
+this permission notice appear in all copies of the Software and that
+both the above copyright notice(s) and this permission notice appear in
+supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+\vspace{1.5cm}
+
+\centerline{\strong{Acknowledgments}}
+
+We are grateful to the members of the \code{soapbuilders}
+mailing list (see \url{http://groups.yahoo.com/soapbuilders}),
+Fredrik Lundh for his \code{soaplib} package (see
+\url{http://www.secretlabs.com/downloads/index.htm\#soap}),
+Cayce Ullman and Brian Matthews for their \code{SOAP.py} package
+(see \url{http://sourceforge.net/projects/pywebsvcs}).
+
+We are particularly grateful to Brian Lloyd and the Zope Corporation
+(\url{http://www.zope.com}) for letting us incorporate his ZOPE
+WebServices package and documentation into \ZSI{}.
+\begin{abstract}
+\noindent
+\ZSI{}, the Zolera SOAP Infrastructure, is a Python package that
+provides an implementation of SOAP messaging, as described in
+\citetitle[http://www.w3.org/TR/soap]{The SOAP 1.1 Specification}.
+In particular, \ZSI{} parses and generates SOAP messages, and
+converts between native Python datatypes and SOAP syntax.
+It can also be used to build applications using
+\citetitle[http://www.w3.org/TR/SOAP-attachments]{SOAP Messages with
+Attachments}.
+\ZSI{} is ``transport neutral'', and provides only a simple
+I/O and dispatch framework; a more complete solution is the
+responsibility of the application using \ZSI{}.
+As usage patterns emerge, and common application frameworks are
+more understood, this may change.
+
+\ZSI{} requires Python 2.0 or later and PyXML version 0.6.6 or later.
+
+The \ZSI{} homepage is at \url{http://pywebsvcs.sf.net/}.
+
+\end{abstract}
+
+\tableofcontents{}
+\input{guide01-intro}
+\input{guide02-wsdl2py}
+\end{document}
diff --git a/doc/guide01-intro.tex b/doc/guide01-intro.tex
new file mode 100644
index 0000000..d874809
--- /dev/null
+++ b/doc/guide01-intro.tex
@@ -0,0 +1,27 @@
+\chapter{Introduction}
+
+\ZSI{}, the Zolera SOAP Infrastructure, is a Python package that
+provides an implementation of the SOAP specification, as described in
+\citetitle[http://www.w3.org/TR/soap]{The SOAP 1.1 Specification}.
+This guide demonstrates \ZSI{}'s typecode generation facilities, these
+typecodes are then used to parse and generates SOAP messages, and
+converts between native Python datatypes and SOAP syntax.
+
+\module{ZSI} requires Python 2.3 or later and PyXML version 0.8.3 or later.
+
+The \module{ZSI} project is maintained at SourceForge, at
+\url{http://pywebsvcs.sf.net}.
+\ZSI{} is discussed on the Python web services mailing list, visit
+\url{http://lists.sourceforge.net/lists/listinfo/pywebsvcs-talk}
+to subscribe.
+
+For a low-level treatment of typecodes, and a description of SOAP-based
+processing see the ZSI manual.
+
+Within this document, \code{tns} is used as the prefix for the
+application's target namespace, and the term
+\emph{element} refers to a DOM element node.)
+
+\section{How to Read this Document}
+
+
diff --git a/doc/guide02-wsdl2py.tex b/doc/guide02-wsdl2py.tex
new file mode 100755
index 0000000..b7570d9
--- /dev/null
+++ b/doc/guide02-wsdl2py.tex
@@ -0,0 +1,365 @@
+\chapter{WSDL/XMLSchema python code generation}
+
+Handling XML Schema (see \citetitle[http://www.w3.org/XML/Schema]{XML Schema specification})
+is one of the more difficult aspects of using WSDL (see
+\citetitle[http://www.w3.org/TR/wsdl]{The Web Services Description Language}.
+Using wsdl2py generates a module with stub code for the client interface,
+and a "types" module that contains typecode representations of the XML Schema types and elements. The generated typecodes are registered in a global schema instance, and once the "types" module is imported by an application all the global elements declarations and type definitions are available everywhere (see section ??).
+
+\section{wsdl2py}
+
+\subsection{Command Line Flags}
+
+\subsubsection{General Flags}
+\begin{description}
+\item[-h, ---help] Display the help message and available command line
+flags that can be passed to wsdl2py.
+\item[-f FILE, ---file=FILE] Create bindings for the WSDL which is located at
+the local file path.
+\item[-u URL, ---url=URL] Create bindings for the remote WSDL which is located
+at the provided URL.
+\item[-x, ---schema] Just process a schema (xsd) file and generate the types
+mapping file.
+\item[-d, ---debug] Output verbose debugging messages during code generation.
+\item[-o OUTPUT_DIR, ---output-dir=OUTPUT_DIR] Write generated files to OUTPUT_DIR.
+\end{description}
+
+\subsubsection{Typecode Extensions (Stable) }
+\begin{description}
+\item[-b, ---complexType (more in subsection~\label{subsubsection:complexType})]
+Generate convenience functions for complexTypes. This includes getters,
+setters, factory methods, and properties. ** Do NOT use with --simple-naming **
+\end{description}
+
+\subsubsection{Development Extensions (Unstable) }
+\begin{description}
+\item[-a, ---address] WS-Addressing support. The WS-Addressing schema must be
+included in the corresponding WSDL.
+\item[-w, ---twisted] Generate a twisted.web client. Dependencies:
+python\verb!>=!2.4, Twisted\verb!>=!2.0.0, TwistedWeb\verb!>=!0.5.0
+\end{description}
+
+\subsubsection{Customizations (Unstable) }
+\begin{description}
+\item[-e, ---extended] Do extended code generation.
+\item[-z ANAME, ---aname=ANAME] Use a custom function, ANAME, for attribute name
+creation.
+\item[-t TYPES, ---types=TYPES] Dump the generated type mappings to a file
+named, ``TYPES.py''.
+\item[-s, ---simple-naming] Simplify the generated naming.
+\item[-c CLIENTCLASSSUFFIX, ---clientClassSuffix=CLIENTCLASSSUFFIX] The suffic
+to use for service client class. (default ``SOAP'')
+\item[-m PYCLASSMAPMODULE, ---pyclassMapModule=PYCLASSMAPMODULE] Use the
+existing existing type mapping file to determine the ``pyclass'' objects to be
+used. The module should contain an attribute, ``mapping'', which is a
+dictionary of form, {schemaTypeName: (moduleName.py, className)}.
+\end{description}
+
+\subsection{Basics of Code Generation}
+\label{subsection:Basics of Code Generation}
+
+\subsubsection{client stub module}
+Using only the {\it General Flags} options one can generate a
+{\bfseries client stub module} from a WSDL description, consisting of
+representations of the WSDL information items {\it service}, {\it binding},
+{\it portType}, and {\it message}.
+
+
+These four items are represented by three abstractions, consisting of a
+{\it \bfseries Locator} class, {\it \bfseries PortType} class, and several
+{\it \bfseries Message} classes. The {\it \bfseries Locator} will have two methods for each {\it service port} declared in
+the WSDL definition. One method returns the address specified in the {\it binding}, and the other is a factory method for returning a {\it \bfseries PortType}
+instance. Each {\it \bfseries Message} class represents the aspects of the {\it binding}
+at the operation level and below, and any type information specified by {\it message part} items.
+
+\subsubsection{types module}
+The {\bfseries types module} is generated with the {\bfseries client module} but
+it can be created independently. This is especially useful for dealing with
+schema definitions that aren't specified inside of a WSDL document.
+
+The module level class defintions each represent a unique namespace, they are
+simply wrappers for the contents of individual namespaces. The inner classes
+are the typecode representations of global {\it type definitions} (suffix {\it \bfseries _Def}),
+and {\it element declarations} (suffix {\it \bfseries _Dec}).
+
+\subsubsection{understanding the generated typecodes}
+The generated inner typecode classes come in two flavors, as mentioned above.
+{\it element declarations} can be serialized into XML, generally {\it type
+definitions} cannot. In very simple terms, the {\it name} attribute of an {\it element declaration} is serialized into an XML tag, but {\it type
+definitions} lack this information so they cannot be directly serialized into an
+XML instance. Most {\it element declaration}s declare a {\it type} attribute,
+this must reference a {\it type definition}. Considering the above scenario, a
+generated {\it TypeCode} class representing an {\it element declaration} will
+subclass the generated {\it TypeCode} class representing the {\it type
+definition}.
+
+\paragraph{pyclass}
+All instances of generated {\it TypeCode} classes will have a {\it pyclass}
+attribute, instances of the {\it pyclass} can be created to store the data
+representing an {\it element declaration}. The {\it pyclass} itself has a {\it
+typecode} attribute, which is a reference to the {\it
+TypeCode} instance describing the data, thus making {\it pyclass} instances
+self-describing.
+When parsing an XML instance the data will be marshalled into a {\it pyclass}
+instance.
+
+\paragraph{aname}
+The {\it aname} is a {\it TypeCode} instance attribute, its value is a string representing the
+attribute name used to reference data representing an element declaration. The set
+of {\it XMLSchema} element names is {\it NCName}, this is a superset of ordinary
+ identifiers in {\it python}.
+
+\citetitle[http://www.w3.org/TR/REC-xml-names/]{Namespaces in XML}
+
+\begin{verbatim}
+From Namespaces in XML
+ NCName ::= (Letter | '_') (NCNameChar)*
+ NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
+
+From Python Reference Manual (2.3 Identifiers and keywords)
+ identifier ::= (letter|"_") (letter | digit | "_")*
+
+Default set of anames
+ ANAME ::= ("_") (letter | digit | "_")*
+\end{verbatim}
+
+\paragraph{transform} {\it NCName} into an {\it ANAME}
+\begin{enumerate}
+\item preprend "_"
+\item character not in set (letter \verb!|! digit \verb!|! "_") change to "_"
+\end{enumerate}
+
+\paragraph{Attribute Declarations: attrs_aname}
+The {\it attrs_aname} is a {\it TypeCode} instance attribute, its value is a string representing the
+attribute name used to reference a dictionary, containing data representing
+attribute declarations. The keys of this dictionary are the
+\verb!(namespace,name)! tuples, the value of each key represents the value of
+the attribute.
+
+
+\paragraph{Mixed Text Content: mixed_aname}
+
+\subsection{Typecode Extensions}
+
+\subsubsection{--complexType}
+\label{subsubsection:complexType}
+The {\it complexType} flag provides many conveniences to the programmer. This
+option is tested and reliable, and highly recommended by the authors.
+
+\paragraph{low-level description}
+When enabled the \verb!__metaclass__! attribute will be set on all generated
+{\it pyclass}es. The metaclass will introspect the {\it typecode} attribute of
+{\it pyclass}, and create a set of helper methods for each element
+and attribute declared in the {\it complexType} definition. This option simply
+adds wrappers for dealing with content, it doesn't modify the generation scheme.
+
+\begin{description}
+\item[Getters/Setters] A getter and setter function is defined for each element
+of a complex type. The functions are named \verb!get_element_ANAME! and
+\verb!set_element_ANAME! respectively. In this example, variable \var{wsreq}
+has functions named \verb!get_element__Options! and \verb!set_element__Options!.
+ In addition to elements, getters and setters are generated for the attributes
+ of a complex type. For attributes, just the name of the attribute is used in
+ determining the method names, so get_attribute_NAME and set_attribute_NAME are
+ created.
+
+\item[Factory Methods] If an element of a complex type is a complex type itself,
+then a conveniece factory method is created to get an instance of that types
+holder class. The factory method is named, \verb!newANAME!, so \var{wsreq} has
+a factory method, \verb!new_Options!.
+
+\item[Properties]
+\citetitle[http://www.python.org/download/releases/2.2/descrintro/#property]{Python class properties}
+are created for each element of the complex type. They are mapped to the
+corresponding getter and setter for that element. To avoid name collisions the
+properties are named, \verb!PNAME!, where the first letter of the type's pname
+attribute is capitalized. In our running example, \var{wsreq} has class
+property, \verb!Options!, which calls functions \verb!get_element__Options! and
+\verb!set_element__Options! under the hood.
+
+\end{description}
+
+\begin{verbatim}
+<xsd:complexType name='WolframSearchOptions'>
+ <xsd:sequence>
+ <xsd:element name='Query' minOccurs='0' maxOccurs='1' type='xsd:string'/>
+ <xsd:element name='Limit' minOccurs='0' maxOccurs='1' type='xsd:int'/>
+ </xsd:sequence>
+ <xsd:attribute name='timeout' type='xsd:double' />
+</xsd:complexType>
+<xsd:element name='WolframSearch'>
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name='Options' minOccurs='0' maxOccurs='1' type='ns1:WolframSearchOptions'/>
+ </xsd:sequence>
+ </xsd:complexType>
+</xsd:element>
+\end{verbatim}
+
+\begin{verbatim}
+# Create a request object to operation WolframSearch
+# to be used as an example below
+from WolframSearchService_services import *
+
+port = WolframSearchServiceLocator().getWolframSearchmyPortType()
+wsreq = WolframSearchRequest()
+\end{verbatim}
+
+
+
+\begin{verbatim}
+# sample usage of the generated code
+
+# get an instance of a Options holder class using factory method
+opts = wsreq.new_Options()
+wsreq.Options = opts
+
+# assign values using the properties or methods
+opts.Query = 'Newton'
+opts.set_element__Limit(10)
+
+# don't forget the attribute
+opts.set_attribute_timeout(1.0)
+
+# At this point the serialized wsreq object would resemble this:
+# <WolframSearch>
+# <Options timeout="1.0" xsi:type="tns:WolframSearchOptions">
+# <Query xsi:type="xsd:string">Newton</Query>
+# <Limit xsi:type="xsd:double">10.0</Limit>
+# </Options>
+# </WolframSearch>
+
+# ready call the remote operation
+wsresp = port.WolframSearch(wsreq)
+
+# returned WolframSearchResponse type holder also has conveniences
+print 'SearchTime:', wsresp.Result.SearchTime
+\end{verbatim}
+
+\section{Code Generation from WSDL and XML Schema}
+
+This section covers wsdl2py, the second way ZSI provides to access WSDL
+services. Given the path to a WSDL service, two files are generated, a
+'service' file and a 'types' file, that one can then use to access the
+service. As an example, we will use the search service provided by Wolfram
+Research Inc.\copyright{}, \url{http://webservices.wolfram.com/wolframsearch/},
+which provides a service for searching the popular MathWorld site,
+\url{http://mathworld.wolfram.com/}, among others.
+
+\begin{verbatim}
+wsdl2py --complexType --url=http://webservices.wolfram.com/services/SearchServices/WolframSearch2.wsdl
+\end{verbatim}
+
+Run the above command to generate the service and type files. wsdl2py uses
+the {\it name} attribute of the {\it wsdl:service} element to name the resulting files.
+In this example, the service name is {\it WolframSearchService}. Therefore the files
+{\it WolframSearchService_services.py} and {\it WolframSearchService_services_types.py}
+should be generated.
+
+The 'service' file contains locator, portType, and message classes.
+A locator instance is used to get an instance of a portType class,
+which is a remote proxy object. Message instances are sent and received
+through the methods of the portType instance.
+
+The 'types' file contains class representations of the definitions and
+declarations defined by all schema instances imported by the WSDL definition.
+XML Schema attributes, wildcards, and derived types are not fully
+handled.
+
+\subsection{Example Use of Generated Code}
+
+The following shows how to call a proxy method for {\it WolframSearch}. It
+assumes wsdl2py has already been run as shown in the section above. The example
+will be explained in greater detail below.
+
+\begin{verbatim}
+# import the generated class stubs
+from WolframSearchService_services import *
+
+# get a port proxy instance
+loc = WolframSearchServiceLocator()
+port = loc.getWolframSearchmyPortType()
+
+# create a new request
+req = WolframSearchRequest()
+req.Options = req.new_Options()
+req.Options.Query = 'newton'
+
+# call the remote method
+resp = port.WolframSearch(req)
+
+# print results
+print 'Search Time:', resp.Result.SearchTime
+print 'Total Matches:', resp.Result.TotalMatches
+for hit in resp.Result.Matches.Item:
+ print '--', hit.Title
+\end{verbatim}
+
+Now each section of the code above will be explained.
+
+\begin{verbatim}
+from WolframSearchService_services import *
+\end{verbatim}
+
+We are primarily interested in the service locator that is imported. The
+binding proxy and classes for all the messages are additionally imported.
+Look at the {\it WolframSearchService_services.py} file for more information.
+
+\begin{verbatim}
+loc = WolframSearchServiceLocator()
+port = loc.getWolframSearchmyPortType()
+\end{verbatim}
+
+Using an instance of the locator, we fetch an instance of the port proxy
+which is used for invoking the remote methods provided by the service. In
+this case the default {\it location} specified in the {\it wsdlsoap:address}
+element is used. You can optionally pass a url to the port getter method to
+specify an alternate location to be used. The {\it portType} - {\it name}
+attribute is used to determine the method name to fetch a port proxy instance.
+In this example, the port name is {\it WolframSearchmyPortType}, hence the
+method of the locator for fetching the proxy is {\it getWolframSearchmyPortType}.
+
+The first step in calling {\it WolframSearch} is to create a request object
+corresponding to the input message of the method. In this case, the name of
+the message is {\it WolframSearchRequest}. A class representing this message
+was imported from the service module.
+
+\begin{verbatim}
+req = WolframSearchRequest()
+req.Options = req.new_Options()
+req.Options.Query = 'newton'
+\end{verbatim}
+
+Once a request object is created we need to populate the instance with the
+information we want to use in our request. This is where the {\tt --complexType}
+option we passed to wsdl2py will come in handy. This caused the creation of
+functions for getting and setting elements and attributes of the type, class
+properties for each element, and convenience functions for creating new instances
+of elements of complex types. This functionality is explained in detail in
+subsection~\ref{subsubsection:complexType}.
+
+Once the request instance is populated, calling the remote service is easy. Using
+the port proxy we call the method we are interested in. An instance of the python
+class representing the return type is returned by this call. The \var{resp} object
+can be used to introspect the result of the remote call.
+
+\begin{verbatim}
+resp = port.WolframSearch(req)
+\end{verbatim}
+
+Here we see that the response message, \var{resp}, represents type {\it WolframSearchReturn}.
+This object has one element, {\it Result} which contains the search results for our
+search of the keyword, {\tt newton}.
+
+\begin{verbatim}
+print 'Search Time:', resp.Result.SearchTime
+...
+\end{verbatim}
+
+Refer to the wsdl for {\it WolframSearchService} for more details on the returned information.
+
+
+\section{Advanced Usage Patterns}
+Not done.
+
+
diff --git a/doc/index.png b/doc/index.png
new file mode 100644
index 0000000..cd918af
--- /dev/null
+++ b/doc/index.png
Binary files differ
diff --git a/doc/modules.png b/doc/modules.png
new file mode 100644
index 0000000..8fa8b75
--- /dev/null
+++ b/doc/modules.png
Binary files differ
diff --git a/doc/next.png b/doc/next.png
new file mode 100644
index 0000000..cfe5e51
--- /dev/null
+++ b/doc/next.png
Binary files differ
diff --git a/doc/previous.png b/doc/previous.png
new file mode 100644
index 0000000..497def4
--- /dev/null
+++ b/doc/previous.png
Binary files differ
diff --git a/doc/pyfav.png b/doc/pyfav.png
new file mode 100644
index 0000000..d2d8669
--- /dev/null
+++ b/doc/pyfav.png
Binary files differ
diff --git a/doc/up.png b/doc/up.png
new file mode 100644
index 0000000..a90e028
--- /dev/null
+++ b/doc/up.png
Binary files differ
diff --git a/doc/version.tex b/doc/version.tex
new file mode 100644
index 0000000..4637f72
--- /dev/null
+++ b/doc/version.tex
@@ -0,0 +1,4 @@
+% Auto-generated file; do not edit
+\date{October 25, 2006}
+\release{2.0.0}
+\setshortversion{2.0}
diff --git a/doc/zsi.css b/doc/zsi.css
new file mode 100644
index 0000000..06a613c
--- /dev/null
+++ b/doc/zsi.css
@@ -0,0 +1,243 @@
+/*
+ * The first part of this is the standard CSS generated by LaTeX2HTML,
+ * with the "empty" declarations removed.
+ */
+
+/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */
+.math { font-family: "Century Schoolbook", serif; }
+.math i { font-family: "Century Schoolbook", serif;
+ font-weight: bold }
+.boldmath { font-family: "Century Schoolbook", serif;
+ font-weight: bold }
+
+/*
+ * Implement both fixed-size and relative sizes.
+ *
+ * I think these can be safely removed, as it doesn't appear that
+ * LaTeX2HTML ever generates these, even though these are carried
+ * over from the LaTeX2HTML stylesheet.
+ */
+small.xtiny { font-size : xx-small; }
+small.tiny { font-size : x-small; }
+small.scriptsize { font-size : smaller; }
+small.footnotesize { font-size : small; }
+big.xlarge { font-size : large; }
+big.xxlarge { font-size : x-large; }
+big.huge { font-size : larger; }
+big.xhuge { font-size : xx-large; }
+
+/*
+ * Document-specific styles come next;
+ * these are added for the Python documentation.
+ *
+ * Note that the size specifications for the H* elements are because
+ * Netscape on Solaris otherwise doesn't get it right; they all end up
+ * the normal text size.
+ */
+
+body { color: #000000;
+ background-color: #ffffff; }
+
+a:link:active { color: #ff0000; }
+a:link:hover { background-color: #bbeeff; }
+a:visited:hover { background-color: #bbeeff; }
+a:visited { color: #551a8b; }
+a:link { color: #0000bb; }
+
+h1, h2, h3, h4, h5, h6 { font-family: avantgarde, sans-serif;
+ font-weight: bold; }
+h1 { font-size: 180%; }
+h2 { font-size: 150%; }
+h3, h4 { font-size: 120%; }
+
+/* These are section titles used in navigation links, so make sure we
+ * match the section header font here, even it not the weight.
+ */
+.sectref { font-family: avantgarde, sans-serif; }
+/* And the label before the titles in navigation: */
+.navlabel { font-size: 85%; }
+
+
+/* LaTeX2HTML insists on inserting <br> elements into headers which
+ * are marked with \label. This little bit of CSS magic ensures that
+ * these elements don't cause spurious whitespace to be added.
+ */
+h1>br, h2>br, h3>br,
+h4>br, h5>br, h6>br { display: none; }
+
+code, tt { font-family: "lucida typewriter", lucidatypewriter,
+ monospace; }
+var { font-family: times, serif;
+ font-style: italic;
+ font-weight: normal; }
+
+.Unix { font-variant: small-caps; }
+
+.typelabel { font-family: lucida, sans-serif; }
+
+.navigation td { background-color: #99ccff;
+ font-weight: bold;
+ font-family: avantgarde, sans-serif;
+ font-size: 110%; }
+
+div.warning { background-color: #fffaf0;
+ border: thin solid black;
+ padding: 1em;
+ margin-left: 2em;
+ margin-right: 2em; }
+
+div.warning .label { font-family: sans-serif;
+ font-size: 110%;
+ margin-right: 0.5em; }
+
+div.note { background-color: #fffaf0;
+ border: thin solid black;
+ padding: 1em;
+ margin-left: 2em;
+ margin-right: 2em; }
+
+div.note .label { margin-right: 0.5em;
+ font-family: sans-serif; }
+
+address { font-size: 80%; }
+.release-info { font-style: italic;
+ font-size: 80%; }
+
+.titlegraphic { vertical-align: top; }
+
+.verbatim pre { color: #00008b;
+ font-family: "lucida typewriter", lucidatypewriter,
+ monospace;
+ font-size: 90%; }
+.verbatim { margin-left: 2em; }
+.verbatim .footer { padding: 0.05in;
+ font-size: 85%;
+ background-color: #99ccff;
+ margin-right: 0.5in; }
+
+.grammar { background-color: #99ccff;
+ margin-right: 0.5in;
+ padding: 0.05in; }
+.grammar-footer { padding: 0.05in;
+ font-size: 85%; }
+.grammartoken { font-family: "lucida typewriter", lucidatypewriter,
+ monospace; }
+
+.productions { background-color: #bbeeff; }
+.productions a:active { color: #ff0000; }
+.productions a:link:hover { background-color: #99ccff; }
+.productions a:visited:hover { background-color: #99ccff; }
+.productions a:visited { color: #551a8b; }
+.productions a:link { color: #0000bb; }
+.productions table { vertical-align: baseline;
+ empty-cells: show; }
+.productions > table td,
+.productions > table th { padding: 2px; }
+.productions > table td:first-child,
+.productions > table td:last-child {
+ font-family: "lucida typewriter",
+ lucidatypewriter,
+ monospace;
+ }
+/* same as the second selector above, but expressed differently for Opera */
+.productions > table td:first-child + td + td {
+ font-family: "lucida typewriter",
+ lucidatypewriter,
+ monospace;
+ vertical-align: baseline;
+ }
+.productions > table td:first-child + td {
+ padding-left: 1em;
+ padding-right: 1em;
+ }
+.productions > table tr { vertical-align: baseline; }
+
+.email { font-family: avantgarde, sans-serif; }
+.mailheader { font-family: avantgarde, sans-serif; }
+.mimetype { font-family: avantgarde, sans-serif; }
+.newsgroup { font-family: avantgarde, sans-serif; }
+.url { font-family: avantgarde, sans-serif; }
+.file { font-family: avantgarde, sans-serif; }
+.guilabel { font-family: avantgarde, sans-serif; }
+
+.realtable { border-collapse: collapse;
+ border-color: black;
+ border-style: solid;
+ border-width: 0px 0px 2px 0px;
+ empty-cells: show;
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: 0.4em;
+ padding-right: 0.4em;
+ }
+.realtable tbody { vertical-align: baseline; }
+.realtable tfoot { display: table-footer-group; }
+.realtable thead { background-color: #99ccff;
+ border-width: 0px 0px 2px 1px;
+ display: table-header-group;
+ font-family: avantgarde, sans-serif;
+ font-weight: bold;
+ vertical-align: baseline;
+ }
+.realtable thead :first-child {
+ border-width: 0px 0px 2px 0px;
+ }
+.realtable thead th { border-width: 0px 0px 2px 1px }
+.realtable td,
+.realtable th { border-color: black;
+ border-style: solid;
+ border-width: 0px 0px 1px 1px;
+ padding-left: 0.4em;
+ padding-right: 0.4em;
+ }
+.realtable td:first-child,
+.realtable th:first-child {
+ border-left-width: 0px;
+ vertical-align: baseline;
+ }
+.center { text-align: center; }
+.left { text-align: left; }
+.right { text-align: right; }
+
+.refcount-info { font-style: italic; }
+.refcount-info .value { font-weight: bold;
+ color: #006600; }
+
+/*
+ * Some decoration for the "See also:" blocks, in part inspired by some of
+ * the styling on Lars Marius Garshol's XSA pages.
+ * (The blue in the navigation bars is #99CCFF.)
+ */
+.seealso { background-color: #fffaf0;
+ border: thin solid black;
+ padding: 0pt 1em 4pt 1em; }
+
+.seealso > .heading { font-size: 110%;
+ font-weight: bold; }
+
+/*
+ * Class 'availability' is used for module availability statements at
+ * the top of modules.
+ */
+.availability .platform { font-weight: bold; }
+
+
+/*
+ * Additional styles for the distutils package.
+ */
+.du-command { font-family: monospace; }
+.du-option { font-family: avantgarde, sans-serif; }
+.du-filevar { font-family: avantgarde, sans-serif;
+ font-style: italic; }
+.du-xxx:before { content: "** ";
+ font-weight: bold; }
+.du-xxx:after { content: " **";
+ font-weight: bold; }
+
+
+/*
+ * Some specialization for printed output.
+ */
+@media print {
+ .online-navigation { display: none; }
+ }
diff --git a/doc/zsi.html b/doc/zsi.html
new file mode 100644
index 0000000..9864982
--- /dev/null
+++ b/doc/zsi.html
@@ -0,0 +1,5024 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+<link rel="STYLESHEET" href="zsi.css" type='text/css' />
+<link rel="first" href="zsi.html" title='ZSI: The Zolera Soap Infrastructure
+<BR> Developer's Guide' />
+<link rel='contents' href='zsi.html' title="Contents" />
+<link rel='last' href='about.html' title='About this document...' />
+<link rel='help' href='about.html' title='About this document...' />
+<meta name='aesop' content='information' />
+<title>ZSI: The Zolera Soap Infrastructure Developer's Guide</title>
+</head>
+<body>
+<DIV CLASS="navigation">
+<div id='top-navigation-panel' xml:id='top-navigation-panel'>
+<table align="center" width="100%" cellpadding="0" cellspacing="2">
+<tr>
+<td class='online-navigation'><img src='previous.png'
+ border='0' height='32' alt='Previous Page' width='32' /></td>
+<td class='online-navigation'><img src='up.png'
+ border='0' height='32' alt='Up One Level' width='32' /></td>
+<td class='online-navigation'><img src='next.png'
+ border='0' height='32' alt='Next Page' width='32' /></td>
+<td align="center" width="100%">ZSI: The Zolera Soap Infrastructure
+<BR> Developer's Guide</td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+</tr></table>
+<div class='online-navigation'>
+</div>
+<hr /></div>
+</DIV>
+<!--End of Navigation Panel-->
+
+<P>
+
+<div class="titlepage">
+<div class='center'>
+<h1>ZSI: The Zolera Soap Infrastructure
+<BR> Developer's Guide</h1>
+<p><b><font size="+2">Rich Salz,</font></b></p>
+<p>
+<span class="email">rsalz@datapower.com
+<BR>blunck@python.org</span>
+</p>
+<p><i> Christopher Blunck</i></p>
+<p><strong>Release 2.0.0</strong><br />
+<strong>October 25, 2006</strong></p>
+<p></p>
+</div>
+</div>
+
+<P>
+<DIV CLASS="centerline" ID="par3933" ALIGN="CENTER">
+<strong>COPYRIGHT</strong></DIV>
+
+<P>
+Copyright &#169; 2001, Zolera Systems, Inc.
+<BR>All Rights Reserved.
+
+<P>
+Copyright &#169; 2002-2003, Rich Salz.
+<BR>All Rights Reserved.
+
+<P>
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or
+sell copies of the Software, and to permit persons to whom the Software
+is furnished to do so, provided that the above copyright notice(s) and
+this permission notice appear in all copies of the Software and that
+both the above copyright notice(s) and this permission notice appear in
+supporting documentation.
+
+<P>
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THIS SOFTWARE.
+
+<P>
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+<P>
+<BR>
+<BR>
+<BR>
+<BR>
+
+<P>
+<DIV CLASS="centerline" ID="par3934" ALIGN="CENTER">
+<strong>Acknowledgments</strong></DIV>
+
+<P>
+We are grateful to the members of the <code>soapbuilders</code>
+mailing list (see <a class="url" href="http://groups.yahoo.com/soapbuilders">http://groups.yahoo.com/soapbuilders</a>),
+Fredrik Lundh for his <code>soaplib</code> package (see
+<a class="url" href="http://www.secretlabs.com/downloads/index.htm#soap">http://www.secretlabs.com/downloads/index.htm#soap</a>),
+Cayce Ullman and Brian Matthews for their <code>SOAP.py</code> package
+(see <a class="url" href="http://sourceforge.net/projects/pywebsvcs">http://sourceforge.net/projects/pywebsvcs</a>).
+
+<P>
+We are particularly grateful to Brian Lloyd and the Zope Corporation
+(<a class="url" href="http://www.zope.com">http://www.zope.com</a>) for letting us incorporate his ZOPE
+WebServices package and documentation into <tt class="module">ZSI</tt>.
+
+<H3>Abstract:</H3>
+<DIV CLASS="ABSTRACT">
+
+<tt class="module">ZSI</tt>, the Zolera SOAP Infrastructure, is a Python package that
+provides an implementation of SOAP messaging, as described in
+<em class="citetitle"><a
+ href="http://www.w3.org/TR/soap"
+ title="The SOAP 1.1 Specification"
+ >The SOAP 1.1 Specification</a></em>.
+In particular, <tt class="module">ZSI</tt> parses and generates SOAP messages, and
+converts between native Python datatypes and SOAP syntax.
+It can also be used to build applications using
+<em class="citetitle"><a
+ href="http://www.w3.org/TR/SOAP-attachments"
+ title="SOAP Messages with
+Attachments"
+ >SOAP Messages with
+Attachments</a></em>.
+<tt class="module">ZSI</tt> is ``transport neutral'', and provides only a simple
+I/O and dispatch framework; a more complete solution is the
+responsibility of the application using <tt class="module">ZSI</tt>.
+As usage patterns emerge, and common application frameworks are
+more understood, this may change.
+
+<P>
+<tt class="module">ZSI</tt> requires Python 2.3 or later and PyXML version 0.8.3 or later.
+
+<P>
+The <tt class="module">ZSI</tt> homepage is at <a class="url" href="http://pywebsvcs.sf.net/">http://pywebsvcs.sf.net/</a>.
+
+<P>
+</DIV>
+<P>
+
+<P>
+<BR><h2><A NAME="SECTION001000000000000000000">
+Contents</A>
+</h2>
+<!--Table of Contents-->
+
+<UL CLASS="TofC">
+<LI><A href="zsi.html#SECTION002000000000000000000">1. Introduction</a>
+<UL>
+<LI><A href="zsi.html#SECTION002100000000000000000">1.1 How to Read this Document</a>
+</ul>
+<LI><A href="zsi.html#SECTION003000000000000000000">2. Examples</a>
+<UL>
+<LI><A href="zsi.html#SECTION003100000000000000000">2.1 Server Side Examples</a>
+<UL>
+<LI><A href="zsi.html#SECTION003110000000000000000">2.1.1 Simple example</a>
+<LI><A href="zsi.html#SECTION003120000000000000000">2.1.2 low level soap processing example</a>
+<LI><A href="zsi.html#SECTION003130000000000000000">2.1.3 A mod_python example</a>
+</ul>
+<LI><A href="zsi.html#SECTION003200000000000000000">2.2 Client Side Examples</a>
+<UL>
+<LI><A href="zsi.html#SECTION003210000000000000000">2.2.1 Simple Example</a>
+<LI><A href="zsi.html#SECTION003220000000000000000">2.2.2 Complex Example: pickler.py</a>
+</ul>
+</ul>
+<LI><A href="zsi.html#SECTION004000000000000000000">3. Exceptions</a>
+<LI><A href="zsi.html#SECTION005000000000000000000">4. Utilities</a>
+<UL>
+<LI><A href="zsi.html#SECTION005100000000000000000">4.1 Low-Level Utilities</a>
+</ul>
+<LI><A href="zsi.html#SECTION006000000000000000000">5. The ParsedSoap module -- basic message handling</a>
+<LI><A href="zsi.html#SECTION007000000000000000000">6. The TypeCode classes -- data conversions</a>
+<UL>
+<LI><A href="zsi.html#SECTION007100000000000000000">6.1 TC.TypeCode</a>
+<LI><A href="zsi.html#SECTION007200000000000000000">6.2 TC.Any -- the basis of dynamic typing</a>
+<UL>
+<LI><A href="zsi.html#SECTION007210000000000000000">6.2.1 simple data</a>
+<LI><A href="zsi.html#SECTION007220000000000000000">6.2.2 compound data</a>
+<LI><A href="zsi.html#SECTION007230000000000000000">6.2.3 class description</a>
+<LI><A href="zsi.html#SECTION007240000000000000000">6.2.4 Adding new types</a>
+</ul>
+<LI><A href="zsi.html#SECTION007300000000000000000">6.3 TC.SimpleType</a>
+<LI><A href="zsi.html#SECTION007400000000000000000">6.4 Strings</a>
+<LI><A href="zsi.html#SECTION007500000000000000000">6.5 Integers</a>
+<LI><A href="zsi.html#SECTION007600000000000000000">6.6 Floating-point Numbers</a>
+<LI><A href="zsi.html#SECTION007700000000000000000">6.7 Dates and Times</a>
+<LI><A href="zsi.html#SECTION007800000000000000000">6.8 Boolean</a>
+<LI><A href="zsi.html#SECTION007900000000000000000">6.9 XML</a>
+<LI><A href="zsi.html#SECTION0071000000000000000000">6.10 ComplexType</a>
+<LI><A href="zsi.html#SECTION0071100000000000000000">6.11 Struct</a>
+<LI><A href="zsi.html#SECTION0071200000000000000000">6.12 Arrays</a>
+<LI><A href="zsi.html#SECTION0071300000000000000000">6.13 Apache Datatype</a>
+</ul>
+<LI><A href="zsi.html#SECTION008000000000000000000">7. The SoapWriter module -- serializing data</a>
+<LI><A href="zsi.html#SECTION009000000000000000000">8. The Fault module -- reporting errors</a>
+<LI><A href="zsi.html#SECTION0010000000000000000000">9. The resolvers module -- fetching remote data</a>
+<LI><A href="zsi.html#SECTION0011000000000000000000">10. Dispatching and Invoking</a>
+<UL>
+<LI><A href="zsi.html#SECTION0011100000000000000000">10.1 Dispatching</a>
+<UL>
+<LI><A href="zsi.html#SECTION0011110000000000000000">10.1.1 Dispatch Behaviors</a>
+<LI><A href="zsi.html#SECTION0011120000000000000000">10.1.2 Special Modules</a>
+<LI><A href="zsi.html#SECTION0011130000000000000000">10.1.3 Dispatch Mechanisms</a>
+<LI><A href="zsi.html#SECTION0011140000000000000000">10.1.4 Other Dispatch Stuff</a>
+</ul>
+<LI><A href="zsi.html#SECTION0011200000000000000000">10.2 The client module -- sending SOAP messages</a>
+<UL>
+<LI><A href="zsi.html#SECTION0011210000000000000000">10.2.1 _Binding</a>
+<LI><A href="zsi.html#SECTION0011220000000000000000">10.2.2 Binding</a>
+<LI><A href="zsi.html#SECTION0011230000000000000000">10.2.3 NamedParamBinding</a>
+</ul>
+</ul>
+<LI><A href="zsi.html#SECTION0012000000000000000000">11. Bibliography</a>
+<LI><A href="zsi.html#SECTION0013000000000000000000">Bibliography</a>
+<LI><A href="zsi.html#SECTION0014000000000000000000">A. CGI Script Array</a>
+<UL>
+<LI><A href="zsi.html#SECTION0014100000000000000000">A.1 Intro</a>
+<UL>
+<LI><A href="zsi.html#SECTION0014110000000000000000">A.1.1 rpc wrapper</a>
+</ul>
+<LI><A href="zsi.html#SECTION0014200000000000000000">A.2 CGI Script</a>
+<LI><A href="zsi.html#SECTION0014300000000000000000">A.3 client test script</a>
+<LI><A href="zsi.html#SECTION0014400000000000000000">A.4 SOAP Trace</a>
+<UL>
+<LI><A href="zsi.html#SECTION0014410000000000000000">A.4.1 hello</a>
+<LI><A href="zsi.html#SECTION0014420000000000000000">A.4.2 hello fault</a>
+<LI><A href="zsi.html#SECTION0014430000000000000000">A.4.3 echo</a>
+<LI><A href="zsi.html#SECTION0014440000000000000000">A.4.4 sum</a>
+<LI><A href="zsi.html#SECTION0014450000000000000000">A.4.5 average</a>
+</ul>
+</ul>
+<LI><A href="zsi.html#SECTION0015000000000000000000">B. CGI Script Struct</a>
+<UL>
+<LI><A href="zsi.html#SECTION0015100000000000000000">B.1 Intro</a>
+<UL>
+<LI><A href="zsi.html#SECTION0015110000000000000000">B.1.1 rpc wrapper</a>
+</ul>
+<LI><A href="zsi.html#SECTION0015200000000000000000">B.2 CGI Script</a>
+<LI><A href="zsi.html#SECTION0015300000000000000000">B.3 client test script</a>
+<LI><A href="zsi.html#SECTION0015400000000000000000">B.4 SOAP Trace</a>
+<UL>
+<LI><A href="zsi.html#SECTION0015410000000000000000">B.4.1 hello</a>
+</ul>
+</ul>
+<LI><A href="zsi.html#SECTION0016000000000000000000">C. Complete Low Level Example</a>
+<UL>
+<LI><A href="zsi.html#SECTION0016100000000000000000">C.1 Intro</a>
+<LI><A href="zsi.html#SECTION0016200000000000000000">C.2 code</a>
+<UL>
+<LI><A href="zsi.html#SECTION0016210000000000000000">C.2.1 httpserver script</a>
+<LI><A href="zsi.html#SECTION0016220000000000000000">C.2.2 typecode module</a>
+<LI><A href="zsi.html#SECTION0016230000000000000000">C.2.3 player script</a>
+<LI><A href="zsi.html#SECTION0016240000000000000000">C.2.4 client test script</a>
+</ul>
+<LI><A href="zsi.html#SECTION0016300000000000000000">C.3 SOAP Trace</a>
+<UL>
+<LI><A href="zsi.html#SECTION0016310000000000000000">C.3.1 GetAverage</a>
+<LI><A href="zsi.html#SECTION0016320000000000000000">C.3.2 fault</a>
+</ul>
+</ul>
+<LI><A href="zsi.html#SECTION0017000000000000000000">D. pickler example</a>
+<UL>
+<LI><A href="zsi.html#SECTION0017100000000000000000">D.1 Intro</a>
+<LI><A href="zsi.html#SECTION0017200000000000000000">D.2 code</a>
+<UL>
+<LI><A href="zsi.html#SECTION0017210000000000000000">D.2.1 typecode module</a>
+<LI><A href="zsi.html#SECTION0017220000000000000000">D.2.2 pickler script</a>
+<LI><A href="zsi.html#SECTION0017230000000000000000">D.2.3 client: invoke savePerson</a>
+<LI><A href="zsi.html#SECTION0017240000000000000000">D.2.4 client: invoke getPerson 3 different ways</a>
+</ul></ul></ul>
+<!--End of Table of Contents-->
+
+<H1><A NAME="SECTION002000000000000000000">
+1. Introduction</A>
+</H1>
+
+<P>
+<tt class="module">ZSI</tt>, the Zolera SOAP Infrastructure, is a Python package that
+provides an implementation of the SOAP specification, as described in
+<em class="citetitle"><a
+ href="http://www.w3.org/TR/soap"
+ title="The SOAP 1.1 Specification"
+ >The SOAP 1.1 Specification</a></em>.
+In particular, <tt class="module">ZSI</tt> parses and generates SOAP messages, and
+converts between native Python datatypes and SOAP syntax.
+
+<P>
+<tt class="module">ZSI</tt> requires Python 2.3 or later and PyXML version 0.8.3 or later.
+
+<P>
+The <tt class="module">ZSI</tt> project is maintained at SourceForge, at
+<a class="url" href="http://pywebsvcs.sf.net">http://pywebsvcs.sf.net</a>.
+<tt class="module">ZSI</tt> is discussed on the Python web services mailing list, visit
+<a class="url" href="http://lists.sourceforge.net/lists/listinfo/pywebsvcs-talk">http://lists.sourceforge.net/lists/listinfo/pywebsvcs-talk</a>to subscribe.
+
+<P>
+For those interested in using the <b class="program">wsdl2py</b> tool see the <em>Users
+Guide</em>, it contains a detailed example of how to use the code generation
+facilities in <tt class="module">ZSI</tt>.
+
+<P>
+For those interested in a high-level tutorial covering <tt class="module">ZSI</tt> and why
+Python was chosen, see the article
+<a class="url" href="http://www.xml.com/pub/a/ws/2002/06/12/soap.html">http://www.xml.com/pub/a/ws/2002/06/12/soap.html</a>,
+written by Rich Salz for xml.com.
+
+<P>
+SOAP-based processing typically involves several steps.
+The following list details the steps of a common processing model naturally
+supported by <tt class="module">ZSI</tt> (other models are certainly possible):
+
+<OL>
+<LI><tt class="module">ZSI</tt> takes data from an input stream and <em>parses</em> it, generating
+ a DOM-based parse tree as part of creating a <tt class="class">ParsedSoap</tt> object.
+ At this point the major syntactic elements of a SOAP message -- the
+ <code>Header</code>, the <code>Body</code>, etc. -- are available.
+</LI>
+<LI>The application does <em>header processing</em>.
+ More specifically, it does local dispatch and processing based on
+ the elements in the SOAP <code>Header</code>.
+ The SOAP <code>actor</code> and <code>mustUnderstand</code> attributes are
+ also handled (or at least recognized) here.
+</LI>
+<LI><tt class="module">ZSI</tt> next <em>parses</em> the <code>Body</code>, creating local Python objects
+ from the data in the SOAP message.
+ The parsing is often under the control of a list of data descriptions,
+ known as <em>typecodes</em>, defined by the application because it knows
+ what type of data it is expecting.
+ In cases where the SOAP data is known to be completely self-describing,
+ the parsing can be <em>dynamic</em> through the use of the <tt class="class">TC.Any</tt>
+ class.
+</LI>
+<LI>The application now <em>dispatches</em> to the appropriate handler
+ in order to do its ``real work.''
+ As part of its processing it may create <em>output objects</em>
+</LI>
+<LI>The application creates a <tt class="class">SoapWriter</tt> instance and outputs
+ an initial set of namespace entries and header elements.
+</LI>
+<LI>Any local data to be sent back to the client is <em>serialized</em>.
+ As with <code>Body</code> parsing, the datatypes can be described through
+ typecodes or determined dynamically (here, through introspection).
+</LI>
+<LI>In the event of any processing exceptions, a <tt class="class">Fault</tt> object
+ can be raised, created, and/or serialized.
+</LI>
+</OL>
+
+<P>
+Note that <tt class="module">ZSI</tt> is ``transport neutral'', and provides only a simple
+I/O and dispatch framework; a more complete solution is available through
+the use of included WSDL tools (<b class="program">wsdl2py</b>), but otherwise this is
+the responsibility of the application using <tt class="module">ZSI</tt>. As usage patterns
+emerge, and common application frameworks are more understood, this may
+change.
+
+<P>
+Within this document, <code>tns</code> is used as the prefix for the
+application's target namespace, and the term
+<em>element</em> refers to a DOM element node.)
+
+<P>
+
+<H1><A NAME="SECTION002100000000000000000">
+1.1 How to Read this Document</A>
+</H1>
+
+<P>
+Readers interested in using WSDL and clients to web services, and those
+intending on implementing web services based on existing WSDL should refer
+to the <em>Users Guide</em>. Others interested in developing the simplest SOAP
+applications, or spending the least amount of time on building a web services
+infrastructure should read chapters 2, 3, and 10 of this document. Readers who
+are developing complex services, and who are familiar with XML Schema and/or
+WSDL, should read this manual in order. This will provide them with enough
+information to implement the processing model described above. They can skip
+probably skip chapters 2 and 10.
+
+<P>
+<tt class="module">ZSI</tt> has the capability to process WSDL definitions and XML Schema documents
+(described in <em class="citetitle"><a
+ href="http://www.w3.org/TR/wsdl"
+ title="The Web Services Description
+Language"
+ >The Web Services Description
+Language</a></em> and <em class="citetitle"><a
+ href="http://www.w3.org/XML/Schema"
+ title="XMLSchema 1.0"
+ >XMLSchema 1.0</a></em>)
+and generate typecodes automatically. For more information see the <em>Users
+Guide</em>.
+
+<P>
+
+<H1><A NAME="SECTION003000000000000000000">
+2. Examples</A>
+</H1>
+
+<P>
+This chapter contains a number of examples to show off some of <tt class="module">ZSI</tt>'s
+features. It is broken down into client-side and server-side examples, and
+explores different implementation options <tt class="module">ZSI</tt> provides.
+
+<P>
+
+<H1><A NAME="SECTION003100000000000000000">
+2.1 Server Side Examples</A>
+</H1>
+
+<H2><A NAME="SECTION003110000000000000000">
+2.1.1 Simple example</A>
+</H2>
+Using the <tt class="module">ZSI.dispatch</tt> module, it is simple to expose Python functions
+as web services. Each function is invoked with all the input parameters
+specified in the client's SOAP request. Any value returned by the function will
+be serialized back to the client; multiple values can be returned by returning a
+tuple.
+
+<P>
+The following code shows some simple services:
+
+<P>
+<div class="verbatim"><pre>
+#!/usr/local/bin/python2.4
+# SOAP Array
+
+def hello():
+ return ["Hello, world"]
+
+def echo(*args):
+ return args
+
+def sum(*args):
+ sum = 0
+ for i in args: sum += i
+ return [sum]
+
+def average(*args):
+ return [sum(*args) / len(args)]
+
+from ZSI import dispatch
+dispatch.AsCGI(rpc=True)
+</pre></div>
+
+<P>
+Each function defines a SOAP request, so if this script is installed
+as a CGI script, a SOAP message can be posted to that script's URL with any of
+<code>hello</code>, <code>echo</code>, or <code>average</code> as the request element,
+and the value returned by the function will be sent back. These functions
+expect and return SOAP-ENC:arrayType instances which are marshalled into python
+<tt class="class">list</tt> instances, this script interoperates with the
+<tt class="class">client.Binding</tt>. For more information see <em>Appendix A</em>.
+
+<P>
+The <tt class="module">ZSI</tt> CGI dispatcher catches exceptions and sends back a SOAP fault.
+For example, a fault will be sent if the <code>hello</code> function is given any
+arguments, or if the <code>average</code> function is given a non-integer.
+
+<P>
+Here is another example but using SOAP-ENC:Struct instances which are marshalled
+into python <tt class="class">dict</tt> instances, this script interoperates with the
+<tt class="class">client.NamedParamBinding</tt>. For more information see <em>Appendix B</em>.
+
+<P>
+<div class="verbatim"><pre>
+#!/usr/local/bin/python2.4
+# SOAP Struct
+
+def hello():
+ return {"value":"Hello, world"}
+
+def echo(**kw):
+ return kw
+
+def sum(**kw):
+ sum = 0
+ for i in kw.values(): sum += i
+ return {"value":sum}
+
+def average(**kw):
+ d = sum(**kw)
+ return d["value"] = d["value"]/len(kw)
+
+from ZSI import dispatch
+dispatch.AsCGI(rpc=True)
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION003120000000000000000">
+2.1.2 low level soap processing example</A>
+</H2>
+
+<P>
+We will now show a more complete example of a robust web service implemented at
+the SOAP layer. It takes as input a player name and array of integers, and returns
+the average. It is presented in sections, following the steps detailed above.
+A complete working example of this service is available in <em>Appendix C</em>.
+
+<P>
+The first section reads in a request, and parses the SOAP header.
+
+<P>
+<div class="verbatim"><pre>
+from ZSI import *
+import sys
+IN, OUT = sys.stdin, sys.stdout
+try:
+ ps = ParsedSoap(IN)
+except ParseException, e:
+ OUT.write(FaultFromZSIException(e).AsSOAP())
+ sys.exit(1)
+except Exception, e:
+ # Faulted while processing; we assume it's in the header.
+ OUT.write(FaultFromException(e, 1).AsSOAP())
+ sys.exit(1)
+
+# We are not prepared to handle any actors or mustUnderstand elements,
+# so we'll arbitrarily fault back with the first one we found.
+a = ps.WhatActorsArePresent()
+if len(a):
+ OUT.write(FaultFromActor(a[0]).AsSOAP())
+ sys.exit(1)
+mu = ps.WhatMustIUnderstand()
+if len(mu):
+ uri, localname = mu[0]
+ OUT.write(FaultFromNotUnderstood(uri, localname).AsSOAP())
+ sys.exit(1)
+</pre></div>
+
+<P>
+This section defines the mappings between Python objects and the SOAP
+data being transmitted. Recall that according to the SOAP specification, RPC
+input and output are modeled as a structure.
+
+<P>
+<div class="verbatim"><pre>
+class Player:
+ def __init__(self, *args):
+ if not len(args): return
+ self.Name = args[0]
+ self.Scores = args[1:]
+Player.typecode = TC.Struct(Player, [
+ TC.String('Name'),
+ TC.Array('Integer', TC.Integer(), 'Scores', undeclared=True),
+ ], 'GetAverage')
+class Average:
+ def __init__(self, average=None):
+ self.average = average
+Average.typecode = TC.Struct(Average, [
+ TC.Integer('average'),
+ ], 'GetAverageResponse')
+</pre></div>
+
+<P>
+This section parses the input, performs the application-level
+activity, and serializes the response.
+<div class="verbatim"><pre>
+try:
+ player = ps.Parse(Player.typecode)
+except EvaluateException, e:
+ OUT.write(FaultFromZSIException(e).AsSOAP())
+ sys.exit(1)
+
+try:
+ total = 0
+ for value in player.Scores: total = total + value
+ result = Average(total / len(player.Scores))
+ sw = SoapWriter()
+ sw.serialize(result, Average.typecode)
+ sw.close()
+ OUT.write(str(sw))
+except Exception, e:
+ OUT.write(FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP())
+ sys.exit(1)
+</pre></div>
+
+<P>
+In the <tt class="method">serialize()</tt> call above, the second parameter is optional, since
+<code>result</code> is an instance of the <tt class="class">Average</tt> class, and the
+<code>Average.typecode</code> attribute is the typecode for class instances.
+
+<P>
+
+<H2><A NAME="SECTION003130000000000000000">
+2.1.3 A mod_python example</A>
+</H2>
+
+<P>
+The Apache module <code>mod_python</code> (see
+<a class="url" href="http://www.modpython.org">http://www.modpython.org</a>) embeds Python within the Apache server.
+In order to expose operations within a module via mod_python, use the
+<tt class="method">dispatch.AsHandler()</tt> function. The <tt class="method">dispatch.AsHandler()</tt>
+function will dispatch requests to any operation defined in the module you
+pass it, which allows for multiple operations to be defined in a module.
+The only trick is to use __import__ to load the XML encodings your service
+expects. This is a required workaround to avoid the pitfalls of restricted
+execution with respect to XML parsing.
+
+<P>
+The following is a complete example of a simple handler. The soap operations
+are implemented in the MyHandler module:
+
+<P>
+<div class="verbatim"><pre>
+def hello():
+ return {"value":"Hello, world"}
+
+def echo(**kw):
+ return kw
+
+def sum(**kw):
+ sum = 0
+ for i in kw.values(): sum += i
+ return {"value":sum}
+
+def average(**kw):
+ d = sum(**kw)
+ d["value"] = d["value"]/len(kw)
+ return d
+</pre></div>
+
+<P>
+Dispatching from within mod_python is achieved by passing the aforementined
+MyHandler module to <code>dispatch.AsHandler()</code>. The following code exposes
+the operations defined in MyHandler via SOAP:
+
+<P>
+<div class="verbatim"><pre>
+from ZSI import dispatch
+from mod_python import apache
+
+import MyHandler
+mod = __import__('encodings.utf_8', globals(), locals(), '*')
+mod = __import__('encodings.utf_16_be', globals(), locals(), '*')
+
+def handler(req):
+ dispatch.AsHandler(modules=(MyHandler,), request=req)
+ return apache.OK
+</pre></div>
+
+<P>
+
+<H1><A NAME="SECTION003200000000000000000">
+2.2 Client Side Examples</A>
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION003210000000000000000">
+2.2.1 Simple Example</A>
+</H2>
+<tt class="module">ZSI</tt> provides two ways for a client to interactive with a server:
+the <tt class="class">Binding</tt> or <tt class="class">NamedParamBinding</tt> class and the
+<tt class="class">ServiceProxy</tt> class. The first is useful when the operations to be
+invoked are not defined in WSDL or when only simple Python datatypes are used;
+the <tt class="class">ServiceProxy</tt> class can be used to parse WSDL definitions in order
+to determine how to serialize and parse the SOAP messages.
+
+<P>
+During development, it is often useful to record ``packet traces'' of
+the SOAP messages being exchanged. Both the <tt class="class">Binding</tt> and
+<tt class="class">ServiceProxy</tt> classes provide a <code>tracefile</code> parameter to specify an
+output stream (such as a file) to capture messages. It can be particularly
+useful when debugging unexpected SOAP faults.
+
+<P>
+The first example provided below demonstrates how to use the <tt class="class">NamedParamBinding</tt>
+class to connect to a remote service and perform an operation.
+
+<P>
+<div class="verbatim"><pre>
+#!/usr/bin/env python
+import sys,time
+from ZSI.client import NamedParamBinding as NPBinding
+
+b = NPBinding(url='http://127.0.0.1/cgi-bin/soapstruct', tracefile=sys.stdout)
+print "Hello: ", b.hello()
+print "Echo: ", b.echo(name="josh", year=2006, pi=3.14, time=time.gmtime())
+print "Sum: ", b.sum(one=1, two=2, three=3)
+print "Average: ", b.average(one=100, two=200, three=300, four=400)
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION003220000000000000000">
+2.2.2 Complex Example: pickler.py</A>
+</H2>
+If the operation invoked returns a ComplexType, typecode information must
+be provided in order to tell <tt class="module">ZSI</tt> how to deserialize the response.
+Here is a sample server-side implementation (for the complete example see
+<em>Appendix D</em>):
+
+<P>
+<div class="verbatim"><pre>
+class Person:
+ def __init__(self, name=None, age=0):
+ self.name = name
+ self.age = age
+
+Person.typecode = TC.Struct(Person,
+ [TC.String('name'),
+ TC.InonNegativeInteger('age')],
+ 'myApp:Person')
+
+# my web service that returns a complex structure
+def getPerson(name):
+ fp = open('%s.person.pickle', % name, 'r')
+ return pickle.load(fp)
+
+# my web service that accepts a complex structure
+def savePerson(person):
+ fp = open('%s.person.pickle' % person.name, 'w')
+ pickle(person, fp)
+ fp.close()
+</pre></div>
+
+<P>
+In order for <tt class="module">ZSI</tt> to transparently deserialize the returned complex type into
+a <tt class="class">Person</tt> instance, a module defining the class and its typecode can be
+passed into the <tt class="class">Binding</tt>. It is also possible to explicitly tell <tt class="module">ZSI</tt>
+what typecode to use by passing it as a parameter to the <tt class="method">Binding.Receive()</tt>
+method.
+
+<P>
+The following fragment shows both styles:
+
+<P>
+<div class="verbatim"><pre>
+import sys
+from ZSI.client import Binding
+from MyComplexTypes import Person
+
+b = Binding(url='http://localhost/test3/pickler.py', tracefile=sys.stdout)
+person = Person('christopher', 26)
+rsp = b.savePerson(person)
+</pre></div>
+
+<P>
+Because the returned complex type is defined in a class present in
+<var>typesmodule</var>, transparent deserialization is possible. When sending
+complex types to the server, it is not necessary to list the module
+in <var>typesmodule</var>:
+
+<P>
+<div class="verbatim"><pre>
+import sys
+import MyComplexTypes
+from ZSI.client import NamedParamBinding as NPBinding, Binding
+from ZSI import TC
+
+kw = {'url':'http://localhost/test3/pickler.py', 'tracefile':sys.stdout}
+b = NPBinding(**kw)
+rsp = b.getPerson(name='christopher')
+assert type(rsp) is dict, 'expecting a dict'
+assert rsp['Person']['name'] == 'christopher', 'wrong person'
+
+b = NPBinding(typesmodule=MyComplexTypes, **kw)
+rsp = b.getPerson(name='christopher')
+assert isinstance(rsp['Person'], MyComplexTypes.Person), (
+ 'expecting instance of %s' %MyComplexTypes.Person)
+
+b = Binding(typesmodule=MyComplexTypes, **kw)
+class Name(str):
+ typecode = TC.String("name")
+
+rsp = b.getPerson(Name('christopher'))
+assert isinstance(rsp['Person'], MyComplexTypes.Person), (
+ 'expecting instance of %s' %MyComplexTypes.Person)
+</pre></div>
+
+<P>
+
+<H1><A NAME="SECTION004000000000000000000">
+3. Exceptions</A>
+</H1>
+
+<P>
+<dl><dt><b><span class="typelabel">exception</span>&nbsp;<tt id='l2h-1' xml:id='l2h-1' class="exception">ZSIException</tt></b></dt>
+<dd>
+Base class for all ZSI Exceptions, it is a subtype of the Python
+<tt class="exception">Exception</tt> class.
+</dd></dl>
+
+<P>
+<dl><dt><b><span class="typelabel">exception</span>&nbsp;<tt id='l2h-2' xml:id='l2h-2' class="exception">ParseException</tt></b></dt>
+<dd>
+<tt class="module">ZSI</tt> can raise this exception while creating a <tt class="class">ParsedSoap</tt> object.
+It is a subtype of the <tt class="exception">ZSIException</tt> class.
+The string form of a <tt class="exception">ParseException</tt> object consists of a
+line of human-readable text.
+If the backtrace is available, it will be concatenated as a second line.
+</dd></dl>
+
+<P>
+The following attributes are read-only:
+
+<P>
+<dl><dt><b><tt id='l2h-3' xml:id='l2h-3' class="member">inheader</tt></b></dt>
+<dd>
+A boolean that indicates if the error was detected in the SOAP <code>Header</code>
+element.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-4' xml:id='l2h-4' class="member">str</tt></b></dt>
+<dd>
+A text string describing the error.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-5' xml:id='l2h-5' class="member">trace</tt></b></dt>
+<dd>
+A text string containing a backtrace to the error.
+This may be <code>None</code> if it was not possible, such as when there was
+a general DOM exception, or when the <code>str</code> text is believed to be
+sufficient.
+</dl>
+
+<P>
+<dl><dt><b><span class="typelabel">exception</span>&nbsp;<tt id='l2h-6' xml:id='l2h-6' class="exception">EvaluateException</tt></b></dt>
+<dd>
+This exception is similar to <tt class="exception">ParseException</tt>, except
+that <tt class="module">ZSI</tt> may raise it while converting between SOAP and local
+Python objects.
+</dd></dl>
+
+<P>
+The following attributes are read-only:
+
+<P>
+<dl><dt><b><tt id='l2h-7' xml:id='l2h-7' class="member">str</tt></b></dt>
+<dd>
+A text string describing the error.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-8' xml:id='l2h-8' class="member">trace</tt></b></dt>
+<dd>
+A text backtrace, as described above for <tt class="exception">ParseException</tt>.
+</dl>
+
+<H1><A NAME="SECTION005000000000000000000">
+4. Utilities</A>
+</H1>
+
+<P>
+<tt class="module">ZSI</tt> defines some utility methods that general applications
+may want to use.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-9' xml:id='l2h-9' class="function">Version</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns a three-element tuple containing the numbers representing the
+major, minor, and release identifying the <tt class="module">ZSI</tt> version.
+
+<span class="versionnote">New in version 1.1.</span>
+
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION005100000000000000000">
+4.1 Low-Level Utilities</A>
+</H1>
+
+<P>
+<tt class="module">ZSI</tt> also defines some low-level utilities for its own use that
+start with a leading underscore and must be imported explicitly.
+They are documented here because they can be useful for developing
+new typecode classes.
+
+<P>
+These functions are mostly used in in <code>parse</code> methods and the
+<tt class="class">ParsedSoap</tt> class. The serialization routines use the
+<tt class="class">ElementProxy</tt> class to encapsulate common DOM-level operations.
+
+<P>
+Some <code>lambda</code>'s are defined so that some DOM accessors
+will return an empty list rather than <code>None</code>.
+This means that rather than writing:
+<div class="verbatim"><pre>
+if elt.childNodes:
+ for N in elt.childNodes:
+ ...
+</pre></div>
+One can write:
+<div class="verbatim"><pre>
+for N in _children(elt):
+ ...
+</pre></div>
+
+<P>
+Other <code>lambda</code>'s return SOAP-related attributes from an element,
+or <code>None</code> if not present.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-10' xml:id='l2h-10' class="function">_attrs</tt></b>(</nobr></td>
+ <td><var>element</var>)</td></tr></table></dt>
+<dd>
+Returns a list of all attributes of the specified <code>element</code>.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-11' xml:id='l2h-11' class="function">_backtrace</tt></b>(</nobr></td>
+ <td><var>elt, dom</var>)</td></tr></table></dt>
+<dd>
+This function returns a text string that traces a ``path'' from <code>dom</code>,
+a DOM root, to <code>elt</code>, an element within that document, in
+XPath syntax.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-12' xml:id='l2h-12' class="function">_child_elements</tt></b>(</nobr></td>
+ <td><var>element</var>)</td></tr></table></dt>
+<dd>
+Returns a list of all children elements of the specified <code>element</code>.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-13' xml:id='l2h-13' class="function">_children</tt></b>(</nobr></td>
+ <td><var>element</var>)</td></tr></table></dt>
+<dd>
+Returns a list of all children of the specified <code>element</code>.
+</dl>
+
+<P>
+_copyright
+_empty_nsuri_list
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-14' xml:id='l2h-14' class="function">_find_arraytype</tt></b>(</nobr></td>
+ <td><var>element</var>)</td></tr></table></dt>
+<dd>
+The value of the SOAP <code>arrayType</code> attribute.
+
+<span class="versionnote">New in version 1.2.</span>
+
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-15' xml:id='l2h-15' class="function">_find_attr</tt></b>(</nobr></td>
+ <td><var>element, name</var>)</td></tr></table></dt>
+<dd>
+The value of the unqualified <code>name</code> attribute.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-16' xml:id='l2h-16' class="function">_find_attrNS</tt></b>(</nobr></td>
+ <td><var>element, namespaceURI, localName</var>)</td></tr></table></dt>
+<dd>
+The value of a <code>name</code> attribute in a namespace <code>namespaceURI</code>.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-17' xml:id='l2h-17' class="function">_find_attrNodeNS</tt></b>(</nobr></td>
+ <td><var>element, namespaceURI, localName</var>)</td></tr></table></dt>
+<dd>
+Works just like <code>_find_attrNS</code>, but this function grabs the attribute Node to
+distinquish between an unspecified attribute(None) and one set to empty
+string("").
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-18' xml:id='l2h-18' class="function">_find_default_namespace</tt></b>(</nobr></td>
+ <td><var>element</var>)</td></tr></table></dt>
+<dd>
+Returns the value of the default namespace.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-19' xml:id='l2h-19' class="function">_find_encstyle</tt></b>(</nobr></td>
+ <td><var>element</var>)</td></tr></table></dt>
+<dd>
+The value of the SOAP <code>encodingStyle</code> attribute.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-20' xml:id='l2h-20' class="function">_find_href</tt></b>(</nobr></td>
+ <td><var>element</var>)</td></tr></table></dt>
+<dd>
+The value of the unqualified <code>href</code> attribute.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-21' xml:id='l2h-21' class="function">_find_type</tt></b>(</nobr></td>
+ <td><var>element</var>)</td></tr></table></dt>
+<dd>
+The value of the XML Schema <code>type</code> attribute.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-22' xml:id='l2h-22' class="function">_find_xmlns_prefix</tt></b>(</nobr></td>
+ <td><var>element, prefix</var>)</td></tr></table></dt>
+<dd>
+The value of the xmlns:prefix <code>type</code> attribute.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-23' xml:id='l2h-23' class="function">_find_xsi_attr</tt></b>(</nobr></td>
+ <td><var>element, attribute</var>)</td></tr></table></dt>
+<dd>
+Find the attribute in any of the XMLSchema namespaces.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-24' xml:id='l2h-24' class="function">_get_element_nsuri_name</tt></b>(</nobr></td>
+ <td><var>element</var>)</td></tr></table></dt>
+<dd>
+Returns a <code>(namespace,name)</code> tuple representing the element tag.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-25' xml:id='l2h-25' class="function">_get_idstr</tt></b>(</nobr></td>
+ <td><var>obj</var>)</td></tr></table></dt>
+<dd>
+Substitute for <tt class="function">id</tt> function. Python 2.3.x generates a <tt class="class">FutureWarning</tt> for negative
+IDs, so we use a different prefix character to ensure uniqueness, and call abs()
+to avoid the warning.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-26' xml:id='l2h-26' class="function">_get_postvalue_from_absoluteURI</tt></b>(</nobr></td>
+ <td><var>url</var>)</td></tr></table></dt>
+<dd>
+Returns POST value from <code>url</code>, and caches these values.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-27' xml:id='l2h-27' class="function">_resolve_prefix</tt></b>(</nobr></td>
+ <td><var>element, prefix</var>)</td></tr></table></dt>
+<dd>
+ resolve <code>prefix</code> to a namespaceURI. If <code>None</code> or empty <code>str</code>,
+ return default namespace or <code>None</code> if not defined.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-28' xml:id='l2h-28' class="function">_valid_encoding</tt></b>(</nobr></td>
+ <td><var>elt</var>)</td></tr></table></dt>
+<dd>
+Return true if the element <code>elt</code> has a SOAP encoding
+that can be handled by <tt class="module">ZSI</tt>
+(currently Section 5 of the SOAP 1.1 specification or an empty encoding
+for XML).
+</dl>
+
+<H1><A NAME="SECTION006000000000000000000">
+5. The <tt class="module">ParsedSoap</tt> module -- basic message handling</A>
+</H1>
+
+<P>
+This class represents an input stream that has been parsed as a SOAP
+message.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-29' xml:id='l2h-29' class="class">ParsedSoap</tt></b>(</nobr></td>
+ <td><var>input</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+Creates a <tt class="class">ParsedSoap</tt> object from the provided input source.
+If <code>input</code> is not a string, then it must be an object with a
+<tt class="method">read()</tt> method that supports the standard Python ``file read''
+semantics.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>envelope</code></td>
+ <td class="center"><code>True</code></td>
+ <td class="left" >expect a SOAP Envelope</td></tr>
+ <tr><td class="left" valign="baseline"><code>keepdom</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >Do not release the DOM when this
+object is destroyed. To access the DOM object, use the
+<tt class="method">GetDomAndReader()</tt> method. The reader object is necessary to
+properly free the DOM structure using <tt class="method">reader.releaseNode(dom)</tt>.
+
+<span class="versionnote">New in version 1.2.</span>
+</td></tr>
+ <tr><td class="left" valign="baseline"><code>readerclass</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >Class used to create DOM-creating
+XML readers; described below.
+
+<span class="versionnote">New in version 1.2.</span>
+</td></tr>
+ <tr><td class="left" valign="baseline"><code>resolver</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >Value for the <code>resolver</code>
+attribute; see below.</td></tr>
+ <tr><td class="left" valign="baseline"><code>trailers</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >Allow trailing data elements
+to appear after the <code>Body</code>.</td></tr></tbody>
+</table></div>
+
+<P>
+</dl>
+
+<P>
+The following attributes of a <tt class="class">ParsedSoap</tt> are read-only:
+
+<P>
+<dl><dt><b><tt id='l2h-30' xml:id='l2h-30' class="member">body</tt></b></dt>
+<dd>
+The root of the SOAP <code>Body</code> element.
+Using the <tt class="method">GetElementNSdict()</tt> method on this attribute can be useful
+to get a dictionary to be used with the <tt class="class">SoapWriter</tt> class.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-31' xml:id='l2h-31' class="member">body_root</tt></b></dt>
+<dd>
+The element that contains the SOAP serialization root; that is,
+the element in the SOAP <code>Body</code> that ``starts off'' the data.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-32' xml:id='l2h-32' class="member">data_elements</tt></b></dt>
+<dd>
+A (possibly empty) list of all child elements of the <code>Body</code> other
+than the root.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-33' xml:id='l2h-33' class="member">header</tt></b></dt>
+<dd>
+The root of the SOAP <code>Header</code> element.
+Using the <tt class="method">GetElementNSdict()</tt> method on this attribute can be useful
+to get a dictionary to be used with the <tt class="class">SoapWriter</tt> class.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-34' xml:id='l2h-34' class="member">header_elements</tt></b></dt>
+<dd>
+A (possibly empty) list of all elements in the SOAP <code>Header</code>.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-35' xml:id='l2h-35' class="member">trailer_elements</tt></b></dt>
+<dd>
+Returns a (possibly empty) list of all elements following the <code>Body</code>.
+If the <code>trailers</code> keyword was not used when the object was
+constructed, this attribute will not be instantiated and retrieving
+it will raise an exception.
+</dl>
+
+<P>
+The following attribute may be modified:
+
+<P>
+<dl><dt><b><tt id='l2h-36' xml:id='l2h-36' class="member">resolver</tt></b></dt>
+<dd>
+If not <code>None</code>,
+this attribute can be invoked to handle absolute <code>href</code>'s in the SOAP data.
+It will be invoked as follows:
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-37' xml:id='l2h-37' class="method">resolver</tt></b>(</nobr></td>
+ <td><var>uri, tc, ps, **keywords</var>)</td></tr></table></dt>
+<dd>
+The <code>uri</code> parameter is the URI to resolve.
+The <code>tc</code> parameter is the typecode that needs to resolve <code>href</code>; this
+may be needed to properly interpret the content of a MIME bodypart, for example.
+The <code>ps</code> parameter is the <tt class="class">ParsedSoap</tt> object that is invoking
+the resolution (this allows a single resolver instance to handle multiple
+SOAP parsers).
+
+<P>
+Failure to resolve the URI should result in an exception being raised.
+If there is no content, return <code>None</code>; this is not the same as an
+empty string.
+If there is content, the data returned should be in a form understandable
+by the typecode.
+</dl>
+</dl>
+
+<P>
+The following methods are available:
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-38' xml:id='l2h-38' class="method">Backtrace</tt></b>(</nobr></td>
+ <td><var>elt</var>)</td></tr></table></dt>
+<dd>
+Returns a human-readable ``trace'' from the document root to the
+specified element.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-39' xml:id='l2h-39' class="method">FindLocalHREF</tt></b>(</nobr></td>
+ <td><var>href, elt</var>)</td></tr></table></dt>
+<dd>
+Returns the element that has an <code>id</code> attribute whose value is specified
+by the <code>href</code> fragment identifier.
+The <code>href</code> <em>must</em> be a fragment reference -- that is, it must
+start with a pound sign.
+This method raises an <tt class="exception">EvaluateException</tt> exception if the
+element isn't found.
+It is mainly for use by the parsing methods in the <tt class="module">TypeCode</tt> module.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-40' xml:id='l2h-40' class="method">GetElementNSdict</tt></b>(</nobr></td>
+ <td><var>elt</var>)</td></tr></table></dt>
+<dd>
+Return a dictionary for all the namespace entries active at the
+current element. Each dictionary key will be the prefix and the value will
+be the namespace URI.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-41' xml:id='l2h-41' class="method">GetMyHeaderElements</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>actorlist=None</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+Returns a list of all elements in the <code>Header</code> that are intended for
+<em>this</em> SOAP processor.
+This includes all elements that either have no SOAP <code>actor</code>
+attribute, or whose value is either the special ``next actor'' value or
+in the <code>actorlist</code> list of URI's.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-42' xml:id='l2h-42' class="method">GetDomAndReader</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns a tuple containing the dom and reader objects, <code>(dom, reader)</code>.
+Unless keepdom is true, the dom and reader objects will go out of scope
+when the ParsedSoap instance is deleted. If keepdom is true, the reader
+object is needed to properly clean up the dom tree with
+<tt class="method">reader.releaseNode(dom)</tt>.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-43' xml:id='l2h-43' class="method">IsAFault</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns true if the message is a SOAP fault.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-44' xml:id='l2h-44' class="method">Parse</tt></b>(</nobr></td>
+ <td><var>how</var>)</td></tr></table></dt>
+<dd>
+Parses the SOAP <code>Body</code> according to the <code>how</code> parameter,
+and returns a Python object.
+If <code>how</code> is not a <tt class="class">TC.TypeCode</tt> object, then it should be a
+Python class object that has a <code>typecode</code> attribute.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-45' xml:id='l2h-45' class="method">ResolveHREF</tt></b>(</nobr></td>
+ <td><var>uri, tc</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This method is invoked to resolve an absolute URI.
+If the typecode <code>tc</code> has a <code>resolver</code> attribute, it will use it
+to resolve the URI specified in the <code>uri</code> parameter,
+otherwise it will use its own <code>resolver</code>, or raise an
+<tt class="exception">EvaluateException</tt> exception.
+
+<P>
+Any <code>keyword</code> parameters will be passed to the chosen resolver.
+If no content is available, it will return <code>None</code>.
+If unable to resolve the URI it will raise an
+<tt class="exception">EvaluateException</tt> exception.
+Otherwise, the resolver should return data in a form acceptable to the
+specified typecode, <code>tc</code>.
+(This will almost always be a file-like object holding opaque data;
+for XML, it may be a DOM tree.)
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-46' xml:id='l2h-46' class="method">WhatActorsArePresent</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns a list of the values of all the SOAP <code>actor</code> attributes
+found in child elements of the SOAP <code>Header</code>.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-47' xml:id='l2h-47' class="method">WhatMustIUnderstand</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns a list of "<tt class="samp">(uri, localname)</tt>" tuples for all elements in the
+SOAP <code>Header</code> that have the SOAP <code>mustUnderstand</code> attribute set
+to a non-zero value.
+</dl>
+
+<P>
+<tt class="module">ZSI</tt> supports multiple DOM implementations.
+The <code>readerclass</code> parameter specifies which one to use.
+The default is to use the DOM provided with the PyXML package developed
+by the Python XML SIG, provided through the <tt class="class">PyExpat.Reader</tt> class
+in the <tt class="module">xml.dom.ext.reader</tt> module.
+
+<P>
+The specified reader class must support the following methods:
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-48' xml:id='l2h-48' class="method">fromString</tt></b>(</nobr></td>
+ <td><var>string</var>)</td></tr></table></dt>
+<dd>
+Return a DOM object from a string.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-49' xml:id='l2h-49' class="method">fromStream</tt></b>(</nobr></td>
+ <td><var>stream</var>)</td></tr></table></dt>
+<dd>
+Return a DOM object from a file-like stream.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-50' xml:id='l2h-50' class="method">releaseNode</tt></b>(</nobr></td>
+ <td><var>dom</var>)</td></tr></table></dt>
+<dd>
+Free the specified DOM object.
+</dl>
+
+<P>
+The DOM object must support the standard Python mapping of the DOM Level 2
+specification.
+While only a small subset of specification is used, the particular
+methods and attributes used by <tt class="module">ZSI</tt> are available only
+by inspecting the source.
+
+<P>
+To use the <code>cDomlette</code> DOM provided by the 4Suite package, use the
+<tt class="class">NonvalidatingReader</tt> class in the <tt class="module">Ft.Xml.Domlette</tt> module.
+Due to name changes in the 1.0 version of 4Suite, a simple adapter class
+is required to use this DOM implementation.
+
+<P>
+<div class="verbatim"><pre>
+from 4Suite.Xml.Domlette import NonvalidatingReaderBase
+
+class 4SuiteAdapterReader(NonvalidatingReaderBase):
+
+ def fromString(self, str):
+ return self.parseString(str)
+
+ def fromStream(self, stream):
+ return self.parseStream(stream)
+
+ def releaseNode(self, node):
+ pass
+</pre></div>
+
+<H1><A NAME="SECTION007000000000000000000">
+6. The <tt class="module">TypeCode</tt> classes -- data conversions</A>
+</H1>
+
+<P>
+The <tt class="module">TypeCode</tt> module defines classes used for converting data
+between SOAP data and local Python objects.
+Python numeric and string types, and sequences and dictionaries, are
+supported by <tt class="module">ZSI</tt>.
+The <tt class="class">TC.TypeCode</tt> class is the parent class of all datatypes
+understood by <tt class="module">ZSI</tt>.
+
+<P>
+All typecodes classes have the prefix <code>TC.</code>, to avoid name clashes.
+
+<P>
+<tt class="module">ZSI</tt> provides fine-grain control over the names used when parsing and
+serializing XML into local Python objects, through the use of two
+attributes: the <code>pname</code>, the <code>aname</code>. The <code>pname</code> specifies the
+name expected on the XML element being parsed and the name to use for the output element
+when serializing. The <code>aname</code> is the name to use for the analogous
+attribute in the local Python object.
+
+<P>
+The <code>pname</code> is the parameter name. It specifies the incoming
+XML element name and the default values for the Python attribute
+and serialized names. All typecodes take the <code>pname</code> argument. This name can be
+specified as either a list or a string. When specified as a list, it must have
+two elements which are interpreted as a ``(namespace-URI, localname)'' pair.
+If specified this way, both the namespace and the local element name
+must match for the parse to succeed. For the Python attribute, and
+when generating output, only the ``localname'' is used. If a namespace-URI is
+specified then the full qualified name is used for output, and it is required
+for input; this <em>requires</em> the namespace prefix to be specified.
+
+<P>
+The <code>aname</code> is the attribute name. This parameter overrides
+any value implied by the <code>pname</code>. Typecodes nested in a <tt class="class">TC.Struct</tt>
+or <tt class="class">TC.ComplexType</tt> can use this parameter to specify
+the tag, dictionary key, or instance attribute to set.
+
+<P>
+The <code>nsdict</code> parameter to the <tt class="class">SoapWriter</tt> construct can be used to
+specify prefix to namespace-URI mappings, these are otherwise handled automatically.
+
+<P>
+
+<H1><A NAME="SECTION007100000000000000000">
+6.1 <tt class="class">TC.TypeCode</tt></A>
+</H1>
+
+<P>
+The <tt class="class">TypeCode</tt> class is the parent class of all typecodes.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-51' xml:id='l2h-51' class="class">TypeCode</tt></b>(</nobr></td>
+ <td><var>**keywords</var>)</td></tr></table></dt>
+<dd>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>pname</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >parameter name of the object</td></tr>
+ <tr><td class="left" valign="baseline"><code>aname</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >attribute name of the object</td></tr>
+ <tr><td class="left" valign="baseline"><code>minOccurs</code></td>
+ <td class="center"><code>1</code></td>
+ <td class="left" >schema facet minimum occurances</td></tr>
+ <tr><td class="left" valign="baseline"><code>maxOccurs</code></td>
+ <td class="center"><code>1</code></td>
+ <td class="left" >schema facet maximum occurances</td></tr>
+ <tr><td class="left" valign="baseline"><code>nillable</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >schema facet is this nillable (<code>xsi:nil="true"</code>)</td></tr>
+ <tr><td class="left" valign="baseline"><code>typed</code></td>
+ <td class="center"><code>True</code></td>
+ <td class="left" >Output type information (in the <code>xsi:type</code>
+attribute) when serializing. By special dispensation, typecodes within a
+<tt class="class">TC.Struct</tt> object inherit this from the container.</td></tr>
+ <tr><td class="left" valign="baseline"><code>unique</code></td>
+ <td class="center"><code>0</code></td>
+ <td class="left" >If true, the object is unique and will
+never be ``aliased'' with another object, so the <code>id</code> attribute
+need not be output.</td></tr><P>
+
+ <tr><td class="left" valign="baseline"><code>pyclass</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >when parsing data, instances of this class
+can be created to store the data. Default behavior is reflective of specific
+TypeCode classes.</td></tr>
+ <tr><td class="left" valign="baseline"><code>attrs_aname</code></td>
+ <td class="center"><code>'_attrs'</code></td>
+ <td class="left" >attribute name of the object where
+attribute values are stored. Used for serialization and parsing.</td></tr><P>
+
+ </tbody>
+</table></div>
+
+<P>
+Optional elements are those which do not have to be an incoming
+message, or which have the XML Schema <code>nil</code> attribute set.
+When parsing the message as part of a <code>Struct</code>, then the Python
+instance attribute will not be set, or the element will not appear as
+a dictionary key.
+When being parsed as a simple type, the value <code>None</code> is returned.
+When serializing an optional element, a non-existent attribute, or a value
+of <code>None</code> is taken to mean not present, and the element is skipped.
+
+<P>
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-52' xml:id='l2h-52' class="member">typechecks</tt></b></dt>
+<dd>
+This is a class attribute.
+If true (the default) then all typecode constructors do more
+rigorous type-checking on their parameters.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-53' xml:id='l2h-53' class="member">tag</tt></b></dt>
+<dd>
+This is a class attribute.
+Specifies the global element declaration this typecode represents, the value is
+a "<tt class="samp">(namespace, name)</tt>" tuple.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-54' xml:id='l2h-54' class="member">type</tt></b></dt>
+<dd>
+This is a class attribute.
+Specifies the global type definition this typecode represents, the value is
+a "<tt class="samp">(namespace, name)</tt>" tuple.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-55' xml:id='l2h-55' class="member">attribute_typecode_dict</tt></b></dt>
+<dd>
+This is a class attribute.
+This is a dict of "<tt class="samp">(URI, NCName)</tt>" tuple keys, the values of each is a
+typecode. This is how attribute declarations other than SOAP and XMLSchema
+attribute declarations (eg. <code>xsi:type</code>, <code>id</code>, <code>href</code>, etc) are
+represented.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-56' xml:id='l2h-56' class="member">logger</tt></b></dt>
+<dd>
+This is a class attribute.
+logger instance for this class.
+</dl>
+
+<P>
+The following methods are useful for defining new typecode classes;
+see the section on dynamic typing for more details. In all of the following,
+the <code>ps</code> parameter is a <tt class="class">ParsedSoap</tt> object.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-57' xml:id='l2h-57' class="method">checkname</tt></b>(</nobr></td>
+ <td><var>elt, ps</var>)</td></tr></table></dt>
+<dd>
+Checks if the name and type of the element <code>elt</code> are
+correct and raises a <tt class="exception">EvaluateException</tt> if not.
+Returns the element's type as a "<tt class="samp">(uri, localname)</tt>" tuple if so.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-58' xml:id='l2h-58' class="method">checktype</tt></b>(</nobr></td>
+ <td><var>elt, ps</var>)</td></tr></table></dt>
+<dd>
+Like <tt class="method">checkname()</tt> except that the element name is ignored.
+This method is actually invoked by <tt class="method">checkname()</tt> to do the
+second half of its processing, but is useful to invoke
+directly, such as when resolving multi-reference data.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-59' xml:id='l2h-59' class="method">nilled</tt></b>(</nobr></td>
+ <td><var>elt, ps</var>)</td></tr></table></dt>
+<dd>
+If the element <code>elt</code> has data, this returns <code>False</code>.
+If it has no data, and the typecode is not optional, an
+<tt class="exception">EvaluateException</tt> is raised; if it is optional,
+a <code>True</code> is returned.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-60' xml:id='l2h-60' class="method">simple_value</tt></b>(</nobr></td>
+ <td><var>elt, ps, mixed=False</var>)</td></tr></table></dt>
+<dd>
+Returns the text content of the element <code>elt</code>.
+If no value is present, or the element has non-text children, an
+<tt class="exception">EvaluateException</tt> is raised. If <code>mixed</code> is <code>False</code> if
+child elements are discovered an <tt class="exception">EvaluateException</tt> is raised, else
+join all text nodes and return the result.
+
+<P>
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION007200000000000000000">
+6.2 <tt class="class">TC.Any</tt> -- the basis of dynamic typing</A>
+</H1>
+
+<P>
+SOAP provides a flexible set of serialization rules, ranging from
+completely self-describing to completely opaque, requiring an external
+schema. For example, the following are all possible ways of encoding an
+integer element <code>i</code> with a value of <code>12</code>:
+
+<P>
+
+<H2><A NAME="SECTION007210000000000000000">
+6.2.1 simple data</A>
+</H2> - requires type information
+<div class="verbatim"><pre>
+&lt;tns:i xsi:type="SOAP-ENC:integer"&gt;12&lt;/tns:i&gt;
+&lt;tns:i xsi:type="xsd:nonNegativeInteger"&gt;12&lt;/tns:i&gt;
+&lt;SOAP-ENC:integer&gt;12&lt;/SOAP-ENC:integer&gt;
+&lt;tns:i&gt;12&lt;/tns:i&gt;
+</pre></div>
+
+<P>
+The first three lines are examples of <em>typed</em> elements.
+If <tt class="module">ZSI</tt> is asked to parse any of the above examples, and a
+<tt class="class">TC.Any</tt> typecode is given, it will properly create a Python
+integer for the first three, and raise a <tt class="exception">EvaluateException</tt>
+for the fourth.
+
+<P>
+
+<H2><A NAME="SECTION007220000000000000000">
+6.2.2 compound data</A>
+</H2> - Struct or Array
+Compound data, such as a <code>struct</code>, may also be self-describing (namespace
+are omitted for clarity):
+<div class="verbatim"><pre>
+&lt;tns:foo&gt;
+ &lt;tns:i xsi:type="SOAP-ENC:integer"&gt;12&lt;/tns:i&gt;
+ &lt;tns:name xsi:type="SOAP-ENC:string"&gt;Hello world&lt;/tns:name&gt;
+&lt;/tns:foo&gt;
+</pre></div>
+
+<P>
+If this is parsed with a <tt class="class">TC.Any</tt> typecode, either a Python <code>dict</code>
+is created or if <code>aslist</code> is True a <code>list</code>:
+<div class="verbatim"><pre>
+ps = ParsedSoap(xml, envelope=False)
+print ps.Parse(TC.Any())
+{ 'name': u'Hello world', 'i': 12 }
+
+print ps.Parse(TC.Any(aslist=True))
+[ 12, u'Hello world' ]
+</pre></div>
+Note that one preserves order, while the other preserves the element names.
+
+<P>
+
+<H2><A NAME="SECTION007230000000000000000">
+6.2.3 class description</A>
+</H2>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-61' xml:id='l2h-61' class="class">Any</tt></b>(</nobr></td>
+ <td><var>name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+Used for parsing incoming SOAP data (that is typed), and serializing
+outgoing Python data.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>aslist</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >If true, then the data is (recursively)
+treated as a list of values.
+The default is a Python dictionary, which preserves parameter names but
+loses the ordering.
+
+<span class="versionnote">New in version 1.1.</span>
+</td></tr></tbody>
+</table></div>
+
+<P>
+In addition, if the Python object being serialized with an <tt class="class">Any</tt>
+has a <code>typecode</code> attribute, then the <tt class="method">serialize</tt> method of
+the typecode will be invoked to do the serialization.
+This allows objects to override the default dynamic serialization.
+</dl>
+
+<P>
+Referring back to the compound XML data above, it is possible to create a new
+typecode capable of parsing elements of type <code>mytype</code>.
+This class would know that the <code>i</code> element is an integer,
+so that the explicit typing becomes optional, rather than required.
+
+<P>
+
+<H2><A NAME="SECTION007240000000000000000">
+6.2.4 Adding new types</A>
+</H2> Most of the <tt class="class">TypeCodes</tt> classes in
+<tt class="module">TC</tt> are registered with <tt class="class">Any</tt>, making an instance of itself
+available for dynamic typing. New <tt class="class">TypeCode</tt> classes can be created and
+registered with <tt class="class">Any</tt> by using <tt class="function">RegisterType</tt>. In order to
+override an existing entry in the registry call <tt class="function">RegisterType</tt> with
+<code>clobber=True</code>. The serialization entries are mappings between builtin
+Python types and a <tt class="class">TypeCode</tt> instance, it is not possible to have one
+Python type map to multiple typecodes. The parsing entries are mappings
+between <code>(namespaceURI,name)</code> tuples, representing the <code>xsi:type</code>
+attribute, and a <tt class="class">TypeCode</tt> instance. Thus, only one instance of a
+<tt class="class">TypeCode</tt> class can represent a XML Schema type. So this mechanism is
+not appropriate for representing XML Schema element information.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-62' xml:id='l2h-62' class="class"><em>NEWTYPECODE</em>(TypeCode)</tt></b>(</nobr></td>
+ <td><var>...</var>)</td></tr></table></dt>
+<dd>
+The new typecode should be derived from the <tt class="class">TC.TypeCode</tt> class, and
+<tt class="method">TypeCode.__init__()</tt> must be invoked in the new class's constructor.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-63' xml:id='l2h-63' class="member">parselist</tt></b></dt>
+<dd>
+This is a class attribute, used when parsing incoming SOAP data.
+It should be a sequence of "<tt class="samp">(uri, localname)</tt>" tuples to identify
+the datatype.
+If <code>uri</code> is <code>None</code>, it is taken to mean either the XML Schema
+namespace or the SOAP encoding namespace;
+this should only be used if adding support for additional primitive types.
+If this list is empty, then the type of the incoming SOAP data is assumed
+to be correct; an empty list also means that incoming typed data cannot
+by dynamically parsed.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-64' xml:id='l2h-64' class="member">errorlist</tt></b></dt>
+<dd>
+This is a class attribute, used when reporting a parsing error.
+It is a text string naming the datatype that was expected.
+If not defined, <tt class="module">ZSI</tt> will create this attribute from the <code>parselist</code>
+attribute when it is needed.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-65' xml:id='l2h-65' class="member">seriallist</tt></b></dt>
+<dd>
+This is a class attribute, used when serializing Python objects
+dynamically.
+It specifies what types of object instances (or Python types) this
+typecode can serialize.
+It should be a sequence, where each element is a Python class object,
+a string naming the class, or a type object from Python's <tt class="module">types</tt>
+module (if the
+new typecode is serializing a built-in Python type).
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-66' xml:id='l2h-66' class="method">parse</tt></b>(</nobr></td>
+ <td><var>elt, ps</var>)</td></tr></table></dt>
+<dd>
+<tt class="module">ZSI</tt> invokes this method to
+parse the <code>elt</code> element and return its Python value.
+The <code>ps</code> parameter is the <tt class="class">ParsedSoap</tt> object, and can be
+used for dereferencing <code>href</code>'s, calling <tt class="method">Backtrace()</tt> to
+report errors, etc.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-67' xml:id='l2h-67' class="method">serialize</tt></b>(</nobr></td>
+ <td><var>sw, pyobj</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+<tt class="module">ZSI</tt> invokes this method to output a Python object to a SOAP stream.
+The <code>sw</code> parameter will be a <tt class="class">SoapWriter</tt> object, and
+the <code>pyobj</code> parameter is the Python object to serialize.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>attrtext</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >Text (with leading space)
+to output as an attribute; this is normally used by the <tt class="class">TC.Array</tt> class
+to pass down indexing information.</td></tr>
+ <tr><td class="left" valign="baseline"><code>name</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >Name to use for serialization; defaults
+to the name specified in the typecode, or a generated name.</td></tr>
+ <tr><td class="left" valign="baseline"><code>typed</code></td>
+ <td class="center"><em>per-typecode</em></td>
+ <td class="left" >Whether or not to output type
+information; the default is to use the value in the typecode.</td></tr></tbody>
+</table></div>
+</dl>
+
+<P>
+Once the new typecode class has been defined, it should be registered with
+<tt class="module">ZSI</tt>'s dynamic type system by invoking the following function:
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-68' xml:id='l2h-68' class="function">RegisterType</tt></b>(</nobr></td>
+ <td><var>class</var><big>[</big><var>, clobber=0</var><big>[</big><var>, **keywords</var><big>]</big><var></var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+By default, it is an error to replace an existing type registration, and
+an exception will be raised.
+The <code>clobber</code> parameter may be given to allow replacement.
+A single instance of the <code>class</code> object will be created, and
+the <code>keyword</code> parameters are passed to the constructor.
+</dl>
+
+<P>
+If the class is not registered, then instances of the class cannot be
+processed as dynamic types.
+This may be acceptable in some environments.
+
+<P>
+
+<H1><A NAME="SECTION007300000000000000000">
+6.3 <tt class="class">TC.SimpleType</tt></A>
+</H1>
+Parent class of all simple types.
+
+<P>
+<dl><dt><b><tt id='l2h-69' xml:id='l2h-69' class="member">empty_content</tt></b></dt>
+<dd>
+This is a class attribute.
+Value returned when tag or node is present, is not nilled, and without text
+content.
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION007400000000000000000">
+6.4 Strings</A>
+</H1>
+
+<P>
+SOAP/XMLSchema Strings are Python strings.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-70' xml:id='l2h-70' class="class">String</tt></b>(</nobr></td>
+ <td><var>name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+The parent type of all strings.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>resolver</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >A function that can resolve an
+absolute URI and return its content as a string, as described in the
+<tt class="class">ParsedSoap</tt> description.</td></tr>
+ <tr><td class="left" valign="baseline"><code>strip</code></td>
+ <td class="center"><code>True</code></td>
+ <td class="left" >If true, leading and trailing whitespace
+are stripped from the content.</td></tr></tbody>
+</table></div>
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-71' xml:id='l2h-71' class="class">Enumeration</tt></b>(</nobr></td>
+ <td><var>value_list, name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+Like <tt class="class">TC.String</tt>, but the value must be a member of
+the <code>choices</code> sequence of text strings
+</dl>
+
+<P>
+In addition to <tt class="class">TC.String</tt>,
+the basic string, several subtypes are provided that transparently
+handle common encodings.
+These classes create a temporary string object and pass that to
+the <tt class="method">serialize()</tt> method.
+When doing RPC encoding, and checking for non-unique strings, the
+<tt class="class">TC.String</tt> class must have the original Python string, as well
+as the new output.
+This is done by adding a parameter to the <tt class="method">serialize()</tt> method:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>orig</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >If deriving a new typecode from the
+string class, and the derivation creates a temporary Python string
+(such as by <tt class="class">Base64String</tt>), than this parameter is the
+original string being serialized.</td></tr></tbody>
+</table></div>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-72' xml:id='l2h-72' class="class">Base64String</tt></b>(</nobr></td>
+ <td><var>name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+The value is encoded in Base-64.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-73' xml:id='l2h-73' class="class">HexBinaryString</tt></b>(</nobr></td>
+ <td><var>name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+Each byte is encoded as its printable version.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-74' xml:id='l2h-74' class="class">URI</tt></b>(</nobr></td>
+ <td><var>name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+The value is URL quoted (e.g., <code>%20</code> for the space character).
+</dl>
+
+<P>
+It is often the case that a parameter will be typed as a string for
+transport purposes, but will in fact have special syntax and processing
+requirements.
+For example, a string could be used for an XPath expression, but it is
+more convenient for the Python value to
+actually be the compiled expression. Here is how to do that:
+
+<P>
+<div class="verbatim"><pre>
+import xml.xpath.pyxpath
+import xml.xpath.pyxpath.Compile as _xpath_compile
+
+class XPathString(TC.String):
+ def __init__(self, name, **kw):
+ TC.String.__init__(self, name, **kw)
+
+ def parse(self, elt, ps):
+ val = TC.String.parse(self, elt, ps)
+ try:
+ val = _xpath_compile(val)
+ except:
+ raise EvaluateException("Invalid XPath expression",
+ ps.Backtrace(elt))
+ return val
+</pre></div>
+
+<P>
+In particular, it is common to send XML as a string, using entity
+encoding to protect the ampersand and less-than characters.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-75' xml:id='l2h-75' class="class">XMLString</tt></b>(</nobr></td>
+ <td><var>name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+Parses the data as a string, but returns an XML DOM object.
+For serialization, takes an XML DOM (or element node), and outputs
+it as a string.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>readerclass</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >Class used to create DOM-creating
+XML readers; described in the <tt class="class">ParsedSoap</tt> chapter.</td></tr></tbody>
+</table></div>
+
+<P>
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION007500000000000000000">
+6.5 Integers</A>
+</H1>
+
+<P>
+SOAP/XMLSchema integers are Python integers.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-76' xml:id='l2h-76' class="class">Integer</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+The parent type of all integers.
+This class handles any of the several types (and ranges) of SOAP integers.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>format</code></td>
+ <td class="center"><code>%d</code></td>
+ <td class="left" >Format string for serializing.
+
+<span class="versionnote">New in version 1.2.</span>
+</td></tr></tbody>
+</table></div>
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-77' xml:id='l2h-77' class="class">IEnumeration</tt></b>(</nobr></td>
+ <td><var>choices</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+Like <tt class="class">TC.Integer</tt>, but the value must be a member of
+the <code>choices</code> sequence.
+</dl>
+
+<P>
+A number of sub-classes are defined to handle smaller-ranged numbers.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-78' xml:id='l2h-78' class="class">Ibyte</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A signed eight-bit value.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-79' xml:id='l2h-79' class="class">IunsignedByte</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+An unsigned eight-bit value.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-80' xml:id='l2h-80' class="class">Ishort</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A signed 16-bit value.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-81' xml:id='l2h-81' class="class">IunsignedShort</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+An unsigned 16-bit value.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-82' xml:id='l2h-82' class="class">Iint</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A signed 32-bit value.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-83' xml:id='l2h-83' class="class">IunsignedInt</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+An unsigned 32-bit value.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-84' xml:id='l2h-84' class="class">Ilong</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+An signed 64-bit value.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-85' xml:id='l2h-85' class="class">IunsignedLong</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+An unsigned 64-bit value.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-86' xml:id='l2h-86' class="class">IpositiveInteger</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A value greater than zero.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-87' xml:id='l2h-87' class="class">InegativeInteger</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A value less than zero.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-88' xml:id='l2h-88' class="class">InonPositiveInteger</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A value less than or equal to zero.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-89' xml:id='l2h-89' class="class">InonNegativeInteger</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A value greater than or equal to zero.
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION007600000000000000000">
+6.6 Floating-point Numbers</A>
+</H1>
+
+<P>
+SOAP/XMLSchema floating point numbers are Python floats.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-90' xml:id='l2h-90' class="class">Decimal</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+The parent type of all floating point numbers.
+This class handles any of the several types (and ranges) of SOAP
+floating point numbers.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>format</code></td>
+ <td class="center"><code>%f</code></td>
+ <td class="left" >Format string for serializing.
+
+<span class="versionnote">New in version 1.2.</span>
+</td></tr></tbody>
+</table></div>
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-91' xml:id='l2h-91' class="class">FPEnumeration</tt></b>(</nobr></td>
+ <td><var>value_list, name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+Like <tt class="class">TC.Decimal</tt>, but the value must be a member of
+the <code>value_list</code> sequence.
+Be careful of round-off errors if using this class.
+</dl>
+
+<P>
+Two sub-classes are defined to handle smaller-ranged numbers.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-92' xml:id='l2h-92' class="class">FPfloat</tt></b>(</nobr></td>
+ <td><var>name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+An IEEE single-precision 32-bit floating point value.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-93' xml:id='l2h-93' class="class">FPdouble</tt></b>(</nobr></td>
+ <td><var>name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+An IEEE double-precision 64-bit floating point value.
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION007700000000000000000">
+6.7 Dates and Times</A>
+</H1>
+
+<P>
+SOAP dates and times are Python time tuples in UTC (GMT), as documented
+in the Python <tt class="module">time</tt> module.
+Time is tricky, and processing anything other than a simple absolute time
+can be difficult.
+(Even then, timezones lie in wait to trip up the unwary.)
+A few caveats are in order:
+
+<P>
+
+<OL>
+<LI>Some date and time formats will be parsed into tuples that are
+not valid time values.
+For example, 75 minutes is a valid duration, although not a legal value
+for the minutes element of a time tuple.
+
+<P>
+</LI>
+<LI>Fractional parts of a second may be lost when parsing, and may have
+extra trailing zero's when serializing.
+
+<P>
+</LI>
+<LI>Badly-formed time tuples may result in non-sensical values being serialized;
+the first six values are taken directly as year, month, day, hour, minute,
+second in UTC.
+
+<P>
+</LI>
+<LI>Although the classes <tt class="class">Duration</tt> and <tt class="class">Gregorian</tt> are defined, they
+are for internal use only and should not be included in any <tt class="class">TypeCode</tt>
+you define. Instead, use the classes beginning with a lower case g in your
+typecodes.
+
+<P>
+</LI>
+</OL>
+
+<P>
+In addition, badly-formed values may result in non-sensical serializations.
+
+<P>
+When serializing, an integral or floating point number is taken as
+the number of seconds since the epoch, in UTC.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-94' xml:id='l2h-94' class="class">Duration</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A relative time period.
+Negative durations have all values less than zero; this makes
+it easy to add a duration to a Python time tuple.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-95' xml:id='l2h-95' class="class">Gregorian</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+An absolute time period.
+This class should not be instantiated directly; use one of the <code>gXXX</code>
+classes instead.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-96' xml:id='l2h-96' class="class">gDateTime</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A date and time.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-97' xml:id='l2h-97' class="class">gDate</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A date.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-98' xml:id='l2h-98' class="class">gYearMonth</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A year and month.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-99' xml:id='l2h-99' class="class">gYear</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A year.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-100' xml:id='l2h-100' class="class">gMonthDay</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A month and day.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-101' xml:id='l2h-101' class="class">gDay</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A day.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-102' xml:id='l2h-102' class="class">gTime</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+A time.
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION007800000000000000000">
+6.8 Boolean</A>
+</H1>
+
+<P>
+SOAP Booleans are Python integers.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-103' xml:id='l2h-103' class="class">Boolean</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+When marshaling zero or the word ``false'' is returned as <code>0</code>
+and any non-zero value or the word ``true'' is returned as <code>1</code>.
+When serializing, the number <code>0</code> or <code>1</code> will be generated.
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION007900000000000000000">
+6.9 XML</A>
+</H1>
+
+<P>
+XML is a Python DOM element node.
+If the value to be serialized is a Python string, then an <code>href</code>
+is generated, with the value used as the URI.
+This can be used, for example, when generating SOAP with attachments.
+Otherwise, the XML is typically put inside a wrapper element that sets
+the proper SOAP encoding style.
+
+<P>
+For efficiency, incoming XML is returend as a ``pointer'' into the
+DOM tree maintained within the <tt class="class">ParsedSoap</tt> object.
+If that object is going to go out of scope, the data will be destroyed
+and any XML objects will become empty elements.
+The class instance variable <code>copyit</code>, if non-zero indicates that a
+deep copy of the XML subtree will be made and returned as the value.
+Note that it is generally more efficient to keep the <tt class="class">ParsedSoap</tt>
+object alive until the XML data is no longerneeded.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-104' xml:id='l2h-104' class="class">XML</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This typecode represents a portion of an XML document embedded in a SOAP
+message.
+The value is the element node.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>copyit</code></td>
+ <td class="center">TC.XML.copyit</td>
+ <td class="left" >Return a copy of the parsed data.</td></tr>
+ <tr><td class="left" valign="baseline"><code>comments</code></td>
+ <td class="center"><code>0</code></td>
+ <td class="left" >Preserve comments in output.</td></tr>
+ <tr><td class="left" valign="baseline"><code>inline</code></td>
+ <td class="center"><code>0</code></td>
+ <td class="left" >The XML sub-tree is single-reference,
+so can be output in-place.</td></tr>
+ <tr><td class="left" valign="baseline"><code>resolver</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >A function that can resolve an
+absolute URI and return its content as an element node, as described in the
+<tt class="class">ParsedSoap</tt> description.</td></tr>
+ <tr><td class="left" valign="baseline"><code>wrapped</code></td>
+ <td class="center"><code>1</code></td>
+ <td class="left" >If zero, the XML is output directly,
+and not within a SOAP wrapper element.
+
+<span class="versionnote">New in version 1.2.</span>
+</td></tr></tbody>
+</table></div>
+</dl>
+
+<P>
+When serializing, it may be necessary to specify which namespace prefixes
+are ``active'' in the XML.
+This is done by using the <code>unsuppressedPrefixes</code> parameter when
+calling the <tt class="method">serialize()</tt> method.
+(This will only work when XML is the top-level item being serialized,
+such as when using typecodes and document-style interfaces.)
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>unsuppressedPrefixes</code></td>
+ <td class="center">[]</td>
+ <td class="left" >An array of strings
+identifying the namespace prefixes that should be output.</td></tr></tbody>
+</table></div>
+
+<P>
+
+<H1><A NAME="SECTION0071000000000000000000">
+6.10 ComplexType</A>
+</H1>
+
+<P>
+Represents the XMLSchema ComplexType .
+
+<span class="versionnote">New in version 2.0.</span>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-105' xml:id='l2h-105' class="class">ComplexType</tt></b>(</nobr></td>
+ <td><var>pyclass, ofwhat</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This class defines a compound data structure.
+If <code>pyclass</code> is <code>None</code>, then the data will be marshaled
+into a Python dictionary, and each item in the <code>ofwhat</code> sequence
+specifies a (possible) dictionary entry.
+Otherwise, <code>pyclass</code> must be a Python class object.
+The data is then marshaled into the object, and each item in the
+<code>ofwhat</code>
+sequence specifies an attribute of the instance to set.
+
+<P>
+Note that each typecode in <code>ofwhat</code> must have a name.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>inorder</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >Items within the structure must appear
+in the order specified in the <code>ofwhat</code> sequence.</td></tr>
+ <tr><td class="left" valign="baseline"><code>inline</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >The structure is single-reference,
+so ZSI does not have to use <code>href/id</code> encodings.</td></tr>
+ <tr><td class="left" valign="baseline"><code>mutable</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >If an object is going to be serialized
+multiple times, and its state may be modified between serializations,
+then this keyword should be used, otherwise a single instance will be
+serialized, with multiple references to it.
+This argument implies the <code>inline</code> argument.
+
+<span class="versionnote">New in version 1.2.</span>
+</td></tr>
+ <tr><td class="left" valign="baseline"><code>type</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >A "<tt class="samp">(uri, localname)</tt>" tuple that
+defines the type of the structure.
+If present, and if the input data has a <code>xsi:type</code> attribute, then the
+namespace-qualified value of that attribute must match the value
+specified by this parameter.
+By default, type-checking is not done for structures; matching child element
+names is usually sufficient and senders rarely provide type information.</td></tr>
+ <tr><td class="left" valign="baseline"><code>mixed</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >using a mixed content model, allow text and
+element content.</td></tr>
+ <tr><td class="left" valign="baseline"><code>mixed_aname</code></td>
+ <td class="center"><code>'_text'</code></td>
+ <td class="left" >if mixed is True, text
+content is set in this attribute (key).</td></tr></tbody>
+</table></div>
+
+<P>
+If the <code>typed</code> keyword is used, then its value is assigned to
+all typecodes in the <code>ofwhat</code> parameter.
+If any of the typecodes in <code>ofwhat</code> are repeatable, then the
+<code>inorder</code> keyword should not be used and the <code>hasextras</code> parameter
+<em>must</em> be used.
+
+<P>
+For example, the following C structure:
+<div class="verbatim"><pre>
+struct foo {
+ int i;
+ char* text;
+};
+</pre></div>
+could be declared as follows:
+<div class="verbatim"><pre>
+class foo:
+ def __init__(self, name):
+ self.name = name
+ def __str__(self):
+ return str((self.name, self.i, self.text))
+
+foo.typecode = TC.Struct(foo,
+ ( TC.Integer('i'), TC.String('text') ),
+ 'foo')
+</pre></div>
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION0071100000000000000000">
+6.11 Struct</A>
+</H1>
+
+<P>
+SOAP Struct is a complex type for accessors identified by name. No element may
+have the same name as any other, nor may any element have a maxOccurs &gt; 1.
+SOAP Structs are either Python dictionaries or instances of application-specified classes.
+
+<P>
+
+<H1><A NAME="SECTION0071200000000000000000">
+6.12 Arrays</A>
+</H1>
+
+<P>
+SOAP arrays are Python lists; multi-dimensional arrays are
+lists of lists and are indistinguishable from a SOAP array of arrays.
+Arrays may be <em>sparse</em>, in which case each element in the
+array is a tuple of "<tt class="samp">(subscript, data)</tt>" pairs.
+If an array is not sparse, a specified <em>fill</em> element will be
+used for the missing values.
+
+<P>
+<strong>Currently only singly-dimensioned arrays are supported.</strong>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-106' xml:id='l2h-106' class="class">Array</tt></b>(</nobr></td>
+ <td><var>atype, ofwhat</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+The <code>atype</code> parameter is a <code>(URI,NCName)</code> tuple representing the SOAP
+array type. The <code>ofwhat</code> parameter is a typecode describing the array elements.
+</dl>
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>childnames</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >Default name to use for the child
+elements.</td></tr>
+ <tr><td class="left" valign="baseline"><code>dimensions</code></td>
+ <td class="center"><code>1</code></td>
+ <td class="left" >The number of dimensions in
+the array.</td></tr>
+ <tr><td class="left" valign="baseline"><code>fill</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >The value to use when an array
+element is omitted.</td></tr>
+ <tr><td class="left" valign="baseline"><code>mutable</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >If an object is going to be serialized
+multiple times, and its state may be modified between serializations,
+then this keyword should be used, otherwise a single instance will be
+serialized, with multiple references to it.</td></tr>
+ <tr><td class="left" valign="baseline"><code>nooffset</code></td>
+ <td class="center"><code>0</code></td>
+ <td class="left" >Do not use the SOAP <code>offset</code>
+attribute so skip leading elements with the same value as <code>fill</code>.</td></tr>
+ <tr><td class="left" valign="baseline"><code>sparse</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >The array is sparse.</td></tr>
+ <tr><td class="left" valign="baseline"><code>size</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >An integer or list of integers that
+specifies the maximum array dimensions.</td></tr>
+ <tr><td class="left" valign="baseline"><code>undeclared</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >The SOAP "<tt class="samp">arrayType</tt>" attribute
+need not appear.</td></tr></tbody>
+</table></div>
+
+<P>
+
+<H1><A NAME="SECTION0071300000000000000000">
+6.13 Apache Datatype</A>
+</H1>
+
+<P>
+The Apache SOAP project, urlhttp://xml.apache.org/soap/index.html,
+has defined a popular SOAP datatype in the
+<code>http://xml.apache.org/xml-soap</code> namespace, a
+<tt class="class">Map</tt>.
+
+<P>
+The <code>Map</code> type is encoded as a list of <code>item</code> elements.
+Each <code>item</code> has a <code>key</code> and <code>value</code> child element; these
+children must have SOAP type information.
+An Apache Map is either a Python dictionary or a list of two-element
+tuples.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-107' xml:id='l2h-107' class="class">Apache.Map</tt></b>(</nobr></td>
+ <td><var>name</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+An Apache map.
+Note that the class name is dotted.
+</dl>
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>aslist</code></td>
+ <td class="center"><code>0</code></td>
+ <td class="left" >Use a list of tuples rather than
+a dictionary.</td></tr></tbody>
+</table></div>
+
+<H1><A NAME="SECTION008000000000000000000">
+7. The <tt class="module">SoapWriter</tt> module -- serializing data</A>
+</H1>
+
+<P>
+The SoapWriter class is used to output SOAP messages.
+Note that its output is encoded as UTF-8; when transporting SOAP over
+HTTP it is therefore important to set the <code>charset</code> attribute
+of the <code>Content-Type</code> header.
+
+<P>
+The <tt class="class">SoapWriter</tt> class reserves some namespace prefixes:
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Prefix</th>
+ <th class="left" >URI</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>SOAP-ENV</code></td>
+ <td class="left" ><code>http://schemas.xmlsoap.org/soap/envelope/</code></td></tr>
+ <tr><td class="left" valign="baseline"><code>SOAP-ENC</code></td>
+ <td class="left" ><code>http://schemas.xmlsoap.org/soap/encoding/</code></td></tr>
+ <tr><td class="left" valign="baseline"><code>ZSI</code></td>
+ <td class="left" ><code>http://www.zolera.com/schemas/ZSI/</code></td></tr>
+ <tr><td class="left" valign="baseline"><code>xsd</code></td>
+ <td class="left" ><code>http://www.w3.org/2001/XMLSchema</code></td></tr>
+ <tr><td class="left" valign="baseline"><code>xsi</code></td>
+ <td class="left" ><code>http://www.w3.org/2001/XMLSchema-instance</code></td></tr></tbody>
+</table></div>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-108' xml:id='l2h-108' class="class">SoapWriter</tt></b>(</nobr></td>
+ <td><var>optional**keywords</var>)</td></tr></table></dt>
+<dd>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>encodingStyle</code></td>
+ <td class="center">None</td>
+ <td class="left" >If not <code>None</code>, then
+use the specified value as the value for the SOAP <code>encodingStyle</code>
+attribute.
+
+<span class="versionnote">New in version 1.2.</span>
+</td></tr>
+ <tr><td class="left" valign="baseline"><code>envelope</code></td>
+ <td class="center"><code>True</code></td>
+ <td class="left" >Create a SOAP Envelope
+
+<span class="versionnote">New in version 1.2.</span>
+</td></tr>
+ <tr><td class="left" valign="baseline"><code>nsdict</code></td>
+ <td class="center"><code>{}</code></td>
+ <td class="left" >Dictionary of namespaces to declare
+in the SOAP <code>Envelope</code></td></tr>
+ <tr><td class="left" valign="baseline"><code>header</code></td>
+ <td class="center"><code>True</code></td>
+ <td class="left" >create a SOAP <code>Header</code> element</td></tr>
+ <tr><td class="left" valign="baseline"><code>outputclass</code></td>
+ <td class="center"><code>ElementProxy</code></td>
+ <td class="left" >wrapper around DOM or other
+XML library.</td></tr></tbody>
+</table></div>
+</dl>
+
+<P>
+Creating a <tt class="class">SoapWriter</tt> object with <code>envelope</code> set to <code>False</code>
+results in an object that can be used for serializing objects into a string.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-109' xml:id='l2h-109' class="method">serialize</tt></b>(</nobr></td>
+ <td><var>pyobj</var><big>[</big><var>, typecode=None</var><big>[</big><var>,
+root=None</var><big>[</big><var>, header_pyobjs=None</var><big>[</big><var>, **keywords</var><big>]</big><var></var><big>]</big><var></var><big>]</big><var></var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This method serializes the <code>pyobj</code> Python object as directed
+by the <code>typecode</code> typecode object.
+If <code>typecode</code> is omitted, then <code>pyobj</code> should be a Python
+object instance of a class that has a <code>typecode</code> attribute.
+It returns <code>self</code>, so that serializations can be chained together, or
+so that the <tt class="method">close()</tt> method can be invoked.
+The <code>root</code> parameter may be used to explicitly indicate the root
+(main element) of a SOAP encoding, or indicate that the item is not the
+root.
+If specified, it should have the numeric value of zero or one.
+Any other keyword parameters are passed to the typecode's <code>serialize</code>
+method.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-110' xml:id='l2h-110' class="method">close</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Invokes all the callbacks, if any. The <tt class="function">close</tt> operations can only
+happen once, if invoked a second time it will just return. This method will be
+invoked automatically if the object is deleted.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-111' xml:id='l2h-111' class="method">__str__</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Invokes the <tt class="function">close</tt> method, and returns a string representation of the
+serialized object. Assumes that <tt class="function">serialize</tt> has been invoked.
+</dl>
+
+<P>
+The following methods are primarily useful for those writing new typecodes.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-112' xml:id='l2h-112' class="method">AddCallback</tt></b>(</nobr></td>
+ <td><var>func, arg</var>)</td></tr></table></dt>
+<dd>
+Used by typecodes when serializing, allows them to add output after
+the SOAP <code>Body</code> is written but before the SOAP <code>Envelope</code> is closed.
+The function <tt class="method">func()</tt>
+will be called with the <tt class="class">SoapWriter</tt> object and the specified <code>arg</code>
+argument, which may be a tuple.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-113' xml:id='l2h-113' class="method">Forget</tt></b>(</nobr></td>
+ <td><var>obj</var>)</td></tr></table></dt>
+<dd>
+Forget that <code>obj</code> has been seen before.
+This is useful when repeatedly serializing a mutable object.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-114' xml:id='l2h-114' class="method">Known</tt></b>(</nobr></td>
+ <td><var>obj</var>)</td></tr></table></dt>
+<dd>
+If <code>obj</code> has been seen before (based on its Python <code>id</code>), return
+<code>1</code>. Otherwise, remember <code>obj</code> and return <code>0</code>.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-115' xml:id='l2h-115' class="method">ReservedNS</tt></b>(</nobr></td>
+ <td><var>prefix, uri</var>)</td></tr></table></dt>
+<dd>
+Returns true if the specified namespace <code>prefix</code> and <code>uri</code> collide
+with those used by the implementation.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-116' xml:id='l2h-116' class="method">writeNSDict</tt></b>(</nobr></td>
+ <td><var>nsdict</var>)</td></tr></table></dt>
+<dd>
+Outputs <code>nsdict</code> as a namespace dictionary.
+It is assumed that an XML start-element is pending on the output
+stream.
+</dl>
+
+<H1><A NAME="SECTION009000000000000000000">
+8. The <tt class="module">Fault</tt> module -- reporting errors</A>
+</H1>
+
+<P>
+SOAP defines a <em>fault</em> message as the way for a recipient to
+indicate it was unable to process a message.
+The <tt class="module">ZSI</tt> <tt class="class">Fault</tt> class encapsulates this.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-117' xml:id='l2h-117' class="class">Fault</tt></b>(</nobr></td>
+ <td><var>code, string</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+The <var>code</var> parameter is a text string identifying the SOAP fault
+code, a namespace-qualified name.
+The class attribute <code>Fault.Client</code> can be used to indicate a problem with
+an incoming message, <code>Fault.Server</code> can be used to
+indicate a problem occurred while processing the request, or <code>Fault.MU</code>
+can be used to indicate a problem with the SOAP <code>mustUnderstand</code>
+attribute.
+The <var>string</var> parameter is a human-readable text string describing the
+fault.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><var>actor</var></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >A string identifying the <code>actor</code>
+attribute that caused the problem (usually because it is unknown).</td></tr>
+ <tr><td class="left" valign="baseline"><var>detail</var></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >A sequence
+of elements to output in the <code>detail</code> element; it may also
+be a text string, in which case it is output as-is, and should
+therefore be XML text.</td></tr>
+ <tr><td class="left" valign="baseline"><var>headerdetail</var></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >Data, treated the same as
+the <code>detail</code> keyword, to be output in the SOAP header. See
+the following paragraph.</td></tr></tbody>
+</table></div>
+
+<P>
+If the fault occurred in the SOAP <code>Header</code>, the specification
+requires that the detail be sent back as an element within
+the SOAP <code>Header</code> element.
+Unfortunately, the SOAP specification does not describe how to encode
+this; <tt class="module">ZSI</tt> defines and uses a
+<code>ZSI:detail</code> element, which is analogous to the SOAP <code>detail</code>
+element.
+</dl>
+
+<P>
+The following attributes are read-only:
+
+<P>
+<dl><dt><b><tt id='l2h-118' xml:id='l2h-118' class="member">actor</tt></b></dt>
+<dd>
+A text string holding the value of the SOAP <code>faultactor</code> element.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-119' xml:id='l2h-119' class="member">code</tt></b></dt>
+<dd>
+A text string holding the value of the SOAP <code>faultcode</code> element.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-120' xml:id='l2h-120' class="member">detail</tt></b></dt>
+<dd>
+A text string or sequence of elements containing holding the value of the
+SOAP <code>detail</code> element, when available.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-121' xml:id='l2h-121' class="member">headerdetail</tt></b></dt>
+<dd>
+A text string or sequence of elements containing holding the value of the
+<tt class="module">ZSI</tt> header detail element, when available.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-122' xml:id='l2h-122' class="member">string</tt></b></dt>
+<dd>
+A text string holding the value of the SOAP <code>faultstring</code> element.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-123' xml:id='l2h-123' class="method">AsSOAP</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>, **kw</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This method serializes the <tt class="class">Fault</tt> object into a SOAP message.
+The message is returned as a string.
+Any keyword arguments are passed to the <tt class="class">SoapWriter</tt> constructor.
+
+<span class="versionnote">New in version 1.1; the old <tt class="method">AsSoap()</tt> method is still available.</span>
+
+</dl>
+
+<P>
+If other data is going to be sent with the fault, the following
+two methods can be used.
+Because some data might need to be output in the SOAP <code>Header</code>,
+serializing a fault is a two-step process.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-124' xml:id='l2h-124' class="method">DataForSOAPHeader</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+This method returns a text string that can be included as the
+<code>header</code> parameter for constructing a <tt class="class">SoapWriter</tt> object.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-125' xml:id='l2h-125' class="method">serialize</tt></b>(</nobr></td>
+ <td><var>sw</var>)</td></tr></table></dt>
+<dd>
+This method outputs the fault object onto the <var>sw</var> object, which is a
+<tt class="class">SoapWriter</tt> instance.
+</dl>
+
+<P>
+Some convenience functions are available to create a <tt class="class">Fault</tt>
+from common conditions.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-126' xml:id='l2h-126' class="function">FaultFromActor</tt></b>(</nobr></td>
+ <td><var>uri</var><big>[</big><var>, actor</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This function could be used when an application receives a message
+that has a SOAP <code>Header</code> element directed to an actor that
+cannot be processed.
+The <var>uri</var> parameter identifies the actor.
+The <var>actor</var> parameter can be used to specify a URI that identifies the
+application, if it is not the ultimate recipient of the SOAP message.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-127' xml:id='l2h-127' class="function">FaultFromException</tt></b>(</nobr></td>
+ <td><var>ex, inheader</var><big>[</big><var>,
+ tb</var><big>[</big><var>, actor</var><big>]</big><var></var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This function creates a <tt class="class">Fault</tt> from a general Python exception.
+A SOAP ``server'' fault is created.
+The <var>ex</var> parameter should be the Python exception.
+The <var>inheader</var> parameter should be true if the error was
+found on a SOAP <code>Header</code> element.
+The optional <var>tb</var> parameter may be a Python traceback
+object, as returned by "<tt class="samp">sys.exc_info()[2]</tt>".
+The <var>actor</var> parameter can be used to specify a URI that identifies the
+application, if it is not the ultimate recipient of the SOAP message.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-128' xml:id='l2h-128' class="function">FaultFromFaultMessage</tt></b>(</nobr></td>
+ <td><var>ps</var>)</td></tr></table></dt>
+<dd>
+This function creates a <tt class="class">Fault</tt> from a <tt class="class">ParsedSoap</tt> object
+passed in as <var>ps</var>.
+It should only be used if the <tt class="method">IsAFault()</tt> method returned true.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-129' xml:id='l2h-129' class="function">FaultFromNotUnderstood</tt></b>(</nobr></td>
+ <td><var>uri, localname,</var><big>[</big><var>, actor</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This function could be used when an application receives a message with
+the SOAP <code>mustUnderstand</code> attribute that it does not understand.
+The <var>uri</var> and <var>localname</var> parameters should identify
+the unknown element.
+The <var>actor</var> parameter can be used to specify a URI that identifies the
+application, if it is not the ultimate recipient of the SOAP message.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-130' xml:id='l2h-130' class="function">FaultFromZSIException</tt></b>(</nobr></td>
+ <td><var>ex</var><big>[</big><var>, actor</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This function creates a <tt class="class">Fault</tt> object from a <tt class="module">ZSI</tt> exception,
+<tt class="exception">ParseException</tt> or <tt class="exception">EvaluateException</tt>, passed in
+as <var>ex</var>.
+A SOAP ``client'' fault is created.
+The <var>actor</var> parameter can be used to specify a URI that identifies the
+application, if it is not the ultimate recipient of the SOAP message.
+</dl>
+
+<H1><A NAME="SECTION0010000000000000000000">
+9. The <tt class="module">resolvers</tt> module -- fetching remote data</A>
+</H1>
+
+<P>
+The <tt class="module">resolvers</tt> module provides some functions and classes
+that can be used as the <code>resolver</code> attribute for <tt class="class">TC.String</tt>
+or <tt class="class">TC.XML</tt> typecodes.
+They process an absolute URL, as described above, and return the
+content.
+Because the <tt class="module">resolvers</tt> module can import a number of other
+large modules, it must be imported directly, as in
+"<tt class="samp">from ZSI import resolvers</tt>".
+
+<P>
+These first two functions pass the URI directly to the <tt class="method">urlopen</tt>
+function in the <tt class="module">urllib</tt> module.
+Therefore, if used directly as resolvers, a client could direct the
+SOAP application to fetch any file on the network or local disk.
+Needless to say, this could pose a security risks.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-131' xml:id='l2h-131' class="function">Opaque</tt></b>(</nobr></td>
+ <td><var>uri, tc, ps</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This function returns the data contained at the specified <code>uri</code>
+as a Python string.
+Base-64 decoding will be done if necessary.
+The <code>tc</code> and <code>ps</code> parameters are ignored; the <code>keywords</code>
+are passed to the <tt class="method">urlopen</tt> method.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-132' xml:id='l2h-132' class="function">XML</tt></b>(</nobr></td>
+ <td><var>uri, tc, ps</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This function returns a list of the child element nodes of the XML
+document at the specified <code>uri</code>.
+The <code>tc</code> and <code>ps</code> parameters are ignored; the <code>keywords</code>
+are passed to the <tt class="method">urlopen</tt> method.
+</dl>
+
+<P>
+The <tt class="class">NetworkResolver</tt> class provides a simple-minded way to limit
+the URI's that will be resolved.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-133' xml:id='l2h-133' class="class">NetworkResolver</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>prefixes=None</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+The <code>prefixes</code> parameter is a list of strings defining the allowed
+prefixes of any URI's.
+If asked to fetch the content for a URI that does start with one of
+the prefixes, it will raise an exception.
+
+<P>
+In addition to <code>Opaque</code> and <code>XML</code> methods, this class
+provides a <code>Resolve</code> method that examines the typecode to determine
+what type of data is desired.
+</dl>
+
+<P>
+If the SOAP application is given a multi-part MIME document, the
+<tt class="class">MIMEResolver</tt> class can be used to process SOAP with Attachments.
+
+<P>
+The <tt class="class">MIMEResolver</tt> class will read the entire multipart MIME document,
+noting any <code>Content-ID</code> or <code>Content-Location</code> headers that appear
+on the headers of any of the message parts, and use them to resolve
+any <code>href</code> attributes that appear in the SOAP message.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-134' xml:id='l2h-134' class="class">MIMEResolver</tt></b>(</nobr></td>
+ <td><var>ct, f</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+The <code>ct</code> parameter is a string that contains the value of the
+MIME <code>Content-Type</code> header.
+The <code>f</code> parameter is the input stream, which should be positioned just
+after the message headers.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>seekable</code></td>
+ <td class="center"><code>0</code></td>
+ <td class="left" >Whether or not the input stream is
+seekable; passed to the constructor for the internal <tt class="class">multifile</tt>
+object.
+
+<span class="versionnote">Changed in version 2.0:
+default had been 1.</span>
+</td></tr>
+ <tr><td class="left" valign="baseline"><code>next</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >A resolver object that will be
+asked to resolve the URI if it is not found in the MIME document.
+
+<span class="versionnote">New in version 1.1.</span>
+</td></tr>
+ <tr><td class="left" valign="baseline"><code>uribase</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >The base URI to be used when
+resolving relative URI's; this will typically be the value of the
+<code>Content-Location</code> header, if present.
+
+<span class="versionnote">New in version 1.1.</span>
+</td></tr></tbody>
+</table></div>
+</dl>
+
+<P>
+In addition to to the <code>Opaque</code>, <code>Resolve</code>, and <code>XML</code> methods
+as described above, the following method is available:
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-135' xml:id='l2h-135' class="method">GetSOAPPart</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+This method returns a stream containing the SOAP message text.
+</dl>
+
+<P>
+The following attributes are read-only:
+
+<P>
+<dl><dt><b><tt id='l2h-136' xml:id='l2h-136' class="member">parts</tt></b></dt>
+<dd>
+An array of tuples, one for each MIME bodypart found.
+Each tuple has two elements, a <tt class="class">mimetools.Message</tt> object
+which contains the headers for the bodypart, and a
+<tt class="class">StringIO</tt> object containing the data.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-137' xml:id='l2h-137' class="member">id_dict</tt></b></dt>
+<dd>
+A dictionary whose keys are the values of any <code>Content-ID</code>
+headers, and whose value is the appropriate <code>parts</code> tuple.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-138' xml:id='l2h-138' class="member">loc_dict</tt></b></dt>
+<dd>
+A dictionary whose keys are the values of any <code>Content-Location</code>
+headers, and whose value is the appropriate <code>parts</code> tuple.
+</dl>
+
+<H1><A NAME="SECTION0011000000000000000000">
+10. Dispatching and Invoking</A>
+</H1>
+
+<P>
+
+<span class="versionnote">New in version 1.1.</span>
+
+<P>
+<tt class="module">ZSI</tt> is focused on parsing and generating SOAP messages, and provides
+limited facilities for dispatching to the appropriate message handler.
+This is because <tt class="module">ZSI</tt> works within many client and server environments,
+and the dispatching styles for these different environments can be
+very different.
+
+<P>
+Nevertheless, <tt class="module">ZSI</tt> includes some dispatch and invocation functions.
+To use them, they must be explicitly imported, as shown in the example
+at the start of this document.
+
+<P>
+The implementation (and names) of the these classes reflects the orientation
+of using SOAP for remote procedure calls (RPC).
+
+<P>
+Both client and server share a class that defines the mechanism a
+client uses to authenticate itself.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-139' xml:id='l2h-139' class="class">AUTH</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+This class defines constants used to identify how the client
+authenticated: <code>none</code> if no authentication was provided;
+<code>httpbasic</code> if HTTP basic authentication was used, or
+<code>zsibasic</code> if <tt class="module">ZSI</tt> basic authentication (see below)) was used.
+</dl>
+
+<P>
+The <tt class="module">ZSI</tt> schema (see the last chapter of this manual)
+defines a SOAP header element, <code>BasicAuth</code>, that
+contains a name and password.
+This is similar to the HTTP basic authentication header, except
+that it can be used independently from an HTTP transport.
+
+<P>
+
+<H1><A NAME="SECTION0011100000000000000000">
+10.1 Dispatching</A>
+</H1>
+
+<P>
+The <tt class="module">ZSI.dispatch</tt> module allows you to expose Python functions as a web
+service. The module provides the infrastructure to parse the request, dispatch
+to the appropriate handler, and then serialize any return value back to the
+client. The value returned by the function will be serialized back to the
+client. If an exception occurs, a SOAP fault will be sent back to the client.
+
+<P>
+
+<H2><A NAME="SECTION0011110000000000000000">
+10.1.1 Dispatch Behaviors</A>
+</H2> By default the callback is invoked with the
+pyobj representation of the body root element, and it is expected to return a
+self-describing request (w/typecode). Parsing is done via a typecode from
+typesmodule, or Any. Other keyword options are available in dispatch mechanisms
+(see below) that result in different behavior.
+
+<P>
+
+<H3><A NAME="SECTION0011111000000000000000">
+10.1.1.1 rpc</A>
+</H3> An rpc service will ignore the body root (RPC Wrapper) of
+the request, and parse all "parts" of message via individual typecodes. The
+callback function is expected to return the parts of the message in a dict or a
+list. The dispatch mechanism will try to serialize it as a Struct but if this
+is not possible it will be serialized as an Array. Parsing done via a typecode
+from typesmodule, or Any. Not compatible with <var>docstyle</var>.
+
+<P>
+
+<H3><A NAME="SECTION0011112000000000000000">
+10.1.1.2 docstyle</A>
+</H3> Callback is invoked with a ParsedSoap instance
+representing the request, and the return value is serialized with an XML
+typecode (DOM). The result in wrapped as an rpc-style message, with
+<em>Response</em> appended to the request wrapper. Not compatible with <var>rpc</var>.
+
+<P>
+
+<H2><A NAME="SECTION0011120000000000000000">
+10.1.2 Special Modules</A>
+</H2> These are keyword options available to all
+dispatch mechansism (see below).
+
+<P>
+
+<H3><A NAME="SECTION0011121000000000000000">
+10.1.2.1 modules</A>
+</H3>Dispatch is based solely on the name of the root element in the
+incoming SOAP request; the request URL is ignored. These modules will be search
+for a matching function. If no modules are specified, only the
+<tt class="module">__main__</tt> module will be searched.
+
+<P>
+
+<H3><A NAME="SECTION0011122000000000000000">
+10.1.2.2 typesmodule</A>
+</H3>Used for parsing. This module should contain class
+definitions with the <code>typecode</code> attribute set to a <tt class="class">TypeCode</tt>
+instance. By default, a class definition matching the root element name will be
+retrieved or the Any typecode will be used. If using <em>rpc</em>, each child of
+the root element will be used to retrieve a class definition of the same name.
+
+<P>
+
+<H2><A NAME="SECTION0011130000000000000000">
+10.1.3 Dispatch Mechanisms</A>
+</H2>
+Three dispatch mechanisms are provided: one supports standard CGI
+scripts, one runs a dedicated server based on the
+<tt class="module">BaseHTTPServer</tt> module, and the third uses the JonPY package,
+<a class="url" href="http://jonpy.sourceforge.net">http://jonpy.sourceforge.net</a>, to support FastCGI.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-140' xml:id='l2h-140' class="method">AsServer</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This creates a <tt class="class">HTTPServer</tt> object with a request handler that only
+supports the ``POST'' method.
+Dispatch is based solely on the name of the root element in the
+incoming SOAP request;
+the request URL is ignored.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>port</code></td>
+ <td class="center"><code>80</code></td>
+ <td class="left" >Port to listen on.</td></tr>
+ <tr><td class="left" valign="baseline"><code>addr</code></td>
+ <td class="center"><code>''</code></td>
+ <td class="left" >Address to listen on.</td></tr>
+ <tr><td class="left" valign="baseline"><code>docstyle</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >Exhibit the <em>docstyle</em> behavior.</td></tr>
+ <tr><td class="left" valign="baseline"><code>rpc</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >Exhibit the <em>rpc</em> behavior.</td></tr>
+ <tr><td class="left" valign="baseline"><code>modules</code></td>
+ <td class="center"><code>(__main__,)</code></td>
+ <td class="left" >List of modules containing
+functions that can be invoked.</td></tr>
+ <tr><td class="left" valign="baseline"><code>typesmodule</code></td>
+ <td class="center"><code>(__main__,)</code></td>
+ <td class="left" >This module is used for
+parsing, it contains class definitions that specify the <code>typecode</code>
+attribute.</td></tr>
+ <tr><td class="left" valign="baseline"><code>nsdict</code></td>
+ <td class="center"><code>{}</code></td>
+ <td class="left" >Namespace dictionary to send in the SOAP
+<code>Envelope</code></td></tr></tbody>
+</table></div>
+
+<P>
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-141' xml:id='l2h-141' class="method">AsCGI</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This method parses the CGI input and invokes a function that has the
+same name as the top-level SOAP request element.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>rpc</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >Exhibit the <em>rpc</em> behavior.</td></tr>
+ <tr><td class="left" valign="baseline"><code>modules</code></td>
+ <td class="center"><code>(__main__,)</code></td>
+ <td class="left" >List of modules containing
+functions that can be invoked.</td></tr>
+ <tr><td class="left" valign="baseline"><code>typesmodule</code></td>
+ <td class="center"><code>(__main__,)</code></td>
+ <td class="left" >This module is used for
+parsing, it contains class definitions that specify the <code>typecode</code>
+attribute.</td></tr>
+ <tr><td class="left" valign="baseline"><code>nsdict</code></td>
+ <td class="center"><code>{}</code></td>
+ <td class="left" >Namespace dictionary to send in the SOAP
+<code>Envelope</code></td></tr></tbody>
+</table></div>
+
+<P>
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-142' xml:id='l2h-142' class="method">AsHandler</tt></b>(</nobr></td>
+ <td><var>request=None</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+
+<P>
+This method is used within a JonPY handler to do dispatch.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>request</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >modpython HTTPRequest instance.</td></tr>
+ <tr><td class="left" valign="baseline"><code>modules</code></td>
+ <td class="center"><code>(__main__,)</code></td>
+ <td class="left" >List of modules containing
+functions that can be invoked.</td></tr>
+ <tr><td class="left" valign="baseline"><code>docstyle</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >Exhibit the <em>docstyle</em> behavior.</td></tr>
+ <tr><td class="left" valign="baseline"><code>rpc</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >Exhibit the <em>rpc</em> behavior.</td></tr>
+ <tr><td class="left" valign="baseline"><code>typesmodule</code></td>
+ <td class="center"><code>(__main__,)</code></td>
+ <td class="left" >This module is used for
+parsing, it contains class definitions that specify the <code>typecode</code>
+attribute.</td></tr>
+ <tr><td class="left" valign="baseline"><code>nsdict</code></td>
+ <td class="center"><code>{}</code></td>
+ <td class="left" >Namespace dictionary to send in the SOAP
+<code>Envelope</code></td></tr></tbody>
+</table></div>
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-143' xml:id='l2h-143' class="method">AsJonPy</tt></b>(</nobr></td>
+ <td><var>request=None</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+
+<P>
+This method is used within a JonPY handler to do dispatch.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>request</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >jonpy Request instance.</td></tr>
+ <tr><td class="left" valign="baseline"><code>modules</code></td>
+ <td class="center"><code>(__main__,)</code></td>
+ <td class="left" >List of modules containing
+functions that can be invoked.</td></tr>
+ <tr><td class="left" valign="baseline"><code>docstyle</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >Exhibit the <em>docstyle</em> behavior.</td></tr>
+ <tr><td class="left" valign="baseline"><code>rpc</code></td>
+ <td class="center"><code>False</code></td>
+ <td class="left" >Exhibit the <em>rpc</em> behavior.</td></tr>
+ <tr><td class="left" valign="baseline"><code>typesmodule</code></td>
+ <td class="center"><code>(__main__,)</code></td>
+ <td class="left" >This module is used for
+parsing, it contains class definitions that specify the <code>typecode</code>
+attribute.</td></tr>
+ <tr><td class="left" valign="baseline"><code>nsdict</code></td>
+ <td class="center"><code>{}</code></td>
+ <td class="left" >Namespace dictionary to send in the SOAP
+<code>Envelope</code></td></tr></tbody>
+</table></div>
+
+<P>
+The following code shows a sample use:
+
+<P>
+<div class="verbatim"><pre>
+import jon.fcgi
+from ZSI import dispatch
+import MyHandler
+
+class Handler(cgi.Handler):
+ def process(self, req):
+ dispatch.AsJonPy(modules=(MyHandler,), request=req)
+
+jon.fcgi.Server({jon.fcgi.FCGI_RESPONDER: Handler}).run()
+</pre></div>
+
+<P>
+</dl>
+
+<P>
+
+<H2><A NAME="SECTION0011140000000000000000">
+10.1.4 Other Dispatch Stuff</A>
+</H2>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-144' xml:id='l2h-144' class="method">GetClientBinding</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+More sophisticated scripts may want to use access the client binding object,
+which encapsulates all information about the client invoking the script.
+This function returns <code>None</code> or the binding information, an
+object of type <tt class="class">ClientBinding</tt>, described below.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-145' xml:id='l2h-145' class="class">ClientBinding</tt></b>(</nobr></td>
+ <td><var>...</var>)</td></tr></table></dt>
+<dd>
+This object contains information about the client.
+It is created internally by <tt class="module">ZSI</tt>.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-146' xml:id='l2h-146' class="method">GetAuth</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+This returns a tuple containing information about the client identity.
+The first element will be one of the constants from the <code>AUTH</code> class
+described above.
+For HTTP or <tt class="module">ZSI</tt> basic authentication, the next two elements will be
+the name and password provided by the client.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-147' xml:id='l2h-147' class="method">GetNS</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns the namespace URI that the client is using, or an empty string.
+This can be useful for versioning.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-148' xml:id='l2h-148' class="method">GetRequest</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns the <tt class="class">ParsedSoap</tt> object of the incoming request.
+</dl>
+
+<P>
+The following attribute is read-only:
+
+<P>
+<dl><dt><b><tt id='l2h-149' xml:id='l2h-149' class="member">environ</tt></b></dt>
+<dd>
+A dictionary of the environment variables.
+This is most useful when <tt class="method">AsCGI()</tt> is used.
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION0011200000000000000000">
+10.2 The <tt class="module">client</tt> module -- sending SOAP messages</A>
+</H1>
+
+<P>
+<tt class="module">ZSI</tt> includes a module to connect to a SOAP server over HTTP, send requests,
+and parse the response.
+It is built on the standard Python <tt class="module">httplib</tt> and <tt class="module">Cookie</tt>
+modules.
+It must be explicitly imported, as in
+"<tt class="samp">from ZSI.client import AUTH,Binding</tt>".
+
+<P>
+
+<H2><A NAME="SECTION0011210000000000000000">
+10.2.1 _Binding</A>
+</H2>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-150' xml:id='l2h-150' class="class">_Binding</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This class encapsulates a connection to a server, known as a <em>binding</em>.
+A single binding may be used for multiple RPC calls.
+Between calls, modifiers may be used to change the URL being posted to,
+etc.
+
+<P>
+Cookies are also supported; if a response comes back with a <code>Set-Cookie</code>
+header, it will be parsed and used in subsequent interactions.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>auth</code></td>
+ <td class="center"><code>(AUTH.none,)</code></td>
+ <td class="left" >A tuple with authentication
+ information; the first value should be one of the constants
+ from the <tt class="class">AUTH</tt> class.</td></tr>
+ <tr><td class="left" valign="baseline"><code>nsdict</code></td>
+ <td class="center"><code>{}</code></td>
+ <td class="left" >Namespace dictionary to send in the
+ SOAP <code>Envelope</code></td></tr>
+ <tr><td class="left" valign="baseline"><code>soapaction</code></td>
+ <td class="center"><code>''</code></td>
+ <td class="left" >Value for the
+ <code>SOAPAction</code> HTTP header.</td></tr>
+ <tr><td class="left" valign="baseline"><code>readerclass</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >Class used to create DOM-creating
+XML readers; see the description in the <tt class="class">ParsedSoap</tt> class.</td></tr>
+ <tr><td class="left" valign="baseline"><code>writerclass</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >ElementProxy Class used to create
+XML writers; see the description in the <tt class="class">SoapWriter</tt> class.</td></tr>
+ <tr><td class="left" valign="baseline"><code>tracefile</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >An object with a <code>write</code>
+ method, where packet traces will be recorded.</td></tr>
+ <tr><td class="left" valign="baseline"><code>transport</code></td>
+ <td class="center">HTTPConnection/HTTPSConnection</td>
+ <td class="left" >transport class</td></tr>
+ <tr><td class="left" valign="baseline"><code>transdict</code></td>
+ <td class="center">{}</td>
+ <td class="left" >keyword arguments for connection initialization</td></tr>
+ <tr><td class="left" valign="baseline"><code>url</code></td>
+ <td class="center">n/a</td>
+ <td class="left" >URL to post to.</td></tr>
+ <tr><td class="left" valign="baseline"><code>wsAddressURI</code></td>
+ <td class="center">None</td>
+ <td class="left" >URI, identifies the WS-Address specification
+to use. By default it's not used.</td></tr>
+ <tr><td class="left" valign="baseline"><code>sig_handler</code></td>
+ <td class="center">None</td>
+ <td class="left" >XML Signature handler, must sign and verify.</td></tr></tbody>
+</table></div>
+
+<P>
+If using SSL, the <code>cert_file</code> and <code>key_file</code> keyword parameters may
+also be used. For details see the documentation for the <tt class="module">httplib</tt>
+module.
+
+<P>
+</dl>
+
+<P>
+Once a <tt class="class">_Binding</tt> object has been created, the following modifiers are
+available. All of them return the binding object, so that multiple modifiers
+can be chained together.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-151' xml:id='l2h-151' class="method">AddHeader</tt></b>(</nobr></td>
+ <td><var>header, value</var>)</td></tr></table></dt>
+<dd>
+Output the specified <code>header</code> and <code>value</code> with the HTTP
+headers.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-152' xml:id='l2h-152' class="method">SetAuth</tt></b>(</nobr></td>
+ <td><var>style, name, password</var>)</td></tr></table></dt>
+<dd>
+The <code>style</code> should be one of the constants from the <code>AUTH</code>
+class described above.
+The remaining parameters will vary depending on the <code>style</code>.
+Currently only basic authentication data of name and password are
+supported.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-153' xml:id='l2h-153' class="method">SetNS</tt></b>(</nobr></td>
+ <td><var>uri</var>)</td></tr></table></dt>
+<dd>
+Set the default namespace for the request to the specified <code>uri</code>.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-154' xml:id='l2h-154' class="method">SetURL</tt></b>(</nobr></td>
+ <td><var>url</var>)</td></tr></table></dt>
+<dd>
+Set the URL where the post is made to <code>url</code>.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-155' xml:id='l2h-155' class="method">ResetHeaders</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Remove any headers that were added by <tt class="method">AddHeader()</tt>.
+</dl>
+
+<P>
+The following attribute may also be modified:
+
+<P>
+<dl><dt><b><tt id='l2h-156' xml:id='l2h-156' class="member">trace</tt></b></dt>
+<dd>
+If this attribute is not <code>None</code>, it should be an object with a
+<code>write</code> method, where packet traces will be recorded.
+</dl>
+
+<P>
+Once the necessary parameters have been specified (at a minimum, the URL
+must have been given in the constructor are through <code>SetURL</code>),
+invocations can be made.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-157' xml:id='l2h-157' class="method">RPC</tt></b>(</nobr></td>
+ <td><var>url, opname, pyobj, replytype=None</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This is the highest-level invocation method.
+It calls <tt class="method">Send()</tt> to send <code>pyobj</code> to the specified <code>url</code>
+to perform the <code>opname</code> operation,
+and calls <tt class="method">Receive()</tt> expecting to get a reply of the specified
+<code>replytype</code>.
+
+<P>
+This method will raise a <tt class="exception">TypeError</tt> if the response does not
+appear to be a SOAP message, or if is valid SOAP but contains a fault.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-158' xml:id='l2h-158' class="method">Send</tt></b>(</nobr></td>
+ <td><var>url, opname, pyboj</var><big>[</big><var>, **keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+This sends the specified <code>pyobj</code> to the specified <code>url</code>, invoking
+the <code>opname</code> method.
+The <code>url</code> can be <code>None</code> if it was specified in the <tt class="class">Binding</tt>
+constructor or if <code>SetURL</code> has been called.
+See below for a shortcut version of this method.
+
+<P>
+The following keyword arguments may be used:
+
+<P>
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>auth_header</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >String (containing presumably
+ serialized XML) to output as an authentication header.</td></tr>SOAP <code>Envelope</code>
+
+ <tr><td class="left" valign="baseline"><code>nsdict</code></td>
+ <td class="center"><code>{}</code></td>
+ <td class="left" >Namespace dictionary to send in the
+ SOAP <code>Envelope</code></td></tr>
+ <tr><td class="left" valign="baseline"><code>requesttypecode</code></td>
+ <td class="center">n/a</td>
+ <td class="left" >Typecode specifying how to serialize
+ the data.</td></tr>
+ <tr><td class="left" valign="baseline"><code>soapaction</code></td>
+ <td class="center">Obtained from the <tt class="class">Binding</tt></td>
+ <td class="left" >Value for the
+ <code>SOAPAction</code> HTTP header.</td></tr></tbody>
+</table></div>
+
+<P>
+</dl>
+
+<P>
+Methods are available to determine the type of response that came back:
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-159' xml:id='l2h-159' class="method">IsSOAP</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns true if the message appears to be a SOAP message.
+(Some servers return an HTML page under certain error conditions.)
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-160' xml:id='l2h-160' class="method">IsAFault</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns true if the message is a SOAP fault.
+</dl>
+
+<P>
+Having determined the type of the message (or, more likely, assuming
+it was good and catching an exception if not), the following methods
+are available to actually parse the data.
+They will continue to return the same value until
+another message is sent.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-161' xml:id='l2h-161' class="method">ReceiveRaw</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns the unparsed message body.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-162' xml:id='l2h-162' class="method">ReceiveSoap</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns a <tt class="class">ParsedSOAP</tt> object containing the parsed message.
+Raises a <tt class="exception">TypeError</tt> if the message wasn't SOAP.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-163' xml:id='l2h-163' class="method">ReceiveFault</tt></b>(</nobr></td>
+ <td><var></var>)</td></tr></table></dt>
+<dd>
+Returns a <tt class="class">Fault</tt> object containing the SOAP fault message.
+Raises a <tt class="exception">TypeError</tt> if the message did not contain a fault.
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-164' xml:id='l2h-164' class="method">Receive</tt></b>(</nobr></td>
+ <td><var>replytype=None</var>)</td></tr></table></dt>
+<dd>
+Parses a SOAP message.
+The <code>replytype</code> specifies how to parse the data.
+If it s <code>None</code>, dynamic parsing will be used, usually resulting
+in a Python list.
+If <code>replytype</code> is a Python class, then the class's <code>typecode</code>
+attribute will be used, otherwise <code>replytype</code> is taken to be
+the typecode to use for parsing the data.
+</dl>
+
+<P>
+Once a reply has been parsed (or its type examined), the following
+read-only attributes are available.
+Their values will remain unchanged until another reply is parsed.
+
+<P>
+<dl><dt><b><tt id='l2h-165' xml:id='l2h-165' class="member">reply_code</tt></b></dt>
+<dd>
+The HTTP reply code, a number.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-166' xml:id='l2h-166' class="member">reply_headers</tt></b></dt>
+<dd>
+The HTTP headers, as a <tt class="class">mimetools</tt> object.
+</dl>
+
+<P>
+<dl><dt><b><tt id='l2h-167' xml:id='l2h-167' class="member">reply_msg</tt></b></dt>
+<dd>
+A text string containing the HTTP reply text.
+</dl>
+
+<P>
+
+<H2><A NAME="SECTION0011220000000000000000">
+10.2.2 Binding</A>
+</H2>
+If an attribute is fetched other than one of those described in
+<tt class="class">_Binding</tt>, it is taken to be the <code>opname</code> of a remote procedure, and
+a callable object is returned. This object dynamically parses its arguments,
+receives the reply, and parses that.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-168' xml:id='l2h-168' class="class">Binding</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+For other keyword arguments see <tt class="class">_Binding</tt>.
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>typesmodule</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >See explanation in Dispatching</td></tr></tbody>
+</table></div>
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-169' xml:id='l2h-169' class="method">opname</tt></b>(</nobr></td>
+ <td><var>*args</var>)</td></tr></table></dt>
+<dd>
+Using this shortcut requires that the <var>url</var> attribute is set, either
+throught the constructor or <tt class="method">SetURL()</tt>.
+</dl>
+
+<P>
+
+<H2><A NAME="SECTION0011230000000000000000">
+10.2.3 NamedParamBinding</A>
+</H2>
+If an attribute is fetched other than one of those described
+in <tt class="class">_Binding</tt>, it is taken to be the <code>opname</code> of a remote procedure, and a callable
+object is returned. This object dynamically parses its arguments, receives the
+reply, and parses that.
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><span class="typelabel">class</span>&nbsp;<tt id='l2h-170' xml:id='l2h-170' class="class">NamedParamBinding</tt></b>(</nobr></td>
+ <td><var></var><big>[</big><var>**keywords</var><big>]</big><var></var>)</td></tr></table></dt>
+<dd>
+For other keyword arguments see <tt class="class">_Binding</tt>.
+<div class="center"><table class="realtable">
+ <thead>
+ <tr>
+ <th class="left" >Keyword</th>
+ <th class="center">Default</th>
+ <th class="left" >Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td class="left" valign="baseline"><code>typesmodule</code></td>
+ <td class="center"><code>None</code></td>
+ <td class="left" >See explanation in Dispatching</td></tr></tbody>
+</table></div>
+</dl>
+
+<P>
+<dl><dt><table cellpadding="0" cellspacing="0"><tr valign="baseline">
+ <td><nobr><b><tt id='l2h-171' xml:id='l2h-171' class="method">opname</tt></b>(</nobr></td>
+ <td><var>**kwargs</var>)</td></tr></table></dt>
+<dd>
+Using this shortcut requires that the <var>url</var> attribute is set, either
+throught the constructor or <tt class="method">SetURL()</tt>.
+</dl>
+
+<P>
+
+<H1><A NAME="SECTION0012000000000000000000">
+11. Bibliography</A>
+</H1>
+
+<H2><A NAME="SECTION0013000000000000000000">
+Bibliography</A>
+</H2><DL COMPACT><DD><P></P><DT><A NAME="ref1">1</A>
+<DD> This is the first item in the Bibliography.
+<P></P><DT><A NAME="ref2">2</A>
+<DD> This is the second item in the Bibliography.
+</DL>
+
+<H1><A NAME="SECTION0014000000000000000000">
+A. CGI Script Array</A>
+</H1>
+
+<P>
+
+<H1><A NAME="SECTION0014100000000000000000">
+A.1 Intro</A>
+</H1> This is an example of a simple web service CGI Script. The
+service returns and expects SOAP Arrays (python <tt class="class">list</tt>). A sample soap
+trace is provided below. In this example the CGI script is dispatched as a
+<em>rpc</em> service.
+
+<P>
+
+<H2><A NAME="SECTION0014110000000000000000">
+A.1.1 rpc wrapper</A>
+</H2> The wrapper element of the request is the dispatch key
+to the callback function, the child elements are passes as a <tt class="class">list</tt> or
+<tt class="class">dict</tt> of values to the callback function. The callback function is
+expected to return a <tt class="class">list</tt> or <tt class="class">dict</tt> of values, the response
+wrapper is by default set to the request wrapper name appended <em>Response</em>.
+
+<P>
+
+<H1><A NAME="SECTION0014200000000000000000">
+A.2 CGI Script</A>
+</H1>
+
+<P>
+<div class="verbatim"><pre>
+#!/usr/local/bin/python2.4
+# SOAP Array
+
+def hello():
+ return ["Hello, world"]
+
+def echo(*args):
+ return args
+
+def sum(*args):
+ sum = 0
+ for i in args: sum += i
+ return [sum]
+
+def average(*args):
+ return [sum(*args) / len(args)]
+
+from ZSI import dispatch
+dispatch.AsCGI(rpc=True)
+</pre></div>
+
+<P>
+
+<H1><A NAME="SECTION0014300000000000000000">
+A.3 client test script</A>
+</H1>
+<div class="verbatim"><pre>
+#!/usr/bin/env python
+# client.py
+import sys
+from ZSI.client import Binding
+b = Binding(url='http://127.0.0.1/cgi-bin/simple', tracefile=sys.stdout)
+print b.hello()
+try:
+ print b.hello(1)
+except Exception, ex:
+ print "Fault: ", ex
+
+print b.echo("whatever", "hi", 1, 2)
+print b.sum(*[2*i for i in range(5)])
+print b.average(*[2*i for i in range(5)])
+</pre></div>
+
+<P>
+
+<H1><A NAME="SECTION0014400000000000000000">
+A.4 SOAP Trace</A>
+</H1>
+
+<H2><A NAME="SECTION0014410000000000000000">
+A.4.1 hello</A>
+</H2>
+<div class="verbatim"><pre>
+$ ./client.py
+Hello: _________________________________ Wed Oct 4 17:36:33 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;hello SOAP-ENC:arrayType="xsd:anyType[0]"&gt;&lt;/hello&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+
+_________________________________ Wed Oct 4 17:36:34 2006 RESPONSE:
+200
+
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;helloResponse SOAP-ENC:arrayType="xsd:anyType[1]"&gt;
+ &lt;element id="o671b0" xsi:type="xsd:string"&gt;Hello, world&lt;/element&gt;
+ &lt;/helloResponse&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+[u'Hello, world']
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION0014420000000000000000">
+A.4.2 hello fault</A>
+</H2>
+<div class="verbatim"><pre>
+_________________________________ Wed Oct 4 17:36:34 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;hello SOAP-ENC:arrayType="xsd:anyType[1]"&gt;
+ &lt;element id="o1803988" xsi:type="xsd:int"&gt;1&lt;/element&gt;
+ &lt;/hello&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+_________________________________ Wed Oct 4 17:36:35 2006 RESPONSE:
+500
+
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;SOAP-ENV:Fault&gt;
+ &lt;faultcode&gt;SOAP-ENV:Server&lt;/faultcode&gt;
+ &lt;faultstring&gt;Processing Failure&lt;/faultstring&gt;
+ &lt;detail&gt;
+ &lt;ZSI:FaultDetail&gt;
+&lt;ZSI:string&gt;exceptions:TypeError hello() takes no arguments (1 given)&lt;/ZSI:string&gt;
+&lt;ZSI:trace&gt;build/bdist.darwin-8.8.0-Power_Macintosh/egg/ZSI/dispatch.py:86:_Dispatch&lt;/ZSI:trace&gt;
+ &lt;/ZSI:FaultDetail&gt;
+ &lt;/detail&gt;
+ &lt;/SOAP-ENV:Fault&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+
+Fault: Processing Failure
+exceptions:TypeError
+hello() takes no arguments (1 given)
+[trace: build/bdist.darwin-8.8.0-Power_Macintosh/egg/ZSI/dispatch.py:86:_Dispatch]
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION0014430000000000000000">
+A.4.3 echo</A>
+</H2>
+<div class="verbatim"><pre>
+_________________________________ Wed Oct 4 17:36:35 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;echo SOAP-ENC:arrayType="xsd:anyType[4]"&gt;
+ &lt;element id="o644c0" xsi:type="xsd:string"&gt;whatever&lt;/element&gt;
+ &lt;element id="o644e0" xsi:type="xsd:string"&gt;hi&lt;/element&gt;
+ &lt;element id="o1803988" xsi:type="xsd:int"&gt;1&lt;/element&gt;
+ &lt;element id="o180397c" xsi:type="xsd:int"&gt;2&lt;/element&gt;
+ &lt;/echo&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+_________________________________ Wed Oct 4 17:36:36 2006 RESPONSE:
+200
+
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;echoResponse SOAP-ENC:arrayType="xsd:anyType[4]"&gt;
+ &lt;element id="o4f4290" xsi:type="xsd:string"&gt;whatever&lt;/element&gt;
+ &lt;element id="o4f4338" xsi:type="xsd:string"&gt;hi&lt;/element&gt;
+ &lt;element id="o1803988" xsi:type="xsd:int"&gt;1&lt;/element&gt;
+ &lt;element id="o180397c" xsi:type="xsd:int"&gt;2&lt;/element&gt;
+ &lt;/echoResponse&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+
+[u'whatever', u'hi', 1, 2]
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION0014440000000000000000">
+A.4.4 sum</A>
+</H2>
+<div class="verbatim"><pre>
+_________________________________ Wed Oct 4 17:36:36 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;sum SOAP-ENC:arrayType="xsd:anyType[5]"&gt;
+ &lt;element id="o1803994" xsi:type="xsd:int"&gt;0&lt;/element&gt;
+ &lt;element id="o180397c" xsi:type="xsd:int"&gt;2&lt;/element&gt;
+ &lt;element id="o1803964" xsi:type="xsd:int"&gt;4&lt;/element&gt;
+ &lt;element id="o180394c" xsi:type="xsd:int"&gt;6&lt;/element&gt;
+ &lt;element id="o1803934" xsi:type="xsd:int"&gt;8&lt;/element&gt;
+ &lt;/sum&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+_________________________________ Wed Oct 4 17:36:37 2006 RESPONSE:
+200
+
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;sumResponse SOAP-ENC:arrayType="xsd:anyType[1]"&gt;
+ &lt;element id="o18038a4" xsi:type="xsd:int"&gt;20&lt;/element&gt;
+ &lt;/sumResponse&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+[20]
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION0014450000000000000000">
+A.4.5 average</A>
+</H2>
+<div class="verbatim"><pre>
+_________________________________ Wed Oct 4 17:36:37 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;average SOAP-ENC:arrayType="xsd:anyType[5]"&gt;
+ &lt;element id="o1803994" xsi:type="xsd:int"&gt;0&lt;/element&gt;
+ &lt;element id="o180397c" xsi:type="xsd:int"&gt;2&lt;/element&gt;
+ &lt;element id="o1803964" xsi:type="xsd:int"&gt;4&lt;/element&gt;
+ &lt;element id="o180394c" xsi:type="xsd:int"&gt;6&lt;/element&gt;
+` &lt;element id="o1803934" xsi:type="xsd:int"&gt;8&lt;/element&gt;
+ &lt;/average&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+_________________________________ Wed Oct 4 17:36:38 2006 RESPONSE:
+200
+
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;averageResponse SOAP-ENC:arrayType="xsd:anyType[1]"&gt;
+ &lt;element id="o1803964" xsi:type="xsd:int"&gt;4&lt;/element&gt;
+ &lt;/averageResponse&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+[4]
+</pre></div>
+<H1><A NAME="SECTION0015000000000000000000">
+B. CGI Script Struct</A>
+</H1>
+
+<P>
+
+<H1><A NAME="SECTION0015100000000000000000">
+B.1 Intro</A>
+</H1> This is an example of a simple web service CGI Script. The service
+returns and expects SOAP Structs (python <tt class="class">dict</tt>). A sample soap trace is
+provided below. In this example the CGI script is dispatched as a <em>rpc</em>
+service.
+
+<P>
+
+<H2><A NAME="SECTION0015110000000000000000">
+B.1.1 rpc wrapper</A>
+</H2> The wrapper element of the request is the dispatch key
+to the callback function, the child elements are passes as a <tt class="class">list</tt> or
+<tt class="class">dict</tt> of values to the callback function. The callback function is
+expected to return a <tt class="class">list</tt> or <tt class="class">dict</tt> of values, the response
+wrapper is by default set to the request wrapper name appended <em>Response</em>.
+
+<P>
+
+<H1><A NAME="SECTION0015200000000000000000">
+B.2 CGI Script</A>
+</H1>
+<div class="verbatim"><pre>
+#!/usr/local/bin/python2.4
+# SOAP Struct
+
+def hello():
+ return {"value":"Hello, world"}
+
+def echo(**kw):
+ return kw
+
+def sum(**kw):
+ sum = 0
+ for i in kw.values(): sum += i
+ return {"value":sum}
+
+def average(**kw):
+ d = sum(**kw)
+ return d["value"] = d["value"]/len(kw)
+
+from ZSI import dispatch
+dispatch.AsCGI(rpc=True)
+</pre></div>
+
+<P>
+
+<H1><A NAME="SECTION0015300000000000000000">
+B.3 client test script</A>
+</H1>
+<div class="verbatim"><pre>
+#!/usr/bin/env python
+import sys,time
+from ZSI.client import NamedParamBinding as NPBinding
+
+b = NPBinding(url='http://127.0.0.1/cgi-bin/soapstruct', tracefile=sys.stdout)
+print "Hello: ", b.hello()
+print "Echo: ", b.echo(name="josh", year=2006, pi=3.14, time=time.gmtime())
+print "Sum: ", b.sum(one=1, two=2, three=3)
+print "Average: ", b.average(one=100, two=200, three=300, four=400)
+</pre></div>
+
+<P>
+
+<H1><A NAME="SECTION0015400000000000000000">
+B.4 SOAP Trace</A>
+</H1>
+
+<H2><A NAME="SECTION0015410000000000000000">
+B.4.1 hello</A>
+</H2>
+<div class="verbatim"><pre>
+</pre></div>
+
+<H1><A NAME="SECTION0016000000000000000000">
+C. Complete Low Level Example</A>
+</H1>
+
+<P>
+
+<H1><A NAME="SECTION0016100000000000000000">
+C.1 Intro</A>
+</H1> This is a complete example of using the low level soap utilities
+in <tt class="module">ZSI</tt> to implement a web service.
+
+<P>
+
+<H1><A NAME="SECTION0016200000000000000000">
+C.2 code</A>
+</H1>
+
+<H2><A NAME="SECTION0016210000000000000000">
+C.2.1 httpserver script</A>
+</H2> Minimal http server example, opens up a new
+process to do the SOAP processing.
+<div class="verbatim"><pre>
+#!/usr/bin/env python
+# file: httpserver.py
+import os
+from subprocess import Popen, PIPE
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+
+class RequestHandler(BaseHTTPRequestHandler):
+ def do_POST(self):
+ length = int(self.headers['content-length'])
+ xml_in = self.rfile.read(length)
+ p = Popen(os.path.join(os.path.curdir, 'player.py'),
+ shell=True, stdin=PIPE, stdout=PIPE)
+
+ (stdout, stderr) = p.communicate(xml_in)
+ code = 200
+ if stdout.find('Fault') &gt;= 0: code = 500
+ self.send_response(code)
+ self.send_header('Content-type', 'text/xml; charset="utf-8"')
+ self.send_header('Content-Length', str(len(stdout)))
+ self.end_headers()
+ self.wfile.write(stdout)
+ self.wfile.flush()
+
+if __name__ == '__main__':
+ server = HTTPServer(('localhost', 8080), RequestHandler)
+ server.serve_forever()
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION0016220000000000000000">
+C.2.2 typecode module</A>
+</H2>
+<div class="verbatim"><pre>
+# file: typecode.py
+# CHECK PYTHONPATH: Must be able to import
+class Player:
+ def __init__(self, *args):
+ if not len(args): return
+ self.Name = args[0]
+ self.Scores = args[1:]
+Player.typecode = TC.Struct(Player, [
+ TC.String('Name'),
+ TC.Array('Integer', TC.Integer(), 'Scores', undeclared=True),
+ ], 'GetAverage')
+class Average:
+ def __init__(self, average=None):
+ self.average = average
+Average.typecode = TC.Struct(Average, [
+ TC.Integer('average'),
+ ], 'GetAverageResponse')
+</pre></div>
+<H2><A NAME="SECTION0016230000000000000000">
+C.2.3 player script</A>
+</H2>
+<div class="verbatim"><pre>
+#!/usr/bin/env python
+# file: player.py
+from ZSI import *
+import sys
+IN, OUT = sys.stdin, sys.stdout
+try:
+ ps = ParsedSoap(IN)
+except ParseException, e:
+ OUT.write(FaultFromZSIException(e).AsSOAP())
+ sys.exit(1)
+except Exception, e:
+ # Faulted while processing; we assume it's in the header.
+ OUT.write(FaultFromException(e, 1).AsSOAP())
+ sys.exit(1)
+
+# We are not prepared to handle any actors or mustUnderstand elements,
+# so we'll arbitrarily fault back with the first one we found.
+a = ps.WhatActorsArePresent()
+if len(a):
+ OUT.write(FaultFromActor(a[0]).AsSOAP())
+ sys.exit(1)
+mu = ps.WhatMustIUnderstand()
+if len(mu):
+ uri, localname = mu[0]
+ OUT.write(FaultFromNotUnderstood(uri, localname).AsSOAP())
+ sys.exit(1)
+
+from typecode import Player, Average
+try:
+ player = ps.Parse(Player.typecode)
+except EvaluateException, e:
+ OUT.write(FaultFromZSIException(e).AsSOAP())
+ sys.exit(1)
+
+try:
+ total = 0
+ for value in player.Scores: total = total + value
+ result = Average(total / len(player.Scores))
+ sw = SoapWriter()
+ sw.serialize(result, Average.typecode)
+ sw.close()
+ OUT.write(str(sw))
+except Exception, e:
+ OUT.write(FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP())
+ sys.exit(1)
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION0016240000000000000000">
+C.2.4 client test script</A>
+</H2>
+<div class="verbatim"><pre>
+#!/usr/bin/env python2.4
+#file: client.py
+from ZSI import *
+from ZSI.wstools.Namespaces import SCHEMA
+from typecode import Player, Average
+
+if __name__ == '__main__':
+ import sys
+ from ZSI.client import Binding
+ b = Binding(url='http://localhost:8080', tracefile=sys.stdout)
+ pyobj = b.RPC(None, None, Player("Josh",10,20,30), replytype=Average)
+ print pyobj
+ print pyobj.__dict__
+</pre></div>
+
+<P>
+
+<H1><A NAME="SECTION0016300000000000000000">
+C.3 SOAP Trace</A>
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION0016310000000000000000">
+C.3.1 GetAverage</A>
+</H2>
+<div class="verbatim"><pre>
+$./client.py
+_________________________________ Thu Oct 5 14:57:39 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;GetAverage&gt;
+ &lt;Name xsi:type="xsd:string"&gt;Josh&lt;/Name&gt;
+ &lt;Scores&gt;
+ &lt;element&gt;10&lt;/element&gt;
+ &lt;element&gt;20&lt;/element&gt;
+ &lt;element&gt;30&lt;/element&gt;
+ &lt;/Scores&gt;
+ &lt;/GetAverage&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+_________________________________ Thu Oct 5 14:57:39 2006 RESPONSE:
+200
+OK
+-------
+Server: BaseHTTP/0.3 Python/2.5
+Date: Thu, 05 Oct 2006 21:57:39 GMT
+Content-type: text/xml; charset="utf-8"
+Content-Length: 431
+
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;GetAverageResponse&gt;
+ &lt;average&gt;20&lt;/average&gt;
+ &lt;/GetAverageResponse&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+
+&lt;__main__.Average instance at 0x5f9760&gt;
+{'average': 20}
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION0016320000000000000000">
+C.3.2 fault</A>
+</H2> Purposely send a incorrect <em>Nae</em> element for the
+<em>Name</em>.
+<div class="verbatim"><pre>
+$./client.py
+_________________________________ Thu Oct 5 14:33:25 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;GetAverage&gt;
+ &lt;Nae xsi:type="xsd:string"&gt;Josh&lt;/Nae&gt;
+ &lt;Scores&gt;
+ &lt;element&gt;10&lt;/element&gt;
+ &lt;element&gt;20&lt;/element&gt;
+ &lt;element&gt;30&lt;/element&gt;
+ &lt;/Scores&gt;
+ &lt;/GetAverage&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+
+_________________________________ Thu Oct 5 14:33:26 2006 RESPONSE:
+500
+Internal Server Error
+
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;SOAP-ENV:Fault&gt;
+ &lt;faultcode&gt;SOAP-ENV:Client&lt;/faultcode&gt;
+ &lt;faultstring&gt;Unparseable message&lt;/faultstring&gt;
+ &lt;detail&gt;&lt;Eoe440&gt;&amp;lt;ZSI:ParseFaultDetail&amp;gt;
+&amp;lt;ZSI:string&amp;gt;Element "Name" missing from complexType&amp;lt;/ZSI:string&amp;gt;
+&amp;lt;ZSI:trace&amp;gt;/SOAP-ENV:Envelope/SOAP-ENV:Body/GetAverage&amp;lt;/ZSI:trace&amp;gt;
+&amp;lt;/ZSI:ParseFaultDetail&amp;gt;&lt;/Eoe440&gt;&lt;/detail&gt;
+ &lt;/SOAP-ENV:Fault&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+Traceback (most recent call last):
+ File "./player_client.py", line 25, in ?
+ pyobj = b.RPC(None, None, Player("Josh",10,20,30), replytype=Average)
+ File "/private/var/www/htdocs/guide/client.py", line 176, in RPC
+
+ File "/private/var/www/htdocs/guide/client.py", line 420, in Receive
+
+ZSI.FaultException: Unparseable message
+&lt;Element Node at 5f9f58: Name='Eoe440' with 0 attributes and 1 children&gt;
+</pre></div>
+
+<H1><A NAME="SECTION0017000000000000000000">
+D. pickler example</A>
+</H1>
+
+<P>
+
+<H1><A NAME="SECTION0017100000000000000000">
+D.1 Intro</A>
+</H1> This is an example of a stateful mod_python web service.
+
+<P>
+
+<H1><A NAME="SECTION0017200000000000000000">
+D.2 code</A>
+</H1>
+
+<H2><A NAME="SECTION0017210000000000000000">
+D.2.1 typecode module</A>
+</H2> Module containing complex type typecode.
+<div class="verbatim"><pre>
+# Complex type definition
+from ZSI import *
+class Person:
+ def __init__(self, name=None, age=0):
+ self.name = name
+ self.age = age
+
+Person.typecode = TC.Struct(Person,
+ [TC.String('name'),
+ TC.InonNegativeInteger('age')],
+ pname=('urn:MyApp','Person'))
+</pre></div>
+<H2><A NAME="SECTION0017220000000000000000">
+D.2.2 pickler script</A>
+</H2> Configure appache to use this script with mod_python
+PythonHandler.
+<div class="verbatim"><pre>
+# pickler.py
+import pickle, new
+from mod_python import apache
+from ZSI import dispatch
+import MyComplexTypes
+
+# my web service that returns a complex structure
+def getPerson(name=None):
+ #fp = open('/tmp/%s.person.pickle'%Person.name, 'r')
+ fp = open('/tmp/%s.person.pickle'%name, 'r')
+ #return pickle.load(fp)
+ p = pickle.load(fp)
+ print "PERSON: ", p
+ print "typecode: ", p.typecode
+ return p
+
+# my web service that accepts a complex structure
+def savePerson(Person):
+ print "PERSON: ", Person
+ fp = open('/tmp/%s.person.pickle'%Person.name, 'w')
+ pickle.dump(Person, fp)
+ fp.close()
+ return {}
+
+mod = __import__('encodings.utf_8', globals(), locals(), '*')
+mod = __import__('encodings.utf_16_be', globals(), locals(), '*')
+
+
+handles = new.module('handles')
+handles.getPerson = getPerson
+handles.savePerson = savePerson
+def handler(req):
+ dispatch.AsHandler(modules=(handles,), request=req, typesmodule=MyComplexTypes, rpc=True)
+ return apache.OK
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION0017230000000000000000">
+D.2.3 client: invoke savePerson</A>
+</H2>
+
+<H3><A NAME="SECTION0017231000000000000000">
+D.2.3.1 script</A>
+</H3>
+<div class="verbatim"><pre>
+import sys
+from ZSI.client import Binding
+from MyComplexTypes import Person
+
+b = Binding(url='http://localhost/test3/pickler.py', tracefile=sys.stdout)
+person = Person('christopher', 26)
+b.savePerson(person)
+</pre></div>
+
+<P>
+
+<H3><A NAME="SECTION0017232000000000000000">
+D.2.3.2 SOAP Trace</A>
+</H3>
+<div class="verbatim"><pre>
+_________________________________ Wed Oct 11 13:10:05 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;savePerson xmlns:ns1="urn:MyApp"&gt;
+ &lt;ns1:Person&gt;&lt;name xsi:type="xsd:string"&gt;christopher&lt;/name&gt;
+ &lt;age xsi:type="xsd:nonNegativeInteger"&gt;26&lt;/age&gt;
+ &lt;/ns1:Person&gt;
+ &lt;/savePerson&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+
+_________________________________ Wed Oct 11 13:10:05 2006 RESPONSE:
+Server: Apache/2.0.53-dev (Unix) mod_ruby/1.2.4 Ruby/1.8.2(2004-12-25)
+mod_python/3.1.4 Python/2.4.1
+Transfer-Encoding: chunked
+Content-Type: text/xml
+
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;savePersonResponse&gt;&lt;/savePersonResponse&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+</pre></div>
+
+<P>
+
+<H2><A NAME="SECTION0017240000000000000000">
+D.2.4 client: invoke getPerson 3 different ways</A>
+</H2>
+
+<H3><A NAME="SECTION0017241000000000000000">
+D.2.4.1 script</A>
+</H3>
+<div class="verbatim"><pre>
+import sys
+import MyComplexTypes
+from ZSI.client import NamedParamBinding as NPBinding, Binding
+from ZSI import TC
+
+kw = {'url':'http://localhost/test3/pickler.py', 'tracefile':sys.stdout}
+b = NPBinding(**kw)
+rsp = b.getPerson(name='christopher')
+assert type(rsp) is dict, 'expecting a dict'
+assert rsp['Person']['name'] == 'christopher', 'wrong person'
+
+b = NPBinding(typesmodule=MyComplexTypes, **kw)
+rsp = b.getPerson(name='christopher')
+assert isinstance(rsp['Person'], MyComplexTypes.Person), (
+ 'expecting instance of %s' %MyComplexTypes.Person)
+
+b = Binding(typesmodule=MyComplexTypes, **kw)
+class Name(str):
+ typecode = TC.String("name")
+
+rsp = b.getPerson(Name('christopher'))
+assert isinstance(rsp['Person'], MyComplexTypes.Person), (
+ 'expecting instance of %s' %MyComplexTypes.Person)
+</pre></div>
+
+<P>
+
+<H3><A NAME="SECTION0017242000000000000000">
+D.2.4.2 SOAP Trace</A>
+</H3> All responses are exactly the same, for comparison
+the three requests are presented first and only the last response is included.
+<div class="verbatim"><pre>
+_________________________________ Wed Oct 11 13:19:00 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;getPerson&gt;
+ &lt;name id="o6c2a0" xsi:type="xsd:string"&gt;christopher&lt;/name&gt;
+ &lt;/getPerson&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+
+** OMIT RESPONSE **
+
+_________________________________ Wed Oct 11 13:19:00 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;getPerson&gt;
+ &lt;name id="o6c2a0" xsi:type="xsd:string"&gt;christopher&lt;/name&gt;
+ &lt;/getPerson&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+
+** OMIT RESPONSE **
+
+_________________________________ Wed Oct 11 13:19:00 2006 REQUEST:
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;getPerson&gt;
+ &lt;name xsi:type="xsd:string"&gt;christopher&lt;/name&gt;
+ &lt;/getPerson&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+
+_________________________________ Wed Oct 11 13:19:00 2006 RESPONSE:
+Server: Apache/2.0.53-dev (Unix) mod_ruby/1.2.4 Ruby/1.8.2(2004-12-25)
+mod_python/3.1.4 Python/2.4.1
+Transfer-Encoding: chunked
+Content-Type: text/xml
+
+&lt;SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
+ &lt;SOAP-ENV:Header&gt;&lt;/SOAP-ENV:Header&gt;
+ &lt;SOAP-ENV:Body&gt;
+ &lt;getPersonResponse xmlns:ns1="urn:MyApp"&gt;
+ &lt;ns1:Person&gt;
+ &lt;name xsi:type="xsd:string"&gt;christopher&lt;/name&gt;
+ &lt;age xsi:type="xsd:nonNegativeInteger"&gt;26&lt;/age&gt;
+ &lt;/ns1:Person&gt;
+ &lt;/getPersonResponse&gt;
+ &lt;/SOAP-ENV:Body&gt;
+&lt;/SOAP-ENV:Envelope&gt;
+</pre></div>
+
+<H1><A NAME="SECTION0018000000000000000000">
+About this document ...</A>
+</H1>
+ <strong>ZSI: The Zolera Soap Infrastructure
+<BR> Developer's Guide</strong>,
+October 25, 2006, Release 2.0.0
+<p> This document was generated using the <a
+ href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/">
+ <strong>LaTeX</strong>2<tt>HTML</tt></a> translator.
+</p>
+
+<p> <a
+ href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/">
+ <strong>LaTeX</strong>2<tt>HTML</tt></a> is Copyright &copy;
+ 1993, 1994, 1995, 1996, 1997, <a
+ href="http://cbl.leeds.ac.uk/nikos/personal.html">Nikos
+ Drakos</a>, Computer Based Learning Unit, University of
+ Leeds, and Copyright &copy; 1997, 1998, <a
+ href="http://www.maths.mq.edu.au/~ross/">Ross
+ Moore</a>, Mathematics Department, Macquarie University,
+ Sydney.
+</p>
+
+<p> The application of <a
+ href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/">
+ <strong>LaTeX</strong>2<tt>HTML</tt></a> to the Python
+ documentation has been heavily tailored by Fred L. Drake,
+ Jr. Original navigation icons were contributed by Christopher
+ Petrilli.
+</p>
+
+<DIV CLASS="navigation">
+<div class='online-navigation'>
+<p></p><hr />
+<table align="center" width="100%" cellpadding="0" cellspacing="2">
+<tr>
+<td class='online-navigation'><img src='previous.png'
+ border='0' height='32' alt='Previous Page' width='32' /></td>
+<td class='online-navigation'><img src='up.png'
+ border='0' height='32' alt='Up One Level' width='32' /></td>
+<td class='online-navigation'><img src='next.png'
+ border='0' height='32' alt='Next Page' width='32' /></td>
+<td align="center" width="100%">ZSI: The Zolera Soap Infrastructure
+<BR> Developer's Guide</td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+<td class='online-navigation'><img src='blank.png'
+ border='0' height='32' alt='' width='32' /></td>
+</tr></table>
+<div class='online-navigation'>
+</div>
+</div>
+<hr />
+<span class="release-info">Release 2.0.0, documentation updated on October 25, 2006.</span>
+</DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/zsi.l2h~ b/doc/zsi.l2h~
new file mode 100644
index 0000000..53b3187
--- /dev/null
+++ b/doc/zsi.l2h~
@@ -0,0 +1,797 @@
+package main;
+push (@INC, '/Users/boverhof/Desktop/Python/PythonDocTools/perl');
+$mydir = '/Users/boverhof/Desktop/Python/PythonDocTools/perl';
+# LaTeX2HTML support base for use with Python documentation.
+
+package main;
+
+use L2hos;
+
+$HTML_VERSION = 4.0;
+
+$MAX_LINK_DEPTH = 2;
+$ADDRESS = '';
+
+$NO_FOOTNODE = 1;
+$NUMBERED_FOOTNOTES = 1;
+
+# Python documentation uses section numbers to support references to match
+# in the printed and online versions.
+#
+$SHOW_SECTION_NUMBERS = 1;
+
+$ICONSERVER = '.';
+$IMAGE_TYPE = 'gif';
+
+# Control where the navigation bars should show up:
+$TOP_NAVIGATION = 1;
+$BOTTOM_NAVIGATION = 1;
+$AUTO_NAVIGATION = 0;
+
+$BODYTEXT = '';
+$CHILDLINE = "\n<p><br /></p><hr class='online-navigation' />\n";
+$VERBOSITY = 0;
+
+# default # of columns for the indexes
+$INDEX_COLUMNS = 2;
+$MODULE_INDEX_COLUMNS = 4;
+
+$HAVE_MODULE_INDEX = 0;
+$HAVE_GENERAL_INDEX = 0;
+$HAVE_TABLE_OF_CONTENTS = 0;
+
+$AESOP_META_TYPE = 'information';
+
+
+# A little painful, but lets us clean up the top level directory a little,
+# and not be tied to the current directory (as far as I can tell). Testing
+# an existing definition of $mydir is needed since it cannot be computed when
+# run under mkhowto with recent versions of LaTeX2HTML, since this file is
+# not read directly by LaTeX2HTML any more. mkhowto is required to prepend
+# the required definition at the top of the actual input file.
+#
+if (!defined $mydir) {
+ use Cwd;
+ use File::Basename;
+ ($myname, $mydir, $myext) = fileparse(__FILE__, '\..*');
+ chop $mydir; # remove trailing '/'
+ $mydir = getcwd() . "$dd$mydir"
+ unless $mydir =~ s|^/|/|;
+}
+$LATEX2HTMLSTYLES = "$mydir$envkey$LATEX2HTMLSTYLES";
+push (@INC, $mydir);
+
+($myrootname, $myrootdir, $myext) = fileparse($mydir, '\..*');
+chop $myrootdir;
+
+
+# Hackish way to get the appropriate paper-*/ directory into $TEXINPUTS;
+# pass in the paper size (a4 or letter) as the environment variable PAPER
+# to add the right directory. If not given, the current directory is
+# added instead for use with HOWTO processing.
+#
+if (defined $ENV{'PAPER'}) {
+ $mytexinputs = "$myrootdir${dd}paper-$ENV{'PAPER'}$envkey";
+}
+else {
+ $mytexinputs = getcwd() . $envkey;
+}
+$mytexinputs .= "$myrootdir${dd}texinputs";
+
+
+# Change this variable to change the text added in "About this document...";
+# this should be an absolute pathname to get it right.
+#
+$ABOUT_FILE = "$myrootdir${dd}html${dd}stdabout.dat";
+
+
+sub custom_driver_hook {
+ #
+ # This adds the directory of the main input file to $TEXINPUTS; it
+ # seems to be sufficiently general that it should be fine for HOWTO
+ # processing.
+ #
+ # XXX This still isn't quite right; we should actually be inserting
+ # $mytexinputs just before any empty entry in TEXINPUTS is one
+ # exists instead of just concatenating the pieces like we do here.
+ #
+ my $file = $_[0];
+ my($jobname, $dir, $ext) = fileparse($file, '\..*');
+ $dir = L2hos->Make_directory_absolute($dir);
+ $dir =~ s/$dd$//;
+ $TEXINPUTS = "$dir$envkey$mytexinputs";
+ # Push everything into $TEXINPUTS since LaTeX2HTML doesn't pick
+ # this up on its own; we clear $ENV{'TEXINPUTS'} so the value set
+ # for this by the main LaTeX2HTML script doesn't contain duplicate
+ # directories.
+ if ($ENV{'TEXINPUTS'}) {
+ $TEXINPUTS .= "$envkey$ENV{'TEXINPUTS'}";
+ $ENV{'TEXINPUTS'} = undef;
+ }
+ print "\nSetting \$TEXINPUTS to $TEXINPUTS\n";
+}
+
+
+# $CUSTOM_BUTTONS is only used for the module index link.
+$CUSTOM_BUTTONS = '';
+
+sub make_nav_sectref($$$) {
+ my($label, $linktype, $title) = @_;
+ if ($title) {
+ if ($title =~ /\<[aA] /) {
+ $title =~ s/\<[aA] /<a class="sectref" rel="$linktype" /;
+ $title =~ s/ HREF=/ href=/;
+ }
+ else {
+ $title = "<span class=\"sectref\">$title</span>";
+ }
+ return "<b class=\"navlabel\">$label:</b>\n$title\n";
+ }
+ return '';
+}
+
+@my_icon_tags = ();
+$my_icon_tags{'next'} = 'Next Page';
+$my_icon_tags{'next_page'} = 'Next Page';
+$my_icon_tags{'previous'} = 'Previous Page';
+$my_icon_tags{'previous_page'} = 'Previous Page';
+$my_icon_tags{'up'} = 'Up One Level';
+$my_icon_tags{'contents'} = 'Contents';
+$my_icon_tags{'index'} = 'Index';
+$my_icon_tags{'modules'} = 'Module Index';
+
+@my_icon_names = ();
+$my_icon_names{'previous_page'} = 'previous';
+$my_icon_names{'next_page'} = 'next';
+
+sub get_my_icon($) {
+ my $name = $_[0];
+ my $text = $my_icon_tags{$name};
+ if ($my_icon_names{$name}) {
+ $name = $my_icon_names{$name};
+ }
+ if ($text eq '') {
+ $name = 'blank';
+ }
+ my $iconserver = ($ICONSERVER eq '.') ? '' : "$ICONSERVER/";
+ return "<img src='$iconserver$name.$IMAGE_TYPE'\n border='0'"
+ . " height='32' alt='$text' width='32' />";
+}
+
+sub unlinkify($) {
+ my $text = "$_[0]";
+ $text =~ s|</[aA]>||;
+ $text =~ s|<a\s+[^>]*>||i;
+ return $text;
+}
+
+sub use_icon($$$) {
+ my($rel,$str,$title) = @_;
+ if ($str) {
+ my $s = "$str";
+ if ($s =~ /\<tex2html_([a-z_]+)_visible_mark\>/) {
+ my $r = get_my_icon($1);
+ $s =~ s/\<tex2html_[a-z_]+_visible_mark\>/$r/;
+ }
+ $s =~ s/<[aA] /<a rel="$rel" title="$title"\n /;
+ $s =~ s/ HREF=/ href=/;
+ return $s;
+ }
+ else {
+ return get_my_icon('blank');
+ }
+}
+
+sub make_nav_panel() {
+ my $s;
+ # new iconic rel iconic page title
+ my $next = use_icon('next', $NEXT, unlinkify($NEXT_TITLE));
+ my $up = use_icon('parent', $UP, unlinkify($UP_TITLE));
+ my $previous = use_icon('prev', $PREVIOUS, unlinkify($PREVIOUS_TITLE));
+ my $contents = use_icon('contents', $CONTENTS, 'Table of Contents');
+ my $index = use_icon('index', $INDEX, 'Index');
+ if (!$CUSTOM_BUTTONS) {
+ $CUSTOM_BUTTONS = get_my_icon('blank');
+ }
+ $s = ('<table align="center" width="100%" cellpadding="0" cellspacing="2">'
+ . "\n<tr>"
+ # left-hand side
+ . "\n<td class='online-navigation'>$previous</td>"
+ . "\n<td class='online-navigation'>$up</td>"
+ . "\n<td class='online-navigation'>$next</td>"
+ # title box
+ . "\n<td align=\"center\" width=\"100%\">$t_title</td>"
+ # right-hand side
+ . "\n<td class='online-navigation'>$contents</td>"
+ # module index
+ . "\n<td class='online-navigation'>$CUSTOM_BUTTONS</td>"
+ . "\n<td class='online-navigation'>$index</td>"
+ . "\n</tr></table>\n"
+ # textual navigation
+ . "<div class='online-navigation'>\n"
+ . make_nav_sectref("Previous", "prev", $PREVIOUS_TITLE)
+ . make_nav_sectref("Up", "parent", $UP_TITLE)
+ . make_nav_sectref("Next", "next", $NEXT_TITLE)
+ . "</div>\n"
+ );
+ # remove these; they are unnecessary and cause errors from validation
+ $s =~ s/ NAME="tex2html\d+"\n */ /g;
+ return $s;
+}
+
+sub add_child_links {
+ my $toc = add_real_child_links(@_);
+ $toc =~ s|\s*</[aA]>|</a>|g;
+ $toc =~ s/ NAME=\"tex2html\d+\"\s*href=/ href=/gi;
+ $toc =~ s|</UL>(\s*<BR( /)?>)?|</ul>|gi;
+ if ($toc =~ / NAME=["']CHILD_LINKS["']/) {
+ return "<div class='online-navigation'>\n$toc</div>\n";
+ }
+ return $toc;
+}
+
+sub get_version_text() {
+ if ($PACKAGE_VERSION ne '' && $t_date) {
+ return ("<span class=\"release-info\">"
+ . "Release $PACKAGE_VERSION$RELEASE_INFO,"
+ . " documentation updated on $t_date.</span>");
+ }
+ if ($PACKAGE_VERSION ne '') {
+ return ("<span class=\"release-info\">"
+ . "Release $PACKAGE_VERSION$RELEASE_INFO.</span>");
+ }
+ if ($t_date) {
+ return ("<span class=\"release-info\">Documentation released on "
+ . "$t_date.</span>");
+ }
+ return '';
+}
+
+
+sub top_navigation_panel() {
+ return "\n<div id='top-navigation-panel' xml:id='top-navigation-panel'>\n"
+ . make_nav_panel()
+ . "<hr /></div>\n";
+}
+
+sub bot_navigation_panel() {
+ return "\n<div class='online-navigation'>\n"
+ . "<p></p><hr />\n"
+ . make_nav_panel()
+ . "</div>\n"
+ . "<hr />\n"
+ . get_version_text()
+ . "\n";
+}
+
+sub add_link {
+ # Returns a pair (iconic link, textual link)
+ my($icon, $current_file, @link) = @_;
+ my($dummy, $file, $title) = split($delim,
+ $section_info{join(' ',@link)});
+ if ($icon =~ /\<tex2html_([_a-z]+)_visible_mark\>/) {
+ my $r = get_my_icon($1);
+ $icon =~ s/\<tex2html_[_a-z]+_visible_mark\>/$r/;
+ }
+ if ($title && ($file ne $current_file)) {
+ $title = purify($title);
+ $title = get_first_words($title, $WORDS_IN_NAVIGATION_PANEL_TITLES);
+ return (make_href($file, $icon), make_href($file, "$title"))
+ }
+ elsif ($icon eq get_my_icon('up') && $EXTERNAL_UP_LINK) {
+ return (make_href($EXTERNAL_UP_LINK, $icon),
+ make_href($EXTERNAL_UP_LINK, "$EXTERNAL_UP_TITLE"))
+ }
+ elsif ($icon eq get_my_icon('previous')
+ && $EXTERNAL_PREV_LINK && $EXTERNAL_PREV_TITLE) {
+ return (make_href($EXTERNAL_PREV_LINK, $icon),
+ make_href($EXTERNAL_PREV_LINK, "$EXTERNAL_PREV_TITLE"))
+ }
+ elsif ($icon eq get_my_icon('next')
+ && $EXTERNAL_DOWN_LINK && $EXTERNAL_DOWN_TITLE) {
+ return (make_href($EXTERNAL_DOWN_LINK, $icon),
+ make_href($EXTERNAL_DOWN_LINK, "$EXTERNAL_DOWN_TITLE"))
+ }
+ return (&inactive_img($icon), "");
+}
+
+sub add_special_link($$$) {
+ my($icon, $file, $current_file) = @_;
+ if ($icon =~ /\<tex2html_([_a-z]+)_visible_mark\>/) {
+ my $r = get_my_icon($1);
+ $icon =~ s/\<tex2html_[_a-z]+_visible_mark\>/$r/;
+ }
+ return (($file && ($file ne $current_file))
+ ? make_href($file, $icon)
+ : undef)
+}
+
+# The img_tag() function seems only to be called with the parameter
+# 'anchor_invisible_mark', which we want to turn into ''. Since
+# replace_icon_marks() is the only interesting caller, and all it really
+# does is call img_tag(), we can just define the hook alternative to be
+# a no-op instead.
+#
+sub replace_icons_hook {}
+
+sub do_cmd_arabic {
+ # get rid of that nasty <SPAN CLASS="arabic">...</SPAN>
+ my($ctr, $val, $id, $text) = &read_counter_value($_[0]);
+ return ($val ? farabic($val) : "0") . $text;
+}
+
+
+sub gen_index_id($$) {
+ # this is used to ensure common index key generation and a stable sort
+ my($str, $extra) = @_;
+ sprintf('%s###%s%010d', $str, $extra, ++$global{'max_id'});
+}
+
+sub insert_index($$$$$) {
+ my($mark, $datafile, $columns, $letters, $prefix) = @_;
+ my $prog = "$myrootdir/tools/buildindex.py";
+ my $index;
+ if ($letters) {
+ $index = `$prog --columns $columns --letters $datafile`;
+ }
+ else {
+ $index = `$prog --columns $columns $datafile`;
+ }
+ if (!s/$mark/$prefix$index/) {
+ print "\nCould not locate index mark: $mark";
+ }
+}
+
+sub add_idx() {
+ print "\nBuilding HTML for the index ...";
+ close(IDXFILE);
+ insert_index($idx_mark, 'index.dat', $INDEX_COLUMNS, 1, '');
+}
+
+
+$idx_module_mark = '<tex2html_idx_module_mark>';
+$idx_module_title = 'Module Index';
+
+sub add_module_idx() {
+ print "\nBuilding HTML for the module index ...";
+ my $key;
+ my $first = 1;
+ my $prevplat = '';
+ my $allthesame = 1;
+ my $prefix = '';
+ foreach $key (keys %Modules) {
+ $key =~ s/<tt>([a-zA-Z0-9._]*)<\/tt>/$1/;
+ my $plat = "$ModulePlatforms{$key}";
+ $plat = ''
+ if ($plat eq $IGNORE_PLATFORM_ANNOTATION);
+ if (!$first) {
+ $allthesame = 0
+ if ($prevplat ne $plat);
+ }
+ else { $first = 0; }
+ $prevplat = $plat;
+ }
+ open(MODIDXFILE, '>modindex.dat') || die "\n$!\n";
+ foreach $key (keys %Modules) {
+ # dump the line in the data file; just use a dummy seqno field
+ my $nkey = $1;
+ my $moditem = "$Modules{$key}";
+ my $plat = '';
+ $key =~ s/<tt>([a-zA-Z0-9._]*)<\/tt>/$1/;
+ if ($ModulePlatforms{$key} && !$allthesame) {
+ $plat = (" <em>(<span class=\"platform\">$ModulePlatforms{$key}"
+ . '</span>)</em>');
+ }
+ print MODIDXFILE $moditem . $IDXFILE_FIELD_SEP
+ . "<tt class=\"module\">$key</tt>$plat###\n";
+ }
+ close(MODIDXFILE);
+
+ if ($GLOBAL_MODULE_INDEX) {
+ $prefix = <<MODULE_INDEX_PREFIX;
+
+<p> This index only lists modules documented in this manual.
+ The <em class="citetitle"><a href="$GLOBAL_MODULE_INDEX">Global Module
+ Index</a></em> lists all modules that are documented in this set
+ of manuals.</p>
+MODULE_INDEX_PREFIX
+ }
+ if (!$allthesame) {
+ $prefix .= <<PLAT_DISCUSS;
+
+<p> Some module names are followed by an annotation indicating what
+platform they are available on.</p>
+
+PLAT_DISCUSS
+ }
+ insert_index($idx_module_mark, 'modindex.dat', $MODULE_INDEX_COLUMNS, 0,
+ $prefix);
+}
+
+# replace both indexes as needed:
+sub add_idx_hook {
+ add_idx() if (/$idx_mark/);
+ process_python_state();
+ if ($MODULE_INDEX_FILE) {
+ local ($_);
+ open(MYFILE, "<$MODULE_INDEX_FILE");
+ sysread(MYFILE, $_, 1024*1024);
+ close(MYFILE);
+ add_module_idx();
+ open(MYFILE,">$MODULE_INDEX_FILE");
+ print MYFILE $_;
+ close(MYFILE);
+ }
+}
+
+
+# In addition to the standard stuff, add label to allow named node files and
+# support suppression of the page complete (for HTML Help use).
+$MY_CONTENTS_PAGE = '';
+sub do_cmd_tableofcontents {
+ local($_) = @_;
+ $TITLE = $toc_title;
+ $tocfile = $CURRENT_FILE;
+ my($closures, $reopens) = preserve_open_tags();
+ anchor_label('contents', $CURRENT_FILE, $_); # this is added
+ $MY_CONTENTS_PAGE = "$CURRENT_FILE";
+ join('', "\\tableofchildlinks[off]", $closures
+ , make_section_heading($toc_title, 'h2'), $toc_mark
+ , $reopens, $_);
+}
+# In addition to the standard stuff, add label to allow named node files.
+sub do_cmd_listoffigures {
+ local($_) = @_;
+ $TITLE = $lof_title;
+ $loffile = $CURRENT_FILE;
+ my($closures, $reopens) = preserve_open_tags();
+ anchor_label('lof', $CURRENT_FILE, $_); # this is added
+ join('', "<br />\n", $closures
+ , make_section_heading($lof_title, 'h2'), $lof_mark
+ , $reopens, $_);
+}
+# In addition to the standard stuff, add label to allow named node files.
+sub do_cmd_listoftables {
+ local($_) = @_;
+ $TITLE = $lot_title;
+ $lotfile = $CURRENT_FILE;
+ my($closures, $reopens) = preserve_open_tags();
+ anchor_label('lot', $CURRENT_FILE, $_); # this is added
+ join('', "<br />\n", $closures
+ , make_section_heading($lot_title, 'h2'), $lot_mark
+ , $reopens, $_);
+}
+# In addition to the standard stuff, add label to allow named node files.
+sub do_cmd_textohtmlinfopage {
+ local($_) = @_;
+ if ($INFO) { #
+ anchor_label("about",$CURRENT_FILE,$_); # this is added
+ } #
+ my $the_version = ''; # and the rest is
+ if ($t_date) { # mostly ours
+ $the_version = ",\n$t_date";
+ if ($PACKAGE_VERSION) {
+ $the_version .= ", Release $PACKAGE_VERSION$RELEASE_INFO";
+ }
+ }
+ my $about;
+ open(ABOUT, "<$ABOUT_FILE") || die "\n$!\n";
+ sysread(ABOUT, $about, 1024*1024);
+ close(ABOUT);
+ $_ = (($INFO == 1)
+ ? join('',
+ $close_all,
+ "<strong>$t_title</strong>$the_version\n",
+ $about,
+ $open_all, $_)
+ : join('', $close_all, $INFO,"\n", $open_all, $_));
+ $_;
+}
+
+$GENERAL_INDEX_FILE = '';
+$MODULE_INDEX_FILE = '';
+
+# $idx_mark will be replaced with the real index at the end
+sub do_cmd_textohtmlindex {
+ local($_) = @_;
+ $TITLE = $idx_title;
+ $idxfile = $CURRENT_FILE;
+ $GENERAL_INDEX_FILE = "$CURRENT_FILE";
+ if (%index_labels) { make_index_labels(); }
+ if (($SHORT_INDEX) && (%index_segment)) { make_preindex(); }
+ else { $preindex = ''; }
+ my $heading = make_section_heading($idx_title, 'h2') . $idx_mark;
+ my($pre, $post) = minimize_open_tags($heading);
+ anchor_label('genindex',$CURRENT_FILE,$_); # this is added
+ return "<br />\n" . $pre . $_;
+}
+
+# $idx_module_mark will be replaced with the real index at the end
+sub do_cmd_textohtmlmoduleindex {
+ local($_) = @_;
+ $TITLE = $idx_module_title;
+ anchor_label('modindex', $CURRENT_FILE, $_);
+ $MODULE_INDEX_FILE = "$CURRENT_FILE";
+ $_ = ('<p></p>' . make_section_heading($idx_module_title, 'h2')
+ . $idx_module_mark . $_);
+ return $_;
+}
+
+# The bibliography and the index should be treated as separate
+# sections in their own HTML files. The \bibliography{} command acts
+# as a sectioning command that has the desired effect. But when the
+# bibliography is constructed manually using the thebibliography
+# environment, or when using the theindex environment it is not
+# possible to use the normal sectioning mechanism. This subroutine
+# inserts a \bibliography{} or a dummy \textohtmlindex command just
+# before the appropriate environments to force sectioning.
+
+# XXX This *assumes* that if there are two {theindex} environments,
+# the first is the module index and the second is the standard
+# index. This is sufficient for the current Python documentation,
+# but that's about it.
+
+sub add_bbl_and_idx_dummy_commands {
+ my $id = $global{'max_id'};
+
+ if (/[\\]tableofcontents/) {
+ $HAVE_TABLE_OF_CONTENTS = 1;
+ }
+ s/([\\]begin\s*$O\d+$C\s*thebibliography)/$bbl_cnt++; $1/eg;
+ s/([\\]begin\s*$O\d+$C\s*thebibliography)/$id++; "\\bibliography$O$id$C$O$id$C $1"/geo;
+ my(@parts) = split(/\\begin\s*$O\d+$C\s*theindex/);
+ if (scalar(@parts) == 3) {
+ # Be careful to re-write the string in place, since $_ is *not*
+ # returned explicity; *** nasty side-effect dependency! ***
+ print "\nadd_bbl_and_idx_dummy_commands ==> adding general index";
+ print "\nadd_bbl_and_idx_dummy_commands ==> adding module index";
+ my $rx = "([\\\\]begin\\s*$O\\d+$C\\s*theindex[\\s\\S]*)"
+ . "([\\\\]begin\\s*$O\\d+$C\\s*theindex)";
+ s/$rx/\\textohtmlmoduleindex $1 \\textohtmlindex $2/o;
+ # Add a button to the navigation areas:
+ $CUSTOM_BUTTONS .= ('<a href="modindex.html" title="Module Index">'
+ . get_my_icon('modules')
+ . '</a>');
+ $HAVE_MODULE_INDEX = 1;
+ $HAVE_GENERAL_INDEX = 1;
+ }
+ elsif (scalar(@parts) == 2) {
+ print "\nadd_bbl_and_idx_dummy_commands ==> adding general index";
+ my $rx = "([\\\\]begin\\s*$O\\d+$C\\s*theindex)";
+ s/$rx/\\textohtmlindex $1/o;
+ $HAVE_GENERAL_INDEX = 1;
+ }
+ elsif (scalar(@parts) == 1) {
+ print "\nadd_bbl_and_idx_dummy_commands ==> no index found";
+ $CUSTOM_BUTTONS .= get_my_icon('blank');
+ $global{'max_id'} = $id; # not sure why....
+ s/([\\]begin\s*$O\d+$C\s*theindex)/\\textohtmlindex $1/o;
+ s/[\\]printindex/\\textohtmlindex /o;
+ }
+ else {
+ die "\n\nBad number of index environments!\n\n";
+ }
+ #----------------------------------------------------------------------
+ lib_add_bbl_and_idx_dummy_commands()
+ if defined(&lib_add_bbl_and_idx_dummy_commands);
+}
+
+# The bibliographic references, the appendices, the lists of figures
+# and tables etc. must appear in the contents table at the same level
+# as the outermost sectioning command. This subroutine finds what is
+# the outermost level and sets the above to the same level;
+
+sub set_depth_levels {
+ # Sets $outermost_level
+ my $level;
+ #RRM: do not alter user-set value for $MAX_SPLIT_DEPTH
+ foreach $level ("part", "chapter", "section", "subsection",
+ "subsubsection", "paragraph") {
+ last if (($outermost_level) = /\\($level)$delimiter_rx/);
+ }
+ $level = ($outermost_level ? $section_commands{$outermost_level} :
+ do {$outermost_level = 'section'; 3;});
+
+ #RRM: but calculate value for $MAX_SPLIT_DEPTH when a $REL_DEPTH was given
+ if ($REL_DEPTH && $MAX_SPLIT_DEPTH) {
+ $MAX_SPLIT_DEPTH = $level + $MAX_SPLIT_DEPTH;
+ } elsif (!($MAX_SPLIT_DEPTH)) { $MAX_SPLIT_DEPTH = 1 };
+
+ %unnumbered_section_commands = ('tableofcontents' => $level,
+ 'listoffigures' => $level,
+ 'listoftables' => $level,
+ 'bibliography' => $level,
+ 'textohtmlindex' => $level,
+ 'textohtmlmoduleindex' => $level);
+ $section_headings{'textohtmlmoduleindex'} = 'h1';
+
+ %section_commands = (%unnumbered_section_commands,
+ %section_commands);
+
+ make_sections_rx();
+}
+
+
+# This changes the markup used for {verbatim} environments, and is the
+# best way I've found that ensures the <dl> goes on the outside of the
+# <pre>...</pre>.
+#
+# Note that this *must* be done in the init file, not the python.perl
+# style support file. The %declarations must be set before
+# initialize() is called in the main LaTeX2HTML script (which happens
+# before style files are loaded).
+#
+%declarations = ('preform' => '<div class="verbatim"><pre></pre></div>',
+ %declarations);
+
+
+# This is a modified version of what's provided by LaTeX2HTML; see the
+# comment on the middle stanza for an explanation of why we keep our
+# own version.
+#
+# This routine must be called once on the text only,
+# else it will "eat up" sensitive constructs.
+sub text_cleanup {
+ # MRO: replaced $* with /m
+ s/(\s*\n){3,}/\n\n/gom; # Replace consecutive blank lines with one
+ s/<(\/?)P>\s*(\w)/<$1P>\n$2/gom; # clean up paragraph starts and ends
+ s/$O\d+$C//go; # Get rid of bracket id's
+ s/$OP\d+$CP//go; # Get rid of processed bracket id's
+ s/(<!)?--?(>)?/(length($1) || length($2)) ? "$1--$2" : "-"/ge;
+ # Spacing commands
+ s/\\( |$)/ /go;
+ #JKR: There should be no more comments in the source now.
+ #s/([^\\]?)%/$1/go; # Remove the comment character
+ # Cannot treat \, as a command because , is a delimiter ...
+ s/\\,/ /go;
+ # Replace tilde's with non-breaking spaces
+ s/ *~/&nbsp;/g;
+
+ # This is why we have this copy of this routine; the following
+ # isn't so desirable as the author/maintainers of LaTeX2HTML seem
+ # to think. It's not commented out in the main script, so we have
+ # to override the whole thing. In particular, we don't want empty
+ # table cells to disappear.
+
+ ### DANGEROUS ?? ###
+ # remove redundant (not <P></P>) empty tags, incl. with attributes
+ #s/\n?<([^PD >][^>]*)>\s*<\/\1>//g;
+ #s/\n?<([^PD >][^>]*)>\s*<\/\1>//g;
+ # remove redundant empty tags (not </P><P> or <TD> or <TH>)
+ #s/<\/(TT|[^PTH][A-Z]+)><\1>//g;
+ #s/<([^PD ]+)(\s[^>]*)?>\n*<\/\1>//g;
+
+ #JCL(jcl-hex)
+ # Replace ^^ special chars (according to p.47 of the TeX book)
+ # Useful when coming from the .aux file (german umlauts, etc.)
+ s/\^\^([^0-9a-f])/chr((64+ord($1))&127)/ge;
+ s/\^\^([0-9a-f][0-9a-f])/chr(hex($1))/ge;
+}
+
+# This is used to map the link rel attributes LaTeX2HTML uses to those
+# currently recommended by the W3C.
+sub custom_REL_hook {
+ my($rel,$junk) = @_;
+ return 'parent' if $rel eq 'up';
+ return 'prev' if $rel eq 'previous';
+ return $rel;
+}
+
+# This is added to get rid of the long comment that follows the
+# doctype declaration; MSIE5 on NT4 SP4 barfs on it and drops the
+# content of the page.
+$MY_PARTIAL_HEADER = '';
+sub make_head_and_body($$) {
+ my($title, $body) = @_;
+ $body = " $body" unless ($body eq '');
+ my $DTDcomment = '';
+ my($version, $isolanguage) = ($HTML_VERSION, 'EN');
+ my %isolanguages = ( 'english', 'EN' , 'USenglish', 'EN.US'
+ , 'original', 'EN' , 'german' , 'DE'
+ , 'austrian', 'DE.AT', 'french' , 'FR'
+ , 'spanish', 'ES');
+ $isolanguage = $isolanguages{$default_language};
+ $isolanguage = 'EN' unless $isolanguage;
+ $title = &purify($title,1);
+ eval("\$title = ". $default_title ) unless ($title);
+
+ # allow user-modification of the <title> tag; thanks Dan Young
+ if (defined &custom_TITLE_hook) {
+ $title = &custom_TITLE_hook($title, $toc_sec_title);
+ }
+
+ if ($DOCTYPE =~ /\/\/[\w\.]+\s*$/) { # language spec included
+ $DTDcomment = "<!DOCTYPE html PUBLIC \"$DOCTYPE\">\n";
+ } else {
+ $DTDcomment = "<!DOCTYPE html PUBLIC \"$DOCTYPE//"
+ . ($ISO_LANGUAGE ? $ISO_LANGUAGE : $isolanguage) . "\">\n";
+ }
+ if ($MY_PARTIAL_HEADER eq '') {
+ my $favicon = '';
+ if ($FAVORITES_ICON) {
+ my($myname, $mydir, $myext) = fileparse($FAVORITES_ICON, '\..*');
+ my $favtype = '';
+ if ($myext eq '.gif' || $myext eq '.png') {
+ $myext =~ s/^[.]//;
+ $favtype = " type=\"image/$myext\"";
+ }
+ $favicon = (
+ "\n<link rel=\"SHORTCUT ICON\" href=\"$FAVORITES_ICON\""
+ . "$favtype />");
+ }
+ $STYLESHEET = $FILE.".css" unless $STYLESHEET;
+ $MY_PARTIAL_HEADER = join('',
+ ($DOCTYPE ? $DTDcomment : ''),
+ "<html>\n<head>",
+ ($BASE ? "\n<base href=\"$BASE\" />" : ''),
+ "\n<link rel=\"STYLESHEET\" href=\"$STYLESHEET\" type='text/css'",
+ " />",
+ $favicon,
+ ($EXTERNAL_UP_LINK
+ ? ("\n<link rel='start' href='" . $EXTERNAL_UP_LINK
+ . ($EXTERNAL_UP_TITLE ?
+ "' title='$EXTERNAL_UP_TITLE' />" : "' />"))
+ : ''),
+ "\n<link rel=\"first\" href=\"$FILE.html\"",
+ ($t_title ? " title='$t_title'" : ''),
+ ' />',
+ ($HAVE_TABLE_OF_CONTENTS
+ ? ("\n<link rel='contents' href='$MY_CONTENTS_PAGE'"
+ . ' title="Contents" />')
+ : ''),
+ ($HAVE_GENERAL_INDEX
+ ? ("\n<link rel='index' href='$GENERAL_INDEX_FILE'"
+ . " title='Index' />")
+ : ''),
+ # disable for now -- Mozilla doesn't do well with multiple indexes
+ # ($HAVE_MODULE_INDEX
+ # ? ("<link rel="index" href='$MODULE_INDEX_FILE'"
+ # . " title='Module Index' />\n")
+ # : ''),
+ ($INFO
+ # XXX We can do this with the Python tools since the About...
+ # page always gets copied to about.html, even when we use the
+ # generated node###.html page names. Won't work with the
+ # rest of the Python doc tools.
+ ? ("\n<link rel='last' href='about.html'"
+ . " title='About this document...' />"
+ . "\n<link rel='help' href='about.html'"
+ . " title='About this document...' />")
+ : ''),
+ $more_links_mark,
+ "\n",
+ ($CHARSET && $HTML_VERSION ge "2.1"
+ ? ('<meta http-equiv="Content-Type" content="text/html; '
+ . "charset=$CHARSET\" />\n")
+ : ''),
+ ($AESOP_META_TYPE
+ ? "<meta name='aesop' content='$AESOP_META_TYPE' />\n" : ''));
+ }
+ if (!$charset && $CHARSET) {
+ $charset = $CHARSET;
+ $charset =~ s/_/\-/go;
+ }
+ join('',
+ $MY_PARTIAL_HEADER,
+ "<title>", $title, "</title>\n</head>\n<body$body>");
+}
+
+sub replace_morelinks {
+ $more_links =~ s/ REL=/ rel=/g;
+ $more_links =~ s/ HREF=/ href=/g;
+ $more_links =~ s/<LINK /<link /g;
+ $more_links =~ s/">/" \/>/g;
+ $_ =~ s/$more_links_mark/$more_links/e;
+}
+
+1; # This must be the last line
+
+# auxillary init file for latex2html
+# generated by mkhowto
+$NO_AUTO_LINK = 1;
+$ABOUT_FILE = "/Users/boverhof/Desktop/Python/PythonDocTools/html/about.dat";
+$ICONSERVER = ".";
+$IMAGE_TYPE = "png";
+$MAX_LINK_DEPTH = "3";
+$MAX_SPLIT_DEPTH = "6";
+1;
diff --git a/doc/zsi.pdf b/doc/zsi.pdf
new file mode 100644
index 0000000..e207db7
--- /dev/null
+++ b/doc/zsi.pdf
Binary files differ
diff --git a/doc/zsi.ps b/doc/zsi.ps
new file mode 100644
index 0000000..986773d
--- /dev/null
+++ b/doc/zsi.ps
@@ -0,0 +1,3495 @@
+%!PS-Adobe-2.0
+%%Creator: dvips(k) 5.95a Copyright 2005 Radical Eye Software
+%%Title: zsi.dvi
+%%Pages: 71
+%%PageOrder: Ascend
+%%BoundingBox: 0 0 612 792
+%%DocumentFonts: Helvetica Helvetica-Oblique Times-Roman Times-Bold
+%%+ CMSY10 Courier Times-Italic Courier-Bold Helvetica-Bold
+%%+ Courier-BoldOblique
+%%DocumentPaperSizes: Letter
+%%EndComments
+%DVIPSWebPage: (www.radicaleye.com)
+%DVIPSCommandLine: dvips -N0 -t letter -o zsi.ps zsi.dvi
+%DVIPSParameters: dpi=600
+%DVIPSSource: TeX output 2006.10.25:2110
+%%BeginProcSet: tex.pro 0 0
+%!
+/TeXDict 300 dict def TeXDict begin/N{def}def/B{bind def}N/S{exch}N/X{S
+N}B/A{dup}B/TR{translate}N/isls false N/vsize 11 72 mul N/hsize 8.5 72
+mul N/landplus90{false}def/@rigin{isls{[0 landplus90{1 -1}{-1 1}ifelse 0
+0 0]concat}if 72 Resolution div 72 VResolution div neg scale isls{
+landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div hsize
+mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul TR[
+matrix currentmatrix{A A round sub abs 0.00001 lt{round}if}forall round
+exch round exch]setmatrix}N/@landscape{/isls true N}B/@manualfeed{
+statusdict/manualfeed true put}B/@copies{/#copies X}B/FMat[1 0 0 -1 0 0]
+N/FBB[0 0 0 0]N/nn 0 N/IEn 0 N/ctr 0 N/df-tail{/nn 8 dict N nn begin
+/FontType 3 N/FontMatrix fntrx N/FontBBox FBB N string/base X array
+/BitMaps X/BuildChar{CharBuilder}N/Encoding IEn N end A{/foo setfont}2
+array copy cvx N load 0 nn put/ctr 0 N[}B/sf 0 N/df{/sf 1 N/fntrx FMat N
+df-tail}B/dfs{div/sf X/fntrx[sf 0 0 sf neg 0 0]N df-tail}B/E{pop nn A
+definefont setfont}B/Cw{Cd A length 5 sub get}B/Ch{Cd A length 4 sub get
+}B/Cx{128 Cd A length 3 sub get sub}B/Cy{Cd A length 2 sub get 127 sub}
+B/Cdx{Cd A length 1 sub get}B/Ci{Cd A type/stringtype ne{ctr get/ctr ctr
+1 add N}if}B/CharBuilder{save 3 1 roll S A/base get 2 index get S
+/BitMaps get S get/Cd X pop/ctr 0 N Cdx 0 Cx Cy Ch sub Cx Cw add Cy
+setcachedevice Cw Ch true[1 0 0 -1 -.1 Cx sub Cy .1 sub]{Ci}imagemask
+restore}B/D{/cc X A type/stringtype ne{]}if nn/base get cc ctr put nn
+/BitMaps get S ctr S sf 1 ne{A A length 1 sub A 2 index S get sf div put
+}if put/ctr ctr 1 add N}B/I{cc 1 add D}B/bop{userdict/bop-hook known{
+bop-hook}if/SI save N @rigin 0 0 moveto/V matrix currentmatrix A 1 get A
+mul exch 0 get A mul add .99 lt{/QV}{/RV}ifelse load def pop pop}N/eop{
+SI restore userdict/eop-hook known{eop-hook}if showpage}N/@start{
+userdict/start-hook known{start-hook}if pop/VResolution X/Resolution X
+1000 div/DVImag X/IEn 256 array N 2 string 0 1 255{IEn S A 360 add 36 4
+index cvrs cvn put}for pop 65781.76 div/vsize X 65781.76 div/hsize X}N
+/p{show}N/RMat[1 0 0 -1 0 0]N/BDot 260 string N/Rx 0 N/Ry 0 N/V{}B/RV/v{
+/Ry X/Rx X V}B statusdict begin/product where{pop false[(Display)(NeXT)
+(LaserWriter 16/600)]{A length product length le{A length product exch 0
+exch getinterval eq{pop true exit}if}{pop}ifelse}forall}{false}ifelse
+end{{gsave TR -.1 .1 TR 1 1 scale Rx Ry false RMat{BDot}imagemask
+grestore}}{{gsave TR -.1 .1 TR Rx Ry scale 1 1 false RMat{BDot}
+imagemask grestore}}ifelse B/QV{gsave newpath transform round exch round
+exch itransform moveto Rx 0 rlineto 0 Ry neg rlineto Rx neg 0 rlineto
+fill grestore}B/a{moveto}B/delta 0 N/tail{A/delta X 0 rmoveto}B/M{S p
+delta add tail}B/b{S p tail}B/c{-4 M}B/d{-3 M}B/e{-2 M}B/f{-1 M}B/g{0 M}
+B/h{1 M}B/i{2 M}B/j{3 M}B/k{4 M}B/w{0 rmoveto}B/l{p -4 w}B/m{p -3 w}B/n{
+p -2 w}B/o{p -1 w}B/q{p 1 w}B/r{p 2 w}B/s{p 3 w}B/t{p 4 w}B/x{0 S
+rmoveto}B/y{3 2 roll p a}B/bos{/SS save N}B/eos{SS restore}B end
+
+%%EndProcSet
+%%BeginProcSet: 8r.enc 0 0
+% File 8r.enc TeX Base 1 Encoding Revision 2.0 2002-10-30
+%
+% @@psencodingfile@{
+% author = "S. Rahtz, P. MacKay, Alan Jeffrey, B. Horn, K. Berry,
+% W. Schmidt, P. Lehman",
+% version = "2.0",
+% date = "30 October 2002",
+% filename = "8r.enc",
+% email = "tex-fonts@@tug.org",
+% docstring = "This is the encoding vector for Type1 and TrueType
+% fonts to be used with TeX. This file is part of the
+% PSNFSS bundle, version 9"
+% @}
+%
+% The idea is to have all the characters normally included in Type 1 fonts
+% available for typesetting. This is effectively the characters in Adobe
+% Standard encoding, ISO Latin 1, Windows ANSI including the euro symbol,
+% MacRoman, and some extra characters from Lucida.
+%
+% Character code assignments were made as follows:
+%
+% (1) the Windows ANSI characters are almost all in their Windows ANSI
+% positions, because some Windows users cannot easily reencode the
+% fonts, and it makes no difference on other systems. The only Windows
+% ANSI characters not available are those that make no sense for
+% typesetting -- rubout (127 decimal), nobreakspace (160), softhyphen
+% (173). quotesingle and grave are moved just because it's such an
+% irritation not having them in TeX positions.
+%
+% (2) Remaining characters are assigned arbitrarily to the lower part
+% of the range, avoiding 0, 10 and 13 in case we meet dumb software.
+%
+% (3) Y&Y Lucida Bright includes some extra text characters; in the
+% hopes that other PostScript fonts, perhaps created for public
+% consumption, will include them, they are included starting at 0x12.
+% These are /dotlessj /ff /ffi /ffl.
+%
+% (4) hyphen appears twice for compatibility with both ASCII and Windows.
+%
+% (5) /Euro was assigned to 128, as in Windows ANSI
+%
+% (6) Missing characters from MacRoman encoding incorporated as follows:
+%
+% PostScript MacRoman TeXBase1
+% -------------- -------------- --------------
+% /notequal 173 0x16
+% /infinity 176 0x17
+% /lessequal 178 0x18
+% /greaterequal 179 0x19
+% /partialdiff 182 0x1A
+% /summation 183 0x1B
+% /product 184 0x1C
+% /pi 185 0x1D
+% /integral 186 0x81
+% /Omega 189 0x8D
+% /radical 195 0x8E
+% /approxequal 197 0x8F
+% /Delta 198 0x9D
+% /lozenge 215 0x9E
+%
+/TeXBase1Encoding [
+% 0x00
+ /.notdef /dotaccent /fi /fl
+ /fraction /hungarumlaut /Lslash /lslash
+ /ogonek /ring /.notdef /breve
+ /minus /.notdef /Zcaron /zcaron
+% 0x10
+ /caron /dotlessi /dotlessj /ff
+ /ffi /ffl /notequal /infinity
+ /lessequal /greaterequal /partialdiff /summation
+ /product /pi /grave /quotesingle
+% 0x20
+ /space /exclam /quotedbl /numbersign
+ /dollar /percent /ampersand /quoteright
+ /parenleft /parenright /asterisk /plus
+ /comma /hyphen /period /slash
+% 0x30
+ /zero /one /two /three
+ /four /five /six /seven
+ /eight /nine /colon /semicolon
+ /less /equal /greater /question
+% 0x40
+ /at /A /B /C
+ /D /E /F /G
+ /H /I /J /K
+ /L /M /N /O
+% 0x50
+ /P /Q /R /S
+ /T /U /V /W
+ /X /Y /Z /bracketleft
+ /backslash /bracketright /asciicircum /underscore
+% 0x60
+ /quoteleft /a /b /c
+ /d /e /f /g
+ /h /i /j /k
+ /l /m /n /o
+% 0x70
+ /p /q /r /s
+ /t /u /v /w
+ /x /y /z /braceleft
+ /bar /braceright /asciitilde /.notdef
+% 0x80
+ /Euro /integral /quotesinglbase /florin
+ /quotedblbase /ellipsis /dagger /daggerdbl
+ /circumflex /perthousand /Scaron /guilsinglleft
+ /OE /Omega /radical /approxequal
+% 0x90
+ /.notdef /.notdef /.notdef /quotedblleft
+ /quotedblright /bullet /endash /emdash
+ /tilde /trademark /scaron /guilsinglright
+ /oe /Delta /lozenge /Ydieresis
+% 0xA0
+ /.notdef /exclamdown /cent /sterling
+ /currency /yen /brokenbar /section
+ /dieresis /copyright /ordfeminine /guillemotleft
+ /logicalnot /hyphen /registered /macron
+% 0xD0
+ /degree /plusminus /twosuperior /threesuperior
+ /acute /mu /paragraph /periodcentered
+ /cedilla /onesuperior /ordmasculine /guillemotright
+ /onequarter /onehalf /threequarters /questiondown
+% 0xC0
+ /Agrave /Aacute /Acircumflex /Atilde
+ /Adieresis /Aring /AE /Ccedilla
+ /Egrave /Eacute /Ecircumflex /Edieresis
+ /Igrave /Iacute /Icircumflex /Idieresis
+% 0xD0
+ /Eth /Ntilde /Ograve /Oacute
+ /Ocircumflex /Otilde /Odieresis /multiply
+ /Oslash /Ugrave /Uacute /Ucircumflex
+ /Udieresis /Yacute /Thorn /germandbls
+% 0xE0
+ /agrave /aacute /acircumflex /atilde
+ /adieresis /aring /ae /ccedilla
+ /egrave /eacute /ecircumflex /edieresis
+ /igrave /iacute /icircumflex /idieresis
+% 0xF0
+ /eth /ntilde /ograve /oacute
+ /ocircumflex /otilde /odieresis /divide
+ /oslash /ugrave /uacute /ucircumflex
+ /udieresis /yacute /thorn /ydieresis
+] def
+
+
+%%EndProcSet
+%%BeginProcSet: texps.pro 0 0
+%!
+TeXDict begin/rf{findfont dup length 1 add dict begin{1 index/FID ne 2
+index/UniqueID ne and{def}{pop pop}ifelse}forall[1 index 0 6 -1 roll
+exec 0 exch 5 -1 roll VResolution Resolution div mul neg 0 0]FontType 0
+ne{/Metrics exch def dict begin Encoding{exch dup type/integertype ne{
+pop pop 1 sub dup 0 le{pop}{[}ifelse}{FontMatrix 0 get div Metrics 0 get
+div def}ifelse}forall Metrics/Metrics currentdict end def}{{1 index type
+/nametype eq{exit}if exch pop}loop}ifelse[2 index currentdict end
+definefont 3 -1 roll makefont/setfont cvx]cvx def}def/ObliqueSlant{dup
+sin S cos div neg}B/SlantFont{4 index mul add}def/ExtendFont{3 -1 roll
+mul exch}def/ReEncodeFont{CharStrings rcheck{/Encoding false def dup[
+exch{dup CharStrings exch known not{pop/.notdef/Encoding true def}if}
+forall Encoding{]exch pop}{cleartomark}ifelse}if/Encoding exch def}def
+end
+
+%%EndProcSet
+%%BeginFont: CMSY10
+%!PS-AdobeFont-1.1: CMSY10 1.0
+%%CreationDate: 1991 Aug 15 07:20:57
+% Copyright (C) 1997 American Mathematical Society. All Rights Reserved.
+11 dict begin
+/FontInfo 7 dict dup begin
+/version (1.0) readonly def
+/Notice (Copyright (C) 1997 American Mathematical Society. All Rights Reserved) readonly def
+/FullName (CMSY10) readonly def
+/FamilyName (Computer Modern) readonly def
+/Weight (Medium) readonly def
+/ItalicAngle -14.035 def
+/isFixedPitch false def
+end readonly def
+/FontName /CMSY10 def
+/PaintType 0 def
+/FontType 1 def
+/FontMatrix [0.001 0 0 0.001 0 0] readonly def
+/Encoding 256 array
+0 1 255 {1 index exch /.notdef put} for
+dup 13 /circlecopyrt put
+dup 102 /braceleft put
+dup 103 /braceright put
+dup 110 /backslash put
+readonly def
+/FontBBox{-29 -960 1116 775}readonly def
+currentdict end
+currentfile eexec
+D9D66F633B846A97B686A97E45A3D0AA052F09F9C8ADE9D907C058B87E9B6964
+7D53359E51216774A4EAA1E2B58EC3176BD1184A633B951372B4198D4E8C5EF4
+A213ACB58AA0A658908035BF2ED8531779838A960DFE2B27EA49C37156989C85
+E21B3ABF72E39A89232CD9F4237FC80C9E64E8425AA3BEF7DED60B122A52922A
+221A37D9A807DD01161779DDE7D31FF2B87F97C73D63EECDDA4C49501773468A
+27D1663E0B62F461F6E40A5D6676D1D12B51E641C1D4E8E2771864FC104F8CBF
+5B78EC1D88228725F1C453A678F58A7E1B7BD7CA700717D288EB8DA1F57C4F09
+0ABF1D42C5DDD0C384C7E22F8F8047BE1D4C1CC8E33368FB1AC82B4E96146730
+DE3302B2E6B819CB6AE455B1AF3187FFE8071AA57EF8A6616B9CB7941D44EC7A
+71A7BB3DF755178D7D2E4BB69859EFA4BBC30BD6BB1531133FD4D9438FF99F09
+4ECC068A324D75B5F696B8688EEB2F17E5ED34CCD6D047A4E3806D000C199D7C
+515DB70A8D4F6146FE068DC1E5DE8BC57030ACE57A0A31C99BEDB251A0ECAD78
+253AB320C099669BFED2878A6C4038A5BCEB5B4C28040BB8B4AC2C167BB2C999
+ED93E2F4AE3E4B44B7B6B469C36A24080D4BEDB5523D202E920D8898DEBD5EB0
+CAC3C5D5F01A87F7B15BD1C197B344F7D94E965CBFE73E8A65A2AA6A6F93B878
+03E30E60F1390C2FB671C8D36E7D6E516A20079B48CA9BE632F5A8D1250F01EE
+6809D6C5B51AE3D1B4C5F2F0273F59A8B6996BAC5E80E1D3465231006F548460
+8FAF92EC9040848022ACFB9FDCF92DB3C3BFCC5ECB83DF8FDF612FFA0143E790
+8BF3F08C9CFB528D9F148285297C422C3453AC567C162F9AF67A8C04A9C6EF24
+F22EAA0E1E3C019E87D4EE91AD20B46B9D0AA009258729F27189AC5CE11652D3
+D333BB22D9FA5C9493F0A0BD7535C830DC8B0DA576488CC641C193FE7CC27697
+A90351F201AA029742EC530597B0CC5F862A5DBB24A7C6D75B4A116E0B6A285C
+04A42215D39D5109AE37295051AF25C4A8317D461D4E67DF4E855356479B6F84
+DC5A56370F1B73CE160EF1D35C0C95598363456BAB251BB783EC8D0517B7D82E
+F6B8E6ECA69FC7DBD1A64E08A9AF313E04C3B00B4FE18D75E1229C7A482494F8
+A78672EFF2E156CC8052A5C44FE7A7DBC97E493C6519CEDE5B0B22461390B8B0
+363289D7F2AC693256A3943D6A0854C3CA39F7E7FAE5E36B0B9CA0A8C47BF5FC
+2CF48FD8F139786D179CF88ED2F083C1513BAB7963DCF8947184D707A37FE528
+61EB53D581AF2B61470F29AEE070A2E97F1425A4C8BBAE7B5AA8D9C59130F6BB
+AEF0C4B2B0AF4E9C145EBBA4EC69144632C9E3CF7F7DBB2E9D554CFCB784D942
+3081E604D1ACE9F686BA4F27240E4F1DBEFBF506685E4AEB0FF4B5D89A6EFE8F
+6043C819750B5720EAE0FE0227A34DA5D92B1BADF2AE24F673B1B2B02580F8D5
+AE65D4CFFBC195EFF321DF9D72F98B0516B5D141AA315F2362450C7F398DA68E
+170D12D7423F8E43DCBD5DCF8EF74BDE161BB620F43FAD0D106C6C3404C92611
+0CEAD737CDA74A1A328CBCEB4B47A9F01AB1EB00BF135902A187EE
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+cleartomark
+%%EndFont
+TeXDict begin 40258431 52099146 1000 600 600 (zsi.dvi)
+@start /Fa 166[50 1[50 2[50 3[50 50 50 8[50 50 50 67[{
+TeXBase1Encoding ReEncodeFont}9 83.022 /Courier-BoldOblique
+rf /Fb 134[72 4[72 3[72 72 72 72 72 2[72 3[72 72 72 14[72
+72 15[72 1[72 18[72 46[{TeXBase1Encoding ReEncodeFont}16
+119.552 /Courier rf /Fc 162[40 1[40 91[{TeXBase1Encoding ReEncodeFont}2
+119.552 /Times-Roman rf /Fd 134[124 2[124 124 124 124
+124 1[124 124 2[124 2[124 3[124 124 2[124 9[124 2[124
+124 2[124 9[124 2[124 67[{TeXBase1Encoding ReEncodeFont}19
+206.559 /Courier rf /Fe 130[45 1[45 45 45 45 45 45 45
+45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45
+45 45 45 45 1[45 1[45 45 45 45 45 45 45 45 45 45 45 45
+45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 1[45 45
+45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45
+45 45 45 45 45 45 45 45 45 45 45 33[{TeXBase1Encoding ReEncodeFont}89
+74.7198 /Courier rf /Ff 134[50 50 72 50 55 28 50 33 1[55
+55 55 83 22 50 1[22 55 55 28 55 55 50 1[55 13[66 2[66
+78 72 83 5[78 1[66 72 72 66 66 6[28 3[55 55 55 55 55
+55 55 1[28 46[{TeXBase1Encoding ReEncodeFont}42 99.6264
+/Helvetica rf /Fg 104[120 29[60 60 86 60 66 33 60 40
+1[66 66 66 100 27 2[27 66 66 33 66 66 60 66 66 8[80 2[86
+73 80 86 1[80 93 86 100 66 2[33 86 93 73 80 86 86 80
+80 7[66 66 66 66 66 66 66 66 66 66 1[33 40 45[{
+TeXBase1Encoding ReEncodeFont}54 119.552 /Helvetica rf
+/Fh 167[80 113 80 86 73 80 86 2[93 86 1[73 2[33 86 93
+73 80 86 86 86 86 65[{TeXBase1Encoding ReEncodeFont}19
+119.552 /Helvetica-Bold rf /Fi 104[83 28[42 42 42 60
+42 46 23 42 28 1[46 46 46 69 18 42 1[18 46 46 23 46 46
+42 46 46 8[55 2[60 51 55 60 1[55 65 60 69 46 2[23 60
+65 51 55 60 60 55 55 7[46 46 46 46 46 46 46 46 46 46
+1[23 28 45[{TeXBase1Encoding ReEncodeFont}56 83.022 /Helvetica
+rf /Fj 133[50 50 50 50 50 50 50 50 50 50 50 50 50 50
+50 50 1[50 50 50 50 50 50 50 50 50 1[50 4[50 50 50 50
+50 50 50 50 50 1[50 50 50 50 50 50 50 50 50 50 50 50
+50 50 50 50 10[50 1[50 5[50 4[50 50 40[{TeXBase1Encoding ReEncodeFont}
+56 83.022 /Courier-Bold rf /Fk 133[32 37 37 55 37 42
+23 32 32 42 42 42 42 60 23 37 23 23 42 42 23 37 42 37
+42 42 8[51 69 1[60 46 42 51 1[51 60 55 69 46 2[28 1[60
+51 1[60 55 51 51 3[56 11[42 42 1[21 28 21 1[42 39[42
+2[{TeXBase1Encoding ReEncodeFont}52 83.022 /Times-Italic
+rf /Fl 130[50 1[50 50 50 50 50 50 50 50 50 50 50 50 50
+50 50 50 50 50 50 50 50 50 50 50 50 50 50 1[50 1[50 1[50
+50 1[50 50 50 50 50 50 50 1[50 50 50 50 50 2[50 50 50
+50 50 50 50 50 50 3[50 2[50 1[50 1[50 1[50 50 50 50 50
+50 50 50 50 2[50 50 50 1[50 1[50 50 34[{TeXBase1Encoding ReEncodeFont}
+72 83.022 /Courier rf /Fm 145[42 6[42 42 88[83 13[{}4
+83.022 /CMSY10 rf /Fn 64[37 39[83 42 1[37 37 24[37 42
+42 60 42 42 23 32 28 42 42 42 42 65 23 42 23 23 42 42
+28 37 42 37 42 37 28 2[28 1[28 51 60 60 78 60 60 51 46
+55 60 46 60 60 74 51 60 32 28 60 60 46 51 60 55 55 60
+5[23 23 42 42 42 42 42 42 42 42 42 42 23 21 28 21 2[28
+28 28 35[46 46 2[{TeXBase1Encoding ReEncodeFont}81 83.022
+/Times-Roman rf /Fo 104[83 28[37 42 42 60 42 46 28 32
+37 1[46 42 46 69 23 46 1[23 46 42 28 37 46 37 46 42 7[60
+3[60 55 46 60 1[51 65 2[55 65 1[32 65 65 1[55 60 60 55
+60 7[42 42 42 42 42 42 42 42 42 42 1[21 28 42[46 2[{
+TeXBase1Encoding ReEncodeFont}55 83.022 /Times-Bold rf
+/Fp 133[37 37 1[54 1[42 21 37 25 1[42 42 42 62 17 37
+2[42 42 1[42 42 37 42 42 32[76 17[21 46[{TeXBase1Encoding ReEncodeFont}
+22 74.7198 /Helvetica rf /Fq 139[28 1[33 2[50 9[44 1[44
+50 18[72 24[50 50 2[50 1[50 3[25 44[{TeXBase1Encoding ReEncodeFont}12
+99.6264 /Times-Roman rf /Fr 133[72 4[80 40 72 48 1[80
+80 80 1[32 72 1[32 80 2[80 1[72 1[80 13[96 104 14[104
+96 21[40 44[{TeXBase1Encoding ReEncodeFont}20 143.462
+/Helvetica rf /Fs 140[72 6[32 6[80 3[80 14[104 31[80
+1[80 1[40 46[{TeXBase1Encoding ReEncodeFont}8 143.462
+/Helvetica-Oblique rf /Ft 104[207 28[103 103 103 149
+103 115 57 103 69 1[115 115 115 172 46 103 1[46 115 115
+57 115 115 103 115 115 6[126 138 3[149 126 138 149 1[138
+161 149 1[115 2[57 149 161 1[138 149 149 138 138 6[57
+18[46 39[{TeXBase1Encoding ReEncodeFont}45 206.559 /Helvetica
+rf end
+%%EndProlog
+%%BeginSetup
+%%Feature: *Resolution 600dpi
+TeXDict begin
+%%BeginPaperSize: Letter
+letter
+%%EndPaperSize
+ end
+%%EndSetup
+%%Page: 1 1
+TeXDict begin 1 0 bop 0 83 3901 9 v 652 446 a Ft(ZSI:)58
+b(The)g(Zoler)n(a)e(Soap)h(Infr)n(astr)s(ucture)2223
+695 y(De)-6 b(v)h(eloper')-10 b(s)56 b(Guide)3015 925
+y Fs(Release)38 b(2.0.0)3254 2045 y Fr(Rich)h(Salz,)2689
+2228 y(Chr)r(istopher)e(Blunc)m(k)3206 4172 y Fq(October)24
+b(25,)h(2006)3161 5201 y Fp(rsalz@datapo)o(w)o(er)l(.com)3260
+5317 y(b)o(lunc)o(k@p)n(ython.org)p eop end
+%%Page: 2 2
+TeXDict begin 2 1 bop 1694 83 a Fo(COPYRIGHT)0 230 y
+Fn(Cop)o(yright)380 227 y(c)357 230 y Fm(\015)20 b Fn(2001,)f(Zolera)g
+(Systems,)i(Inc.)0 330 y(All)g(Rights)f(Reserv)o(ed.)0
+476 y(Cop)o(yright)380 473 y(c)357 476 y Fm(\015)g Fn(2002-2003,)d
+(Rich)j(Salz.)0 576 y(All)h(Rights)f(Reserv)o(ed.)0 723
+y(Permission)27 b(is)h(hereby)d(granted,)i(free)g(of)g(char)o(ge,)g(to)
+g(an)o(y)f(person)g(obtaining)g(a)h(cop)o(y)f(of)h(this)h(softw)o(are)f
+(and)f(associated)h(docu-)0 823 y(mentation)20 b(\002les)j(\(the)e
+(\224Softw)o(are\224\),)g(to)g(deal)h(in)f(the)h(Softw)o(are)f(without)
+g(restriction,)g(including)f(without)g(limitation)i(the)f(rights)g(to)0
+922 y(use,)k(cop)o(y)-5 b(,)24 b(modify)-5 b(,)23 b(mer)o(ge,)h
+(publish,)g(distrib)n(ute,)h(and/or)d(sell)k(copies)e(of)g(the)g(Softw)
+o(are,)g(and)g(to)h(permit)e(persons)h(to)g(whom)g(the)0
+1022 y(Softw)o(are)i(is)i(furnished)c(to)j(do)f(so,)j(pro)o(vided)24
+b(that)i(the)h(abo)o(v)o(e)e(cop)o(yright)f(notice\(s\))i(and)g(this)h
+(permission)f(notice)g(appear)f(in)i(all)0 1121 y(copies)g(of)g(the)g
+(Softw)o(are)g(and)g(that)g(both)f(the)i(abo)o(v)o(e)d(cop)o(yright)h
+(notice\(s\))g(and)h(this)g(permission)g(notice)f(appear)g(in)i
+(supporting)0 1221 y(documentation.)0 1368 y(THE)e(SOFTW)-10
+b(ARE)26 b(IS)g(PR)m(O)l(VIDED)g(\224AS)g(IS\224,)h(WITHOUT)e(W)-10
+b(ARRANTY)27 b(OF)f(ANY)g(KIND,)g(EXPRESS)g(OR)h(IMPLIED,)0
+1468 y(INCLUDING)f(B)o(UT)h(NO)m(T)g(LIMITED)e(T)o(O)h(THE)g(W)-10
+b(ARRANTIES)27 b(OF)h(MERCHANT)-8 b(ABILITY)d(,)26 b(FITNESS)g(FOR)i(A)
+f(P)-8 b(AR-)0 1567 y(TICULAR)29 b(PURPOSE)g(AND)f(NONINFRINGEMENT)g
+(OF)g(THIRD)h(P)-8 b(AR)j(TY)29 b(RIGHTS.)f(IN)h(NO)f(EVENT)g(SHALL)h
+(THE)0 1667 y(COPYRIGHT)19 b(HOLDER)g(OR)g(HOLDERS)g(INCLUDED)f(IN)h
+(THIS)g(NO)m(TICE)f(BE)h(LIABLE)f(FOR)i(ANY)e(CLAIM,)h(OR)g(ANY)0
+1766 y(SPECIAL)25 b(INDIRECT)g(OR)h(CONSEQ)o(UENTIAL)f(D)m(AMA)m(GES,)f
+(OR)i(ANY)f(D)m(AMA)m(GES)f(WHA)-9 b(TSOEVER)25 b(RESUL)-8
+b(TING)0 1866 y(FR)m(OM)27 b(LOSS)g(OF)g(USE,)f(D)m(A)-9
+b(T)h(A)27 b(OR)g(PR)m(OFITS,)f(WHETHER)h(IN)f(AN)h(A)m(CTION)f(OF)h
+(CONTRA)m(CT)-6 b(,)27 b(NEGLIGENCE)f(OR)0 1966 y(O)m(THER)20
+b(T)o(OR)-5 b(TIOUS)20 b(A)m(CTION,)g(ARISING)h(OUT)f(OF)h(OR)h(IN)e
+(CONNECTION)h(WITH)f(THE)g(USE)h(OR)g(PERFORMANCE)0 2065
+y(OF)g(THIS)f(SOFTW)-10 b(ARE.)0 2212 y(Except)27 b(as)i(contained)d
+(in)i(this)g(notice,)h(the)f(name)f(of)h(a)g(cop)o(yright)e(holder)g
+(shall)j(not)e(be)h(used)g(in)g(adv)o(ertising)e(or)h(otherwise)g(to)0
+2312 y(promote)18 b(the)j(sale,)f(use)h(or)f(other)f(dealings)g(in)i
+(this)g(Softw)o(are)e(without)h(prior)f(written)h(authorization)e(of)h
+(the)i(cop)o(yright)d(holder)-5 b(.)1625 2766 y Fo(Ackno)o(wledgments)0
+2913 y Fn(W)e(e)60 b(are)e(grateful)f(to)i(the)f(members)g(of)g(the)g
+Fl(soapbuilders)f Fn(mailing)h(list)h(\(see)g Fl(http://groups.yahoo.)0
+3012 y(com/soapbuilders)p Fn(\),)45 b(Fredrik)d(Lundh)e(for)i(his)h
+Fl(soaplib)f Fn(package)f(\(see)i Fl(http://www.secretlabs.com/)0
+3112 y(downloads/index.htm)p Fm(n)p Fl(#soap)p Fn(\),)20
+b(Cayce)25 b(Ullman)f(and)g(Brian)g(Matthe)n(ws)h(for)e(their)i
+Fl(SOAP.py)e Fn(package)g(\(see)i Fl(http:)0 3211 y
+(//sourceforge.net/projects/pywebsvc)o(s)p Fn(\).)0 3358
+y(W)-7 b(e)31 b(are)f(particularly)e(grateful)g(to)i(Brian)g(Llo)o(yd)f
+(and)g(the)h(Zope)f(Corporation)e(\()p Fl(http://www.zope.com)p
+Fn(\))g(for)i(letting)g(us)0 3458 y(incorporate)18 b(his)i(ZOPE)h(W)-7
+b(ebServices)20 b(package)f(and)g(documentation)f(into)h
+Fl(ZSI)p Fn(.)p eop end
+%%Page: 1 3
+TeXDict begin 1 2 bop 1796 2336 a Fo(Abstract)0 2566
+y Fl(ZSI)p Fn(,)24 b(the)h(Zolera)e(SO)m(AP)j(Infrastructure,)c(is)k(a)
+f(Python)e(package)g(that)i(pro)o(vides)d(an)j(implementation)d(of)i
+(SO)m(AP)h(messaging,)g(as)0 2666 y(described)d(in)i
+Fk(The)g(SO)-5 b(AP)23 b(1.1)g(Speci\002cation)p Fn(.)33
+b(In)23 b(particular)m(,)f Fl(ZSI)i Fn(parses)g(and)f(generates)f(SO)m
+(AP)j(messages,)f(and)f(con)m(v)o(erts)f(be-)0 2766 y(tween)29
+b(nati)n(v)o(e)f(Python)f(datatypes)h(and)h(SO)m(AP)g(syntax.)50
+b(It)30 b(can)e(also)h(be)g(used)g(to)g(b)n(uild)f(applications)g
+(using)g Fk(SO)-5 b(AP)29 b(Messa)o(g)o(es)0 2865 y(with)24
+b(Attac)o(hments)p Fn(.)33 b Fl(ZSI)24 b Fn(is)g(\223transport)e
+(neutral\224,)h(and)g(pro)o(vides)f(only)g(a)i(simple)g(I/O)f(and)g
+(dispatch)g(frame)n(w)o(ork;)g(a)h(more)e(com-)0 2965
+y(plete)h(solution)g(is)h(the)g(responsibility)e(of)h(the)h
+(application)e(using)h Fl(ZSI)p Fn(.)g(As)h(usage)f(patterns)g(emer)o
+(ge,)g(and)f(common)g(application)0 3065 y(frame)n(w)o(orks)c(are)i
+(more)g(understood,)d(this)k(may)f(change.)0 3211 y Fl(ZSI)g
+Fn(requires)f(Python)g(2.3)h(or)g(later)g(and)g(PyXML)g(v)o(ersion)f
+(0.8.3)f(or)i(later)-5 b(.)0 3358 y(The)20 b Fl(ZSI)g
+Fn(homepage)e(is)j(at)g Fl(http://pywebsvcs.sf.net/)p
+Fn(.)p eop end
+%%Page: 2 4
+TeXDict begin 2 3 bop eop end
+%%Page: 1 5
+TeXDict begin 1 4 bop 2764 747 a Ft(CONTENTS)0 1594 y
+Fo(1)83 b(Intr)o(oduction)3281 b(1)125 1694 y Fn(1.1)110
+b(Ho)n(w)20 b(to)h(Read)f(this)h(Document)42 b(.)g(.)f(.)g(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)143
+b(2)0 1876 y Fo(2)83 b(Examples)3387 b(3)125 1976 y Fn(2.1)110
+b(Serv)o(er)20 b(Side)g(Examples)68 b(.)41 b(.)g(.)g(.)h(.)f(.)g(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)143
+b(3)125 2076 y(2.2)110 b(Client)21 b(Side)f(Examples)81
+b(.)41 b(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)
+g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)143 b(7)0 2258 y Fo(3)83
+b(Exceptions)3304 b(11)0 2441 y(4)83 b(Utilities)3415
+b(13)125 2540 y Fn(4.1)110 b(Lo)n(w-Le)n(v)o(el)19 b(Utilities)25
+b(.)41 b(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f
+(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(13)0 2723
+y Fo(5)83 b(The)21 b Fj(ParsedSoap)e Fo(module)h(\227)h(basic)g
+(message)f(handling)1801 b(15)0 2906 y(6)83 b(The)21
+b Fj(TypeCode)e Fo(classes)i(\227)g(data)e(con)m(v)o(ersions)2165
+b(19)125 3005 y Fn(6.1)110 b Fl(TC.TypeCode)65 b Fn(.)41
+b(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)
+g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(19)125 3105
+y(6.2)110 b Fl(TC.Any)20 b Fn(\227)h(the)f(basis)h(of)f(dynamic)e
+(typing)81 b(.)41 b(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)
+102 b(21)125 3205 y(6.3)110 b Fl(TC.SimpleType)27 b Fn(.)41
+b(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(23)125 3304 y(6.4)110
+b(Strings)67 b(.)42 b(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)g(.)102 b(23)125 3404 y(6.5)110 b(Inte)o(gers)35 b(.)42
+b(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)
+f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102
+b(24)125 3504 y(6.6)110 b(Floating-point)18 b(Numbers)62
+b(.)41 b(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)
+g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(25)125 3603 y(6.7)110
+b(Dates)21 b(and)f(T)m(imes)59 b(.)41 b(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)
+f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g
+(.)102 b(26)125 3703 y(6.8)110 b(Boolean)25 b(.)42 b(.)f(.)g(.)g(.)h(.)
+f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)
+g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(26)125 3802
+y(6.9)110 b(XML)56 b(.)41 b(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g
+(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)
+g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h
+(.)f(.)g(.)g(.)102 b(27)125 3902 y(6.10)68 b(Comple)o(xT)-7
+b(ype)20 b(.)41 b(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102
+b(27)125 4002 y(6.11)68 b(Struct)42 b(.)f(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)
+g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(28)125 4101 y(6.12)68
+b(Arrays)76 b(.)42 b(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)
+g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)
+g(.)102 b(28)125 4201 y(6.13)68 b(Apache)20 b(Datatype)39
+b(.)i(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)
+f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(29)0 4384
+y Fo(7)83 b(The)21 b Fj(SoapWriter)e Fo(module)h(\227)h(serializing)f
+(data)2084 b(31)0 4566 y(8)83 b(The)21 b Fj(Fault)f Fo(module)g(\227)h
+(r)o(eporting)e(err)o(ors)2299 b(33)0 4749 y(9)83 b(The)21
+b Fj(resolvers)e Fo(module)h(\227)h(fetching)f(r)o(emote)f(data)1944
+b(35)0 4932 y(10)41 b(Dispatching)20 b(and)g(In)m(v)o(oking)2778
+b(37)125 5031 y Fn(10.1)68 b(Dispatching)25 b(.)41 b(.)g(.)h(.)f(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)
+f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(37)125 5131 y(10.2)68
+b(The)20 b Fl(client)g Fn(module)f(\227)h(sending)g(SO)m(AP)h(messages)
+69 b(.)41 b(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h
+(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(40)0
+5313 y Fo(11)41 b(Bibliograph)o(y)3226 b(43)p 0 5549
+3901 4 v 3882 5649 a Fi(i)p eop end
+%%Page: 2 6
+TeXDict begin 2 5 bop 0 83 a Fo(A)65 b(CGI)20 b(Script)h(Array)3059
+b(47)125 183 y Fn(A.1)92 b(Intro)78 b(.)41 b(.)h(.)f(.)g(.)g(.)h(.)f(.)
+g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(47)125 282 y(A.2)92
+b(CGI)21 b(Script)65 b(.)41 b(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)g(.)102 b(47)125 382 y(A.3)92 b(client)21 b(test)g(script)k(.)42
+b(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)
+f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(48)125
+482 y(A.4)92 b(SO)m(AP)21 b(T)m(race)73 b(.)41 b(.)h(.)f(.)g(.)h(.)f(.)
+g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)
+g(.)h(.)f(.)g(.)g(.)102 b(48)0 664 y Fo(B)70 b(CGI)20
+b(Script)h(Struct)3055 b(53)125 764 y Fn(B.1)97 b(Intro)78
+b(.)41 b(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102
+b(53)125 863 y(B.2)97 b(CGI)21 b(Script)65 b(.)41 b(.)g(.)h(.)f(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)
+g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(53)125 963 y(B.3)97
+b(client)21 b(test)g(script)k(.)42 b(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)
+g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)
+g(.)102 b(54)125 1063 y(B.4)97 b(SO)m(AP)21 b(T)m(race)73
+b(.)41 b(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)
+h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102
+b(54)0 1245 y Fo(C)65 b(Complete)20 b(Lo)o(w)g(Le)o(v)o(el)h(Example)
+2627 b(55)125 1345 y Fn(C.1)97 b(Intro)78 b(.)41 b(.)h(.)f(.)g(.)g(.)h
+(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)
+h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f
+(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102 b(55)125 1445
+y(C.2)97 b(code)21 b(.)41 b(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)
+f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g
+(.)h(.)f(.)g(.)g(.)102 b(55)125 1544 y(C.3)97 b(SO)m(AP)21
+b(T)m(race)73 b(.)41 b(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)
+g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)102
+b(59)0 1727 y Fo(D)65 b(pickler)20 b(example)3127 b(61)125
+1826 y Fn(D.1)92 b(Intro)78 b(.)41 b(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)
+f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g
+(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)
+g(.)g(.)h(.)f(.)g(.)g(.)102 b(61)125 1926 y(D.2)92 b(code)21
+b(.)41 b(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)
+g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)
+102 b(61)p 0 5549 3901 4 v 0 5649 a Fi(ii)p eop end
+%%Page: 1 7
+TeXDict begin 1 6 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3641
+427 y Fh(ONE)p 0 515 V 2821 978 a Ft(Introduction)0 1457
+y Fl(ZSI)p Fn(,)18 b(the)g(Zolera)g(SO)m(AP)h(Infrastructure,)d(is)j(a)
+f(Python)f(package)g(that)i(pro)o(vides)d(an)i(implementation)e(of)i
+(the)g(SO)m(AP)i(speci\002cation,)0 1556 y(as)26 b(described)f(in)g
+Fk(The)h(SO)-5 b(AP)25 b(1.1)g(Speci\002cation)p Fn(.)39
+b(In)25 b(particular)m(,)g Fl(ZSI)h Fn(parses)f(and)g(generates)g(SO)m
+(AP)h(messages,)h(and)e(con)m(v)o(erts)0 1656 y(between)19
+b(nati)n(v)o(e)h(Python)f(datatypes)g(and)h(SO)m(AP)h(syntax.)0
+1803 y Fl(ZSI)f Fn(requires)f(Python)g(2.3)h(or)g(later)g(and)g(PyXML)g
+(v)o(ersion)f(0.8.3)f(or)i(later)-5 b(.)0 1950 y(The)20
+b Fl(ZSI)g Fn(project)f(is)i(maintained)e(at)i(SourceF)o(or)o(ge,)c(at)
+j Fl(http://pywebsvcs.sf.net)p Fn(.)i Fl(ZSI)e Fn(is)h(discussed)f(on)g
+(the)g(Python)0 2049 y(web)c(services)f(mailing)g(list,)i(visit)g
+Fl(http://lists.sourceforge.net/lists)o(/list)o(info/)o(pywebs)o(vcs-)t
+(ta)o(lk)0 2149 y Fn(to)j(subscribe.)0 2296 y(F)o(or)j(those)h
+(interested)f(in)g(using)h(the)f Fo(wsdl2py)h Fn(tool)f(see)i(the)e
+Fk(User)o(s)i(Guide)p Fn(,)f(it)g(contains)f(a)h(detailed)f(e)o(xample)
+g(of)g(ho)n(w)g(to)h(use)g(the)0 2395 y(code)c(generation)e(f)o
+(acilities)j(in)f Fl(ZSI)p Fn(.)0 2542 y(F)o(or)f(those)g(interested)f
+(in)i(a)f(high-le)n(v)o(el)e(tutorial)i(co)o(v)o(ering)e
+Fl(ZSI)i Fn(and)f(why)h(Python)f(w)o(as)i(chosen,)e(see)i(the)f
+(article)g Fl(http://www.)0 2642 y(xml.com/pub/a/ws/2002/06/12/soap.ht)
+o(ml)p Fn(,)c(written)20 b(by)f(Rich)i(Salz)g(for)e(xml.com.)0
+2789 y(SO)m(AP-based)j(processing)e(typically)h(in)m(v)n(olv)o(es)g(se)
+n(v)o(eral)g(steps.)31 b(The)22 b(follo)n(wing)e(list)k(details)e(the)g
+(steps)h(of)e(a)i(common)d(processing)0 2888 y(model)f(naturally)g
+(supported)f(by)i Fl(ZSI)g Fn(\(other)f(models)h(are)g(certainly)f
+(possible\):)104 3100 y(1.)41 b Fl(ZSI)27 b Fn(tak)o(es)h(data)f(from)f
+(an)h(input)g(stream)g(and)g Fk(par)o(ses)h Fn(it,)h(generating)c(a)j
+(DOM-based)e(parse)h(tree)h(as)g(part)f(of)g(creating)f(a)208
+3200 y Fl(ParsedSoap)i Fn(object.)53 b(At)30 b(this)g(point)f(the)h
+(major)e(syntactic)i(elements)f(of)g(a)i(SO)m(AP)f(message)f(\227)i
+(the)e Fl(Header)p Fn(,)j(the)208 3299 y Fl(Body)p Fn(,)19
+b(etc.)26 b(\227)20 b(are)h(a)n(v)n(ailable.)104 3465
+y(2.)41 b(The)23 b(application)f(does)i Fk(header)e(pr)l(ocessing)p
+Fn(.)35 b(More)23 b(speci\002cally)-5 b(,)24 b(it)g(does)g(local)g
+(dispatch)f(and)g(processing)f(based)h(on)h(the)208 3564
+y(elements)d(in)g(the)g(SO)m(AP)i Fl(Header)p Fn(.)k(The)21
+b(SO)m(AP)i Fl(actor)d Fn(and)h Fl(mustUnderstand)f Fn(attrib)n(utes)h
+(are)g(also)h(handled)d(\(or)i(at)208 3664 y(least)f(recognized\))e
+(here.)104 3829 y(3.)41 b Fl(ZSI)24 b Fn(ne)o(xt)f Fk(par)o(ses)i
+Fn(the)f Fl(Body)p Fn(,)g(creating)f(local)h(Python)f(objects)h(from)f
+(the)h(data)g(in)g(the)g(SO)m(AP)h(message.)37 b(The)24
+b(parsing)f(is)208 3929 y(often)e(under)g(the)h(control)f(of)h(a)h
+(list)g(of)f(data)g(descriptions,)f(kno)n(wn)g(as)i Fk(typecodes)p
+Fn(,)e(de\002ned)h(by)f(the)i(application)d(because)i(it)208
+4029 y(kno)n(ws)15 b(what)h(type)f(of)h(data)f(it)i(is)g(e)o(xpecting.)
+k(In)16 b(cases)h(where)e(the)h(SO)m(AP)h(data)e(is)i(kno)n(wn)e(to)h
+(be)f(completely)g(self-describing,)208 4128 y(the)20
+b(parsing)f(can)h(be)g Fk(dynamic)f Fn(through)f(the)i(use)h(of)f(the)g
+Fl(TC.Any)f Fn(class.)104 4294 y(4.)41 b(The)16 b(application)f(no)n(w)
+i Fk(dispatc)o(hes)e Fn(to)j(the)e(appropriate)f(handler)g(in)i(order)f
+(to)h(do)f(its)i(\223real)f(w)o(ork.)-6 b(\224)23 b(As)18
+b(part)e(of)h(its)h(processing)208 4393 y(it)i(may)g(create)g
+Fk(output)f(objects)104 4559 y Fn(5.)41 b(The)27 b(application)g
+(creates)i(a)f Fl(SoapWriter)f Fn(instance)h(and)g(outputs)f(an)h
+(initial)h(set)g(of)f(namespace)f(entries)h(and)g(header)208
+4658 y(elements.)104 4824 y(6.)41 b(An)o(y)20 b(local)h(data)g(to)g(be)
+g(sent)g(back)f(to)h(the)g(client)g(is)h Fk(serialized)p
+Fn(.)28 b(As)21 b(with)h Fl(Body)e Fn(parsing,)g(the)h(datatypes)f(can)
+h(be)g(described)208 4923 y(through)d(typecodes)g(or)i(determined)f
+(dynamically)f(\(here,)h(through)f(introspection\).)104
+5089 y(7.)41 b(In)19 b(the)i(e)n(v)o(ent)e(of)h(an)o(y)f(processing)g
+(e)o(xceptions,)f(a)j Fl(Fault)f Fn(object)g(can)g(be)g(raised,)f
+(created,)h(and/or)e(serialized.)0 5300 y(Note)25 b(that)h
+Fl(ZSI)g Fn(is)g(\223transport)e(neutral\224,)i(and)f(pro)o(vides)f
+(only)g(a)i(simple)g(I/O)f(and)g(dispatch)g(frame)n(w)o(ork;)h(a)g
+(more)f(complete)g(so-)0 5400 y(lution)e(is)i(a)n(v)n(ailable)e
+(through)f(the)i(use)g(of)f(included)f(WSDL)j(tools)f(\()p
+Fo(wsdl2py)p Fn(\),)g(b)n(ut)g(otherwise)f(this)h(is)h(the)f
+(responsibility)e(of)i(the)p 0 5549 3901 4 v 3854 5649
+a Fi(1)p eop end
+%%Page: 2 8
+TeXDict begin 2 7 bop 0 83 a Fn(application)16 b(using)h
+Fl(ZSI)p Fn(.)h(As)h(usage)e(patterns)g(emer)o(ge,)f(and)i(common)d
+(application)i(frame)n(w)o(orks)f(are)h(more)g(understood,)f(this)i
+(may)0 183 y(change.)0 330 y(W)m(ithin)j(this)g(document,)d
+Fl(tns)j Fn(is)g(used)g(as)g(the)g(pre\002x)f(for)g(the)g(application')
+-5 b(s)20 b(tar)o(get)g(namespace,)f(and)h(the)h(term)f
+Fk(element)g Fn(refers)g(to)0 429 y(a)h(DOM)f(element)g(node.\))0
+757 y Fg(1.1)121 b(Ho)n(w)34 b(to)g(Read)h(this)e(Document)0
+989 y Fn(Readers)27 b(interested)f(in)i(using)e(WSDL)i(and)e(clients)i
+(to)f(web)g(services,)h(and)f(those)g(intending)e(on)i(implementing)e
+(web)i(services)0 1089 y(based)c(on)g(e)o(xisting)f(WSDL)i(should)e
+(refer)h(to)g(the)g Fk(User)o(s)i(Guide)p Fn(.)33 b(Others)23
+b(interested)g(in)g(de)n(v)o(eloping)e(the)i(simplest)g(SO)m(AP)h
+(appli-)0 1189 y(cations,)d(or)h(spending)e(the)h(least)i(amount)d(of)h
+(time)h(on)f(b)n(uilding)g(a)h(web)f(services)h(infrastructure)d
+(should)i(read)g(chapters)g(2,)g(3,)h(and)0 1288 y(10)g(of)g(this)g
+(document.)29 b(Readers)22 b(who)g(are)g(de)n(v)o(eloping)e(comple)o(x)
+g(services,)i(and)g(who)g(are)g(f)o(amiliar)g(with)g(XML)g(Schema)g
+(and/or)0 1388 y(WSDL,)17 b(should)f(read)h(this)g(manual)f(in)h(order)
+-5 b(.)23 b(This)17 b(will)h(pro)o(vide)c(them)j(with)g(enough)e
+(information)f(to)j(implement)f(the)h(processing)0 1488
+y(model)i(described)g(abo)o(v)o(e.)k(The)o(y)d(can)g(skip)g(probably)e
+(skip)i(chapters)f(2)h(and)g(10.)0 1634 y Fl(ZSI)27 b
+Fn(has)f(the)h(capability)e(to)i(process)f(WSDL)h(de\002nitions)f(and)g
+(XML)h(Schema)e(documents)g(\(described)g(in)i Fk(The)f(W)-8
+b(eb)27 b(Services)0 1734 y(Description)i(Langua)o(g)o(e)f
+Fn(and)h Fk(XMLSc)o(hema)g(1.0)p Fn(\))f(and)i(generate)e(typecodes)h
+(automatically)-5 b(.)51 b(F)o(or)30 b(more)f(information)e(see)k(the)0
+1834 y Fk(User)o(s)22 b(Guide)p Fn(.)p 0 5549 3901 4
+v 0 5649 a Fi(2)2981 b(Chapter)23 b(1.)52 b(Introduction)p
+eop end
+%%Page: 3 9
+TeXDict begin 3 8 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3623
+427 y Fh(TW)n(O)p 0 515 V 2993 978 a Ft(Examples)0 1465
+y Fn(This)25 b(chapter)f(contains)g(a)i(number)d(of)i(e)o(xamples)f(to)
+h(sho)n(w)g(of)n(f)f(some)h(of)g Fl(ZSI)p Fn(')-5 b(s)25
+b(features.)39 b(It)25 b(is)h(brok)o(en)d(do)n(wn)h(into)h(client-side)
+0 1565 y(and)20 b(serv)o(er)n(-side)f(e)o(xamples,)g(and)g(e)o(xplores)
+g(dif)n(ferent)f(implementation)g(options)i Fl(ZSI)g
+Fn(pro)o(vides.)0 1892 y Fg(2.1)121 b(Ser)t(v)m(er)33
+b(Side)g(Examples)0 2141 y Ff(2.1.1)100 b(Simple)29 b(e)m(xample)0
+2344 y Fn(Using)18 b(the)f Fl(ZSI.dispatch)f Fn(module,)h(it)h(is)g
+(simple)g(to)g(e)o(xpose)e(Python)h(functions)f(as)i(web)g(services.)24
+b(Each)17 b(function)f(is)i(in)m(v)n(ok)o(ed)0 2444 y(with)31
+b(all)h(the)f(input)g(parameters)e(speci\002ed)i(in)h(the)f(client')-5
+b(s)31 b(SO)m(AP)h(request.)57 b(An)o(y)31 b(v)n(alue)f(returned)f(by)i
+(the)g(function)f(will)i(be)0 2544 y(serialized)20 b(back)f(to)i(the)f
+(client;)g(multiple)g(v)n(alues)g(can)g(be)g(returned)e(by)i(returning)
+e(a)j(tuple.)0 2691 y(The)f(follo)n(wing)e(code)i(sho)n(ws)g(some)g
+(simple)g(services:)236 2929 y Fe(#!/usr/local/bin/python2.4)236
+3020 y(#)45 b(SOAP)f(Array)236 3203 y(def)h(hello\(\):)416
+3294 y(return)e(["Hello,)h(world"])236 3477 y(def)h(echo\()641
+3490 y(*)686 3477 y(args\):)416 3568 y(return)e(args)236
+3751 y(def)i(sum\()596 3764 y(*)641 3751 y(args\):)416
+3842 y(sum)f(=)h(0)416 3933 y(for)f(i)h(in)f(args:)g(sum)g(+=)h(i)416
+4025 y(return)e([sum])236 4207 y(def)i(average\()776
+4220 y(*)821 4207 y(args\):)416 4299 y(return)e([sum\()954
+4312 y(*)999 4299 y(args\))g(/)i(len\(args\)])236 4481
+y(from)f(ZSI)h(import)f(dispatch)236 4573 y(dispatch.AsCGI\(rpc=True\))
+0 4902 y Fn(Each)15 b(function)f(de\002nes)h(a)h(SO)m(AP)g(request,)f
+(so)h(if)g(this)f(script)h(is)g(installed)g(as)g(a)f(CGI)h(script,)g(a)
+g(SO)m(AP)h(message)e(can)g(be)g(posted)g(to)g(that)0
+5002 y(script')-5 b(s)22 b(URL)g(with)g(an)o(y)e(of)i
+Fl(hello)p Fn(,)f Fl(echo)p Fn(,)g(or)g Fl(average)g
+Fn(as)h(the)g(request)f(element,)g(and)g(the)g(v)n(alue)g(returned)f
+(by)h(the)g(function)0 5101 y(will)32 b(be)g(sent)g(back.)58
+b(These)31 b(functions)g(e)o(xpect)f(and)h(return)g(SO)m(AP-ENC:arrayT)
+-7 b(ype)29 b(instances)j(which)f(are)g(marshalled)g(into)0
+5201 y(p)o(ython)17 b Fl(list)i Fn(instances,)g(this)g(script)g
+(interoperates)e(with)i(the)g Fl(client.Binding)p Fn(.)j(F)o(or)d(more)
+f(information)e(see)k Fk(Appendix)d(A)p Fn(.)0 5348 y(The)26
+b Fl(ZSI)h Fn(CGI)g(dispatcher)e(catches)i(e)o(xceptions)e(and)h(sends)
+h(back)f(a)h(SO)m(AP)g(f)o(ault.)44 b(F)o(or)27 b(e)o(xample,)f(a)h(f)o
+(ault)g(will)g(be)g(sent)g(if)g(the)p 0 5549 3901 4 v
+3854 5649 a Fi(3)p eop end
+%%Page: 4 10
+TeXDict begin 4 9 bop 0 83 a Fl(hello)20 b Fn(function)e(is)j(gi)n(v)o
+(en)e(an)o(y)h(ar)o(guments,)e(or)i(if)g(the)g Fl(average)g
+Fn(function)e(is)j(gi)n(v)o(en)e(a)i(non-inte)o(ger)-5
+b(.)0 230 y(Here)17 b(is)i(another)c(e)o(xample)h(b)n(ut)i(using)f(SO)m
+(AP-ENC:Struct)g(instances)g(which)g(are)g(marshalled)f(into)i(p)o
+(ython)d Fl(dict)i Fn(instances,)h(this)0 330 y(script)i(interoperates)
+f(with)h(the)g Fl(client.NamedParamBinding)p Fn(.)h(F)o(or)f(more)g
+(information)d(see)k Fk(Appendix)d(B)p Fn(.)236 476 y
+Fe(#!/usr/local/bin/python2.4)236 568 y(#)45 b(SOAP)f(Struct)236
+750 y(def)h(hello\(\):)416 842 y(return)e({"value":"Hello,)g(world"})
+236 1024 y(def)i(echo\()641 1037 y(**)731 1024 y(kw\):)416
+1116 y(return)e(kw)236 1298 y(def)i(sum\()596 1311 y(**)686
+1298 y(kw\):)416 1390 y(sum)f(=)h(0)416 1481 y(for)f(i)h(in)f
+(kw.values\(\):)f(sum)h(+=)h(i)236 1572 y(return)f({"value":sum})236
+1755 y(def)h(average\()776 1768 y(**)866 1755 y(kw\):)236
+1846 y(d)g(=)g(sum\()596 1859 y(**)686 1846 y(kw\))416
+1938 y(return)e(d["value"])h(=)g(d["value"]/len\(kw\))236
+2120 y(from)g(ZSI)h(import)f(dispatch)236 2212 y
+(dispatch.AsCGI\(rpc=True\))0 2679 y Ff(2.1.2)100 b(lo)o(w)28
+b(le)m(v)n(el)g(soap)h(processing)g(e)m(xample)0 2882
+y Fn(W)-7 b(e)21 b(will)f(no)n(w)e(sho)n(w)i(a)f(more)g(complete)f(e)o
+(xample)g(of)h(a)h(rob)n(ust)f(web)g(service)g(implemented)f(at)i(the)f
+(SO)m(AP)h(layer)-5 b(.)25 b(It)19 b(tak)o(es)h(as)g(input)0
+2982 y(a)k(player)f(name)g(and)h(array)f(of)g(inte)o(gers,)h(and)f
+(returns)g(the)h(a)n(v)o(erage.)35 b(It)24 b(is)h(presented)d(in)i
+(sections,)h(follo)n(wing)d(the)i(steps)g(detailed)0
+3081 y(abo)o(v)o(e.)f(A)e(complete)e(w)o(orking)g(e)o(xample)f(of)i
+(this)h(service)f(is)h(a)n(v)n(ailable)f(in)g Fk(Appendix)f(C)p
+Fn(.)0 3228 y(The)h(\002rst)h(section)f(reads)g(in)g(a)h(request,)e
+(and)h(parses)g(the)g(SO)m(AP)h(header)-5 b(.)p 0 5549
+3901 4 v 0 5649 a Fi(4)3050 b(Chapter)24 b(2.)52 b(Examples)p
+eop end
+%%Page: 5 11
+TeXDict begin 5 10 bop 236 83 a Fe(from)44 b(ZSI)h(import)954
+96 y(*)236 174 y(import)f(sys)236 266 y(IN,)h(OUT)f(=)h(sys.stdin,)e
+(sys.stdout)236 357 y(try:)416 448 y(ps)h(=)h(ParsedSoap\(IN\))236
+540 y(except)f(ParseException,)e(e:)416 631 y
+(OUT.write\(FaultFromZSIException\(e\).A)o(sSOAP\()o(\)\))416
+722 y(sys.exit\(1\))236 814 y(except)i(Exception,)f(e:)416
+905 y(#)h(Faulted)g(while)g(processing;)f(we)h(assume)g(it's)g(in)h
+(the)f(header.)416 996 y(OUT.write\(FaultFromException\(e,)39
+b(1\).AsSOAP\(\)\))416 1088 y(sys.exit\(1\))236 1270
+y(#)45 b(We)f(are)h(not)f(prepared)g(to)g(handle)g(any)g(actors)g(or)g
+(mustUnderstand)f(elements,)236 1362 y(#)i(so)f(we'll)g(arbitrarily)f
+(fault)h(back)h(with)f(the)g(first)g(one)g(we)h(found.)236
+1453 y(a)g(=)g(ps.WhatActorsArePresent\(\))236 1544 y(if)g(len\(a\):)
+416 1636 y(OUT.write\(FaultFromActor\(a[0]\).AsSOA)o(P\(\)\))416
+1727 y(sys.exit\(1\))236 1818 y(mu)g(=)f(ps.WhatMustIUnderstand\(\))236
+1910 y(if)h(len\(mu\):)416 2001 y(uri,)f(localname)f(=)i(mu[0])416
+2092 y(OUT.write\(FaultFromNotUnderstood\(uri)o(,)39
+b(localname\).AsSOAP\(\)\))416 2183 y(sys.exit\(1\))0
+2470 y Fn(This)18 b(section)f(de\002nes)h(the)f(mappings)g(between)g
+(Python)f(objects)i(and)f(the)g(SO)m(AP)i(data)e(being)g(transmitted.)
+23 b(Recall)c(that)f(according)0 2570 y(to)i(the)h(SO)m(AP)g
+(speci\002cation,)e(RPC)i(input)f(and)g(output)e(are)j(modeled)d(as)j
+(a)g(structure.)236 2716 y Fe(class)44 b(Player:)416
+2808 y(def)g(__init__\(self,)1267 2821 y(*)1312 2808
+y(args\):)595 2899 y(if)g(not)h(len\(args\):)e(return)595
+2990 y(self.Name)g(=)i(args[0])595 3082 y(self.Scores)e(=)i(args[1:])
+236 3173 y(Player.typecode)e(=)h(TC.Struct\(Player,)e([)1671
+3264 y(TC.String\('Name'\),)1671 3356 y(TC.Array\('Integer',)f
+(TC.Integer\(\),)i('Scores',)g(undeclared=True\),)1671
+3447 y(],)h('GetAverage'\))236 3538 y(class)g(Average:)416
+3630 y(def)g(__init__\(self,)e(average=None\):)595 3721
+y(self.average)h(=)h(average)236 3812 y(Average.typecode)e(=)j
+(TC.Struct\(Average,)d([)1671 3904 y(TC.Integer\('average'\),)1671
+3995 y(],)i('GetAverageResponse'\))0 4282 y Fn(This)20
+b(section)g(parses)h(the)f(input,)f(performs)f(the)j(application-le)n
+(v)o(el)c(acti)n(vity)-5 b(,)19 b(and)h(serializes)g(the)h(response.)p
+0 5549 3901 4 v 0 5649 a Fi(2.1.)52 b(Ser)r(v)n(er)23
+b(Side)h(Examples)2843 b(5)p eop end
+%%Page: 6 12
+TeXDict begin 6 11 bop 236 83 a Fe(try:)416 174 y(player)43
+b(=)i(ps.Parse\(Player.typecode\))236 266 y(except)f
+(EvaluateException,)e(e:)416 357 y
+(OUT.write\(FaultFromZSIException\(e\).A)o(sSOAP\()o(\)\))416
+448 y(sys.exit\(1\))236 631 y(try:)416 722 y(total)i(=)g(0)416
+814 y(for)g(value)g(in)g(player.Scores:)f(total)h(=)g(total)g(+)h
+(value)416 905 y(result)e(=)i(Average\(total)e(/)h
+(len\(player.Scores\)\))416 996 y(sw)g(=)h(SoapWriter\(\))416
+1088 y(sw.serialize\(result,)c(Average.typecode\))416
+1179 y(sw.close\(\))416 1270 y(OUT.write\(str\(sw\)\))236
+1362 y(except)j(Exception,)f(e:)416 1453 y
+(OUT.write\(FaultFromException\(e,)c(0,)45 b
+(sys.exc_info\(\)[2]\).AsSOAP\(\)\))416 1544 y(sys.exit\(1\))0
+1831 y Fn(In)25 b(the)g Fl(serialize\(\))e Fn(call)j(abo)o(v)o(e,)e
+(the)h(second)g(parameter)e(is)j(optional,)f(since)g
+Fl(result)g Fn(is)h(an)f(instance)f(of)h(the)g Fl(Average)0
+1930 y Fn(class,)c(and)f(the)g Fl(Average.typecode)e
+Fn(attrib)n(ute)h(is)i(the)g(typecode)d(for)i(class)h(instances.)0
+2215 y Ff(2.1.3)100 b(A)28 b(mod)p 614 2215 30 4 v 37
+w(p)m(ython)h(e)m(xample)0 2418 y Fn(The)35 b(Apache)f(module)g
+Fl(mod_python)g Fn(\(see)h Fl(http://www.modpython.org)p
+Fn(\))c(embeds)j(Python)g(within)h(the)g(Apache)0 2518
+y(serv)o(er)-5 b(.)24 b(In)17 b(order)g(to)h(e)o(xpose)f(operations)f
+(within)h(a)i(module)d(via)i(mod)p 2046 2518 25 4 v 29
+w(p)o(ython,)e(use)i(the)g Fl(dispatch.AsHandler\(\))d
+Fn(function.)0 2617 y(The)21 b Fl(dispatch.AsHandler\(\))d
+Fn(function)i(will)i(dispatch)f(requests)g(to)g(an)o(y)g(operation)e
+(de\002ned)i(in)g(the)h(module)e(you)g(pass)i(it,)0 2717
+y(which)h(allo)n(ws)g(for)g(multiple)f(operations)g(to)h(be)g
+(de\002ned)g(in)g(a)h(module.)32 b(The)23 b(only)g(trick)f(is)j(to)e
+(use)p 2975 2717 V 3005 2717 V 83 w(import)p 3258 2717
+V 3287 2717 V 82 w(to)h(load)e(the)i(XML)0 2817 y(encodings)17
+b(your)g(service)i(e)o(xpects.)k(This)c(is)h(a)f(required)e(w)o
+(orkaround)e(to)k(a)n(v)n(oid)f(the)h(pitf)o(alls)g(of)f(restricted)h
+(e)o(x)o(ecution)d(with)j(respect)0 2916 y(to)h(XML)h(parsing.)0
+3063 y(The)26 b(follo)n(wing)e(is)j(a)g(complete)e(e)o(xample)f(of)i(a)
+h(simple)f(handler)-5 b(.)42 b(The)25 b(soap)h(operations)f(are)h
+(implemented)e(in)i(the)g(MyHandler)0 3163 y(module:)236
+3401 y Fe(def)45 b(hello\(\):)416 3492 y(return)e({"value":"Hello,)g
+(world"})236 3675 y(def)i(echo\()641 3688 y(**)731 3675
+y(kw\):)416 3766 y(return)e(kw)236 3949 y(def)i(sum\()596
+3962 y(**)686 3949 y(kw\):)416 4040 y(sum)f(=)h(0)416
+4132 y(for)f(i)h(in)f(kw.values\(\):)f(sum)h(+=)h(i)236
+4223 y(return)f({"value":sum})236 4406 y(def)h(average\()776
+4419 y(**)866 4406 y(kw\):)236 4497 y(d)g(=)g(sum\()596
+4510 y(**)686 4497 y(kw\))416 4588 y(d["value"])e(=)h
+(d["value"]/len\(kw\))236 4680 y(return)g(d)0 4966 y
+Fn(Dispatching)62 b(from)g(within)g(mod)p 1119 4966 V
+29 w(p)o(ython)f(is)j(achie)n(v)o(ed)d(by)h(passing)h(the)g
+(aforementined)d(MyHandler)h(module)g(to)0 5066 y Fl
+(dispatch.AsHandler\(\))p Fn(.)22 b(The)e(follo)n(wing)e(code)i(e)o
+(xposes)f(the)h(operations)f(de\002ned)g(in)h(MyHandler)f(via)h(SO)m
+(AP:)p 0 5549 3901 4 v 0 5649 a Fi(6)3050 b(Chapter)24
+b(2.)52 b(Examples)p eop end
+%%Page: 7 13
+TeXDict begin 7 12 bop 236 83 a Fe(from)44 b(ZSI)h(import)f(dispatch)
+236 174 y(from)g(mod_python)g(import)f(apache)236 357
+y(import)h(MyHandler)236 448 y(mod)h(=)f
+(__import__\('encodings.utf_8',)c(globals\(\),)j(locals\(\),)h(')2837
+461 y(*)2882 448 y('\))236 540 y(mod)h(=)f
+(__import__\('encodings.utf_16_be',)39 b(globals\(\),)44
+b(locals\(\),)f(')3016 553 y(*)3061 540 y('\))236 722
+y(def)i(handler\(req\):)416 814 y
+(dispatch.AsHandler\(modules=\(MyHandle)o(r,\),)39 b(request=req\))416
+905 y(return)k(apache.OK)0 1354 y Fg(2.2)121 b(Client)34
+b(Side)f(Examples)0 1603 y Ff(2.2.1)100 b(Simple)29 b(Example)0
+1806 y Fl(ZSI)22 b Fn(pro)o(vides)e(tw)o(o)i(w)o(ays)g(for)f(a)h
+(client)g(to)g(interacti)n(v)o(e)e(with)i(a)g(serv)o(er:)28
+b(the)22 b Fl(Binding)f Fn(or)g Fl(NamedParamBinding)e
+Fn(class)k(and)0 1906 y(the)h Fl(ServiceProxy)d Fn(class.)36
+b(The)24 b(\002rst)g(is)g(useful)f(when)g(the)h(operations)e(to)h(be)h
+(in)m(v)n(ok)o(ed)e(are)h(not)g(de\002ned)g(in)h(WSDL)g(or)f(when)0
+2005 y(only)c(simple)h(Python)f(datatypes)g(are)h(used;)g(the)g
+Fl(ServiceProxy)e Fn(class)j(can)e(be)h(used)g(to)g(parse)g(WSDL)h
+(de\002nitions)e(in)h(order)e(to)0 2105 y(determine)h(ho)n(w)g(to)i
+(serialize)f(and)g(parse)g(the)g(SO)m(AP)h(messages.)0
+2252 y(During)k(de)n(v)o(elopment,)f(it)j(is)f(often)g(useful)f(to)h
+(record)f(\223pack)o(et)g(traces\224)h(of)f(the)h(SO)m(AP)h(messages)f
+(being)f(e)o(xchanged.)40 b(Both)26 b(the)0 2351 y Fl(Binding)17
+b Fn(and)h Fl(ServiceProxy)e Fn(classes)j(pro)o(vide)d(a)j
+Fl(tracefile)e Fn(parameter)f(to)i(specify)g(an)g(output)f(stream)h
+(\(such)f(as)i(a)f(\002le\))0 2451 y(to)i(capture)f(messages.)25
+b(It)c(can)f(be)g(particularly)f(useful)g(when)h(deb)n(ugging)d(une)o
+(xpected)h(SO)m(AP)j(f)o(aults.)0 2598 y(The)g(\002rst)h(e)o(xample)e
+(pro)o(vided)e(belo)n(w)j(demonstrates)f(ho)n(w)g(to)i(use)f(the)g
+Fl(NamedParamBinding)e Fn(class)j(to)g(connect)d(to)j(a)f(remote)0
+2698 y(service)f(and)g(perform)e(an)i(operation.)236
+3027 y Fe(#!/usr/bin/env)43 b(python)236 3118 y(import)h(sys,time)236
+3210 y(from)g(ZSI.client)g(import)f(NamedParamBinding)f(as)j(NPBinding)
+236 3392 y(b)g(=)g(NPBinding\(url='http://127.0.0.1/cgi-)o(bin/so)o
+(apstru)o(ct',)39 b(tracefile=sys.stdout\))236 3484 y(print)44
+b("Hello:)g(",)g(b.hello\(\))236 3575 y(print)g("Echo:)g(",)h
+(b.echo\(name="josh",)c(year=2006,)i(pi=3.14,)h(time=time.gmtime\(\)\))
+236 3666 y(print)g("Sum:)g(",)h(b.sum\(one=1,)e(two=2,)g(three=3\))236
+3758 y(print)h("Average:)g(",)g(b.average\(one=100,)e(two=200,)h
+(three=300,)g(four=400\))0 4316 y Ff(2.2.2)100 b(Comple)m(x)29
+b(Example:)35 b(pic)n(kler)-5 b(.p)m(y)0 4519 y Fn(If)23
+b(the)g(operation)e(in)m(v)n(ok)o(ed)g(returns)h(a)h(Comple)o(xT)-7
+b(ype,)21 b(typecode)g(information)f(must)j(be)g(pro)o(vided)d(in)j
+(order)f(to)h(tell)g Fl(ZSI)g Fn(ho)n(w)f(to)0 4619 y(deserialize)e
+(the)g(response.)k(Here)c(is)h(a)g(sample)f(serv)o(er)n(-side)f
+(implementation)f(\(for)h(the)h(complete)f(e)o(xample)g(see)i
+Fk(Appendix)d(D)p Fn(\):)p 0 5549 3901 4 v 0 5649 a Fi(2.2.)52
+b(Client)24 b(Side)h(Examples)2875 b(7)p eop end
+%%Page: 8 14
+TeXDict begin 8 13 bop 236 174 a Fe(class)44 b(Person:)326
+266 y(def)g(__init__\(self,)f(name=None,)g(age=0\):)416
+357 y(self.name)g(=)i(name)416 448 y(self.age)e(=)i(age)236
+631 y(Person.typecode)e(=)h(TC.Struct\(Person,)416 722
+y([TC.String\('name'\),)460 814 y(TC.InonNegativeInteger\('age'\)],)416
+905 y('myApp:Person'\))236 1088 y(#)h(my)f(web)h(service)e(that)i
+(returns)e(a)i(complex)e(structure)236 1179 y(def)i(getPerson\(name\):)
+326 1270 y(fp)f(=)h(open\('\045s.person.pickle',)c(\045)k(name,)e
+('r'\))326 1362 y(return)h(pickle.load\(fp\))236 1544
+y(#)h(my)f(web)h(service)e(that)i(accepts)e(a)i(complex)e(structure)236
+1636 y(def)i(savePerson\(person\):)326 1727 y(fp)f(=)h
+(open\('\045s.person.pickle')c(\045)k(person.name,)d('w'\))326
+1818 y(pickle\(person,)g(fp\))326 1910 y(fp.close\(\))0
+2196 y Fn(In)25 b(order)e(for)i Fl(ZSI)g Fn(to)g(transparently)e
+(deserialize)h(the)h(returned)e(comple)o(x)g(type)i(into)g(a)g
+Fl(Person)f Fn(instance,)i(a)f(module)f(de\002ning)0
+2296 y(the)d(class)g(and)g(its)g(typecode)e(can)i(be)g(passed)f(into)h
+(the)g Fl(Binding)p Fn(.)26 b(It)21 b(is)g(also)g(possible)g(to)g(e)o
+(xplicitly)e(tell)j Fl(ZSI)f Fn(what)f(typecode)f(to)0
+2395 y(use)h(by)g(passing)g(it)h(as)g(a)f(parameter)f(to)h(the)h
+Fl(Binding.Receive\(\))c Fn(method.)0 2542 y(The)j(follo)n(wing)e
+(fragment)h(sho)n(ws)h(both)f(styles:)236 2780 y Fe(import)44
+b(sys)236 2872 y(from)g(ZSI.client)g(import)f(Binding)236
+2963 y(from)h(MyComplexTypes)f(import)h(Person)236 3146
+y(b)h(=)g(Binding\(url='http://localhost/test3/)o(pickle)o(r.py',)38
+b(tracefile=sys.stdout\))236 3237 y(person)44 b(=)h
+(Person\('christopher',)c(26\))236 3328 y(rsp)k(=)f
+(b.savePerson\(person\))0 3706 y Fn(Because)19 b(the)g(returned)e
+(comple)o(x)h(type)g(is)i(de\002ned)e(in)h(a)h(class)g(present)e(in)h
+Fk(typesmodule)p Fn(,)f(transparent)f(deserialization)h(is)i(possible.)
+0 3806 y(When)g(sending)f(comple)o(x)f(types)i(to)h(the)f(serv)o(er)m
+(,)f(it)i(is)g(not)f(necessary)f(to)i(list)g(the)f(module)f(in)h
+Fk(typesmodule)p Fn(:)p 0 5549 3901 4 v 0 5649 a Fi(8)3050
+b(Chapter)24 b(2.)52 b(Examples)p eop end
+%%Page: 9 15
+TeXDict begin 9 14 bop 236 174 a Fe(import)44 b(sys)236
+266 y(import)g(MyComplexTypes)236 357 y(from)g(ZSI.client)g(import)f
+(NamedParamBinding)f(as)j(NPBinding,)e(Binding)236 448
+y(from)h(ZSI)h(import)f(TC)236 631 y(kw)h(=)f
+({'url':'http://localhost/test3/pickler.py)o(',)39 b
+('tracefile':sys.stdout})236 722 y(b)45 b(=)g(NPBinding\()866
+735 y(**)956 722 y(kw\))236 814 y(rsp)g(=)f
+(b.getPerson\(name='christopher'\))236 905 y(assert)g(type\(rsp\))f(is)
+i(dict,)f('expecting)f(a)i(dict')236 996 y(assert)f
+(rsp['Person']['name'])d(==)k('christopher',)d('wrong)i(person')236
+1179 y(b)h(=)g(NPBinding\(typesmodule=MyComplexTypes)o(,)2119
+1192 y(**)2209 1179 y(kw\))236 1270 y(rsp)g(=)f
+(b.getPerson\(name='christopher'\))236 1362 y(assert)g
+(isinstance\(rsp['Person'],)d(MyComplexTypes.Person\),)g(\()416
+1453 y('expecting)i(instance)g(of)i(\045s')f
+(\045MyComplexTypes.Person\))236 1636 y(b)h(=)g
+(Binding\(typesmodule=MyComplexTypes,)2029 1649 y(**)2119
+1636 y(kw\))236 1727 y(class)f(Name\(str\):)416 1818
+y(typecode)f(=)i(TC.String\("name"\))236 2001 y(rsp)g(=)f
+(b.getPerson\(Name\('christopher'\)\))236 2092 y(assert)g
+(isinstance\(rsp['Person'],)d(MyComplexTypes.Person\),)g(\()416
+2183 y('expecting)i(instance)g(of)i(\045s')f
+(\045MyComplexTypes.Person\))p 0 5549 3901 4 v 0 5649
+a Fi(2.2.)52 b(Client)24 b(Side)h(Examples)2875 b(9)p
+eop end
+%%Page: 10 16
+TeXDict begin 10 15 bop 0 5549 3901 4 v 0 5649 a Fi(10)p
+eop end
+%%Page: 11 17
+TeXDict begin 11 16 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3495
+427 y Fh(THREE)p 0 515 V 2890 978 a Ft(Exceptions)0 1452
+y Fo(exception)19 b Fj(ZSIException)208 1551 y Fn(Base)i(class)g(for)e
+(all)i(ZSI)f(Exceptions,)f(it)i(is)g(a)g(subtype)e(of)h(the)g(Python)f
+Fl(Exception)g Fn(class.)0 1698 y Fo(exception)g Fj(ParseException)208
+1798 y Fl(ZSI)26 b Fn(can)g(raise)h(this)g(e)o(xception)d(while)j
+(creating)e(a)i Fl(ParsedSoap)e Fn(object.)43 b(It)27
+b(is)h(a)e(subtype)g(of)g(the)g Fl(ZSIException)208 1897
+y Fn(class.)e(The)17 b(string)f(form)g(of)g(a)h Fl(ParseException)e
+Fn(object)h(consists)h(of)g(a)g(line)g(of)f(human-readable)d(te)o(xt.)
+24 b(If)16 b(the)h(backtrace)208 1997 y(is)k(a)n(v)n(ailable,)e(it)i
+(will)g(be)f(concatenated)e(as)j(a)g(second)e(line.)0
+2144 y(The)h(follo)n(wing)e(attrib)n(utes)j(are)f(read-only:)0
+2291 y Fj(inheader)208 2390 y Fn(A)g(boolean)f(that)h(indicates)g(if)h
+(the)f(error)f(w)o(as)i(detected)e(in)i(the)f(SO)m(AP)h
+Fl(Header)e Fn(element.)0 2537 y Fj(str)208 2637 y Fn(A)h(te)o(xt)g
+(string)g(describing)f(the)h(error)-5 b(.)0 2784 y Fj(trace)208
+2883 y Fn(A)23 b(te)o(xt)g(string)g(containing)f(a)h(backtrace)f(to)i
+(the)f(error)-5 b(.)33 b(This)24 b(may)e(be)i Fl(None)f
+Fn(if)g(it)h(w)o(as)g(not)f(possible,)g(such)g(as)h(when)f(there)208
+2983 y(w)o(as)e(a)f(general)f(DOM)i(e)o(xception,)d(or)i(when)f(the)h
+Fl(str)h Fn(te)o(xt)f(is)h(belie)n(v)o(ed)e(to)h(be)g(suf)n(\002cient.)
+0 3130 y Fo(exception)f Fj(EvaluateException)208 3229
+y Fn(This)g(e)o(xception)e(is)k(similar)e(to)h Fl(ParseException)p
+Fn(,)d(e)o(xcept)h(that)i Fl(ZSI)f Fn(may)g(raise)g(it)h(while)g(con)m
+(v)o(erting)c(between)j(SO)m(AP)208 3329 y(and)g(local)h(Python)f
+(objects.)0 3476 y(The)h(follo)n(wing)e(attrib)n(utes)j(are)f
+(read-only:)0 3623 y Fj(str)208 3722 y Fn(A)g(te)o(xt)g(string)g
+(describing)f(the)h(error)-5 b(.)0 3869 y Fj(trace)208
+3969 y Fn(A)20 b(te)o(xt)g(backtrace,)f(as)i(described)e(abo)o(v)o(e)f
+(for)i Fl(ParseException)p Fn(.)p 0 5549 3901 4 v 3808
+5649 a Fi(11)p eop end
+%%Page: 12 18
+TeXDict begin 12 17 bop 0 5549 3901 4 v 0 5649 a Fi(12)p
+eop end
+%%Page: 13 19
+TeXDict begin 13 18 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3561
+427 y Fh(FOUR)p 0 515 V 3234 978 a Ft(Utilities)0 1457
+y Fl(ZSI)20 b Fn(de\002nes)g(some)g(utility)g(methods)f(that)i(general)
+e(applications)g(may)h(w)o(ant)g(to)g(use.)0 1604 y Fj(Version)p
+Fl(\(\))208 1703 y Fn(Returns)f(a)h(three-element)e(tuple)h(containing)
+e(the)j(numbers)e(representing)f(the)j(major)m(,)e(minor)m(,)g(and)h
+(release)h(identifying)e(the)208 1803 y Fl(ZSI)i Fn(v)o(ersion.)49
+b(Ne)n(w)21 b(in)f(v)o(ersion)f(1.1.)0 2130 y Fg(4.1)121
+b(Lo)n(w-Le)l(v)m(el)35 b(Utilities)0 2363 y Fl(ZSI)26
+b Fn(also)g(de\002nes)g(some)f(lo)n(w-le)n(v)o(el)g(utilities)i(for)e
+(its)i(o)n(wn)e(use)h(that)g(start)g(with)g(a)h(leading)e(underscore)e
+(and)j(must)f(be)h(imported)0 2463 y(e)o(xplicitly)-5
+b(.)24 b(The)o(y)19 b(are)h(documented)d(here)j(because)f(the)o(y)h
+(can)g(be)g(useful)g(for)f(de)n(v)o(eloping)f(ne)n(w)i(typecode)e
+(classes.)0 2610 y(These)h(functions)e(are)i(mostly)g(used)f(in)h(in)h
+Fl(parse)e Fn(methods)g(and)g(the)h Fl(ParsedSoap)f Fn(class.)25
+b(The)19 b(serialization)f(routines)g(use)i(the)0 2709
+y Fl(ElementProxy)f Fn(class)i(to)f(encapsulate)f(common)f(DOM-le)n(v)o
+(el)i(operations.)0 2856 y(Some)f Fl(lambda)p Fn(')-5
+b(s)18 b(are)h(de\002ned)f(so)h(that)g(some)g(DOM)g(accessors)f(will)i
+(return)e(an)g(empty)g(list)i(rather)e(than)h Fl(None)p
+Fn(.)24 b(This)19 b(means)f(that)0 2956 y(rather)h(than)h(writing:)236
+3102 y Fe(if)45 b(elt.childNodes:)371 3194 y(for)f(N)h(in)f
+(elt.childNodes:)505 3285 y(...)0 3524 y Fn(One)20 b(can)g(write:)236
+3671 y Fe(for)45 b(N)f(in)h(_children\(elt\):)371 3763
+y(...)0 4049 y Fn(Other)20 b Fl(lambda)p Fn(')-5 b(s)20
+b(return)f(SO)m(AP-related)g(attrib)n(utes)i(from)e(an)h(element,)f(or)
+h Fl(None)g Fn(if)h(not)e(present.)0 4196 y Fj(_attrs)p
+Fl(\()p Fk(element)q Fl(\))208 4296 y Fn(Returns)h(a)g(list)h(of)f(all)
+h(attrib)n(utes)f(of)g(the)g(speci\002ed)g Fl(element)p
+Fn(.)0 4442 y Fj(_backtrace)p Fl(\()p Fk(elt,)e(dom)p
+Fl(\))208 4542 y Fn(This)23 b(function)f(returns)h(a)h(te)o(xt)g
+(string)f(that)h(traces)g(a)g(\223path\224)f(from)f Fl(dom)p
+Fn(,)j(a)f(DOM)g(root,)f(to)h Fl(elt)p Fn(,)g(an)g(element)f(within)g
+(that)208 4642 y(document,)18 b(in)i(XP)o(ath)g(syntax.)0
+4789 y Fj(_child_elements)p Fl(\()p Fk(element)q Fl(\))208
+4888 y Fn(Returns)g(a)g(list)h(of)f(all)h(children)e(elements)h(of)g
+(the)g(speci\002ed)g Fl(element)p Fn(.)0 5035 y Fj(_children)p
+Fl(\()p Fk(element)q Fl(\))208 5135 y Fn(Returns)g(a)g(list)h(of)f(all)
+h(children)e(of)h(the)g(speci\002ed)g Fl(element)p Fn(.)p
+5 5282 25 4 v 30 5282 a(cop)o(yright)p 373 5282 V 48
+w(empty)p 612 5282 V 28 w(nsuri)p 807 5282 V 29 w(list)0
+5381 y Fj(_find_arraytype)p Fl(\()p Fk(element)q Fl(\))p
+0 5549 3901 4 v 3808 5649 a Fi(13)p eop end
+%%Page: 14 20
+TeXDict begin 14 19 bop 208 83 a Fn(The)19 b(v)n(alue)h(of)g(the)g(SO)m
+(AP)h Fl(arrayType)e Fn(attrib)n(ute.)50 b(Ne)n(w)21
+b(in)f(v)o(ersion)f(1.2.)0 230 y Fj(_find_attr)p Fl(\()p
+Fk(element,)f(name)p Fl(\))208 330 y Fn(The)h(v)n(alue)h(of)g(the)g
+(unquali\002ed)e Fl(name)i Fn(attrib)n(ute.)0 476 y Fj(_find_attrNS)p
+Fl(\()p Fk(element,)d(namespaceURI,)h(localName)p Fl(\))208
+576 y Fn(The)h(v)n(alue)h(of)g(a)g Fl(name)g Fn(attrib)n(ute)g(in)h(a)f
+(namespace)f Fl(namespaceURI)p Fn(.)0 723 y Fj(_find_attrNodeNS)p
+Fl(\()p Fk(element,)e(namespaceURI,)g(localName)p Fl(\))208
+823 y Fn(W)-7 b(orks)20 b(just)h(lik)o(e)g Fl(_find_attrNS)p
+Fn(,)d(b)n(ut)i(this)h(function)e(grabs)g(the)i(attrib)n(ute)f(Node)f
+(to)i(distinquish)e(between)g(an)i(unspeci-)208 922 y(\002ed)f(attrib)n
+(ute\(None\))e(and)h(one)h(set)h(to)f(empty)g(string\(\224\224\).)0
+1069 y Fj(_find_default_namespace)p Fl(\()p Fk(element)q
+Fl(\))208 1169 y Fn(Returns)g(the)g(v)n(alue)f(of)h(the)g(def)o(ault)g
+(namespace.)0 1316 y Fj(_find_encstyle)p Fl(\()p Fk(element)q
+Fl(\))208 1415 y Fn(The)f(v)n(alue)h(of)g(the)g(SO)m(AP)h
+Fl(encodingStyle)d Fn(attrib)n(ute.)0 1562 y Fj(_find_href)p
+Fl(\()p Fk(element)q Fl(\))208 1662 y Fn(The)h(v)n(alue)h(of)g(the)g
+(unquali\002ed)e Fl(href)i Fn(attrib)n(ute.)0 1808 y
+Fj(_find_type)p Fl(\()p Fk(element)q Fl(\))208 1908 y
+Fn(The)f(v)n(alue)h(of)g(the)g(XML)g(Schema)g Fl(type)g
+Fn(attrib)n(ute.)0 2055 y Fj(_find_xmlns_prefix)p Fl(\()p
+Fk(element,)c(pr)m(e\002x)p Fl(\))208 2155 y Fn(The)j(v)n(alue)h(of)g
+(the)g(xmlns:pre\002x)f Fl(type)h Fn(attrib)n(ute.)0
+2301 y Fj(_find_xsi_attr)p Fl(\()p Fk(element,)d(attrib)n(ute)p
+Fl(\))208 2401 y Fn(Find)j(the)g(attrib)n(ute)g(in)g(an)o(y)f(of)h(the)
+g(XMLSchema)g(namespaces.)0 2548 y Fj(_get_element_nsuri_name)p
+Fl(\()p Fk(element)q Fl(\))208 2648 y Fn(Returns)g(a)g
+Fl(\(namespace,name\))e Fn(tuple)i(representing)e(the)i(element)g(tag.)
+0 2794 y Fj(_get_idstr)p Fl(\()p Fk(obj)p Fl(\))208 2894
+y Fn(Substitute)g(for)h Fl(id)g Fn(function.)k(Python)20
+b(2.3.x)g(generates)g(a)h Fl(FutureWarning)e Fn(for)i(ne)o(gati)n(v)o
+(e)e(IDs,)i(so)g(we)h(use)f(a)g(dif)n(ferent)208 2994
+y(pre\002x)e(character)g(to)h(ensure)g(uniqueness,)e(and)i(call)g
+(abs\(\))g(to)h(a)n(v)n(oid)e(the)i(w)o(arning.)0 3141
+y Fj(_get_postvalue_from_absoluteURI)p Fl(\()p Fk(url)p
+Fl(\))208 3240 y Fn(Returns)f(POST)g(v)n(alue)g(from)f
+Fl(url)p Fn(,)h(and)g(caches)g(these)g(v)n(alues.)0 3387
+y Fj(_resolve_prefix)p Fl(\()p Fk(element,)d(pr)m(e\002x)p
+Fl(\))208 3487 y Fn(resolv)o(e)h Fl(prefix)h Fn(to)h(a)g(namespaceURI.)
+e(If)h Fl(None)g Fn(or)h(empty)e Fl(str)p Fn(,)i(return)e(def)o(ault)h
+(namespace)f(or)h Fl(None)h Fn(if)g(not)f(de\002ned.)0
+3634 y Fj(_valid_encoding)p Fl(\()p Fk(elt)q Fl(\))208
+3733 y Fn(Return)24 b(true)h(if)g(the)g(element)f Fl(elt)h
+Fn(has)g(a)h(SO)m(AP)f(encoding)e(that)i(can)g(be)g(handled)e(by)i
+Fl(ZSI)g Fn(\(currently)d(Section)j(5)g(of)g(the)208
+3833 y(SO)m(AP)c(1.1)e(speci\002cation)h(or)f(an)i(empty)e(encoding)f
+(for)i(XML\).)p 0 5549 3901 4 v 0 5649 a Fi(14)3101 b(Chapter)24
+b(4.)52 b(Utilities)p eop end
+%%Page: 15 21
+TeXDict begin 15 20 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3634
+427 y Fh(FIVE)p 0 515 V 709 978 a Ft(The)57 b Fd(ParsedSoap)g
+Ft(module)g(\227)g(basic)2224 1227 y(message)g(handling)0
+1717 y Fn(This)20 b(class)i(represents)d(an)h(input)f(stream)h(that)h
+(has)f(been)g(parsed)f(as)i(a)g(SO)m(AP)g(message.)0
+1864 y Fo(class)g Fj(ParsedSoap)p Fl(\()p Fk(input)q
+Fc([)p Fk(,)c(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+1963 y Fn(Creates)26 b(a)g Fl(ParsedSoap)f Fn(object)g(from)g(the)h
+(pro)o(vided)d(input)j(source.)41 b(If)26 b Fl(input)f
+Fn(is)i(not)f(a)g(string,)h(then)e(it)i(must)f(be)g(an)208
+2063 y(object)19 b(with)i(a)f Fl(read\(\))g Fn(method)f(that)h
+(supports)f(the)h(standard)f(Python)g(\223\002le)i(read\224)f
+(semantics.)208 2196 y(The)f(follo)n(wing)g(k)o(e)o(yw)o(ord)f(ar)o
+(guments)g(may)i(be)g(used:)303 2325 y Fo(K)n(eyw)o(ord)p
+900 2355 4 100 v 322 w(Default)p 1262 2355 V 98 w(Description)p
+254 2358 3601 4 v 303 2428 a Fl(envelope)p 900 2458 4
+100 v 280 w(True)p 1262 2458 V 130 w Fn(e)o(xpect)g(a)g(SO)m(AP)h(En)m
+(v)o(elope)303 2528 y Fl(keepdom)p 900 2856 4 399 v 305
+w(False)p 1262 2856 V 105 w Fn(Do)54 b(not)g(release)f(the)h(DOM)g
+(when)f(this)i(object)e(is)i(destro)o(yed.)124 b(T)-7
+b(o)54 b(ac-)1313 2627 y(cess)42 b(the)e(DOM)g(object,)k(use)d(the)f
+Fl(GetDomAndReader\(\))e Fn(method.)83 b(The)1313 2727
+y(reader)45 b(object)g(is)i(necessary)e(to)g(properly)f(free)h(the)h
+(DOM)g(structure)e(using)1313 2827 y Fl(reader.releaseNode\(dom\))p
+Fn(.)j(Ne)n(w)21 b(in)f(v)o(ersion)f(1.2.)303 2926 y
+Fl(readerclass)p 900 3056 4 200 v 130 w(None)p 1262 3056
+V 130 w Fn(Class)26 b(used)d(to)h(create)g(DOM-creating)e(XML)i
+(readers;)h(described)e(belo)n(w)-5 b(.)71 b(Ne)n(w)1313
+3026 y(in)21 b(v)o(ersion)e(1.2.)303 3125 y Fl(resolver)p
+900 3155 4 100 v 280 w(None)p 1262 3155 V 130 w Fn(V)-9
+b(alue)20 b(for)g(the)g Fl(resolver)f Fn(attrib)n(ute;)h(see)h(belo)n
+(w)-5 b(.)303 3225 y Fl(trailers)p 900 3255 V 255 w(False)p
+1262 3255 V 105 w Fn(Allo)n(w)21 b(trailing)e(data)h(elements)g(to)h
+(appear)e(after)g(the)i Fl(Body)p Fn(.)0 3367 y(The)f(follo)n(wing)e
+(attrib)n(utes)j(of)e(a)i Fl(ParsedSoap)e Fn(are)h(read-only:)0
+3514 y Fj(body)208 3614 y Fn(The)27 b(root)h(of)f(the)h(SO)m(AP)h
+Fl(Body)f Fn(element.)48 b(Using)28 b(the)g Fl(GetElementNSdict\(\))d
+Fn(method)i(on)h(this)g(attrib)n(ute)g(can)g(be)208 3713
+y(useful)19 b(to)i(get)f(a)g(dictionary)f(to)h(be)g(used)g(with)h(the)f
+Fl(SoapWriter)f Fn(class.)0 3860 y Fj(body_root)208 3960
+y Fn(The)j(element)f(that)i(contains)e(the)i(SO)m(AP)g(serialization)e
+(root;)i(that)g(is,)g(the)g(element)e(in)i(the)f(SO)m(AP)h
+Fl(Body)f Fn(that)h(\223starts)g(of)n(f)5 b(\224)208
+4059 y(the)20 b(data.)0 4206 y Fj(data_elements)208 4306
+y Fn(A)g(\(possibly)f(empty\))g(list)j(of)e(all)g(child)g(elements)g
+(of)g(the)g Fl(Body)g Fn(other)g(than)f(the)h(root.)0
+4453 y Fj(header)208 4552 y Fn(The)h(root)h(of)g(the)g(SO)m(AP)h
+Fl(Header)f Fn(element.)30 b(Using)22 b(the)g Fl(GetElementNSdict\(\))e
+Fn(method)g(on)i(this)h(attrib)n(ute)f(can)g(be)208 4652
+y(useful)d(to)i(get)f(a)g(dictionary)f(to)h(be)g(used)g(with)h(the)f
+Fl(SoapWriter)f Fn(class.)0 4799 y Fj(header_elements)208
+4899 y Fn(A)h(\(possibly)f(empty\))g(list)j(of)e(all)g(elements)g(in)h
+(the)f(SO)m(AP)h Fl(Header)p Fn(.)0 5045 y Fj(trailer_elements)208
+5145 y Fn(Returns)j(a)h(\(possibly)e(empty\))g(list)j(of)e(all)h
+(elements)f(follo)n(wing)e(the)j Fl(Body)p Fn(.)37 b(If)24
+b(the)h Fl(trailers)e Fn(k)o(e)o(yw)o(ord)g(w)o(as)i(not)f(used)208
+5245 y(when)19 b(the)h(object)g(w)o(as)h(constructed,)d(this)j(attrib)n
+(ute)f(will)h(not)f(be)g(instantiated)f(and)h(retrie)n(ving)e(it)j
+(will)g(raise)g(an)f(e)o(xception.)0 5392 y(The)g(follo)n(wing)e
+(attrib)n(ute)i(may)g(be)g(modi\002ed:)p 0 5549 3901
+4 v 3808 5649 a Fi(15)p eop end
+%%Page: 16 22
+TeXDict begin 16 21 bop 0 83 a Fj(resolver)208 183 y
+Fn(If)24 b(not)g Fl(None)p Fn(,)i(this)f(attrib)n(ute)f(can)h(be)f(in)m
+(v)n(ok)o(ed)f(to)i(handle)f(absolute)g Fl(href)p Fn(')-5
+b(s)25 b(in)f(the)h(SO)m(AP)h(data.)38 b(It)25 b(will)g(be)g(in)m(v)n
+(ok)o(ed)e(as)208 282 y(follo)n(ws:)208 415 y Fj(resolver)p
+Fl(\()p Fk(uri,)18 b(tc,)j(ps,)f(**k)o(e)n(ywor)m(ds)p
+Fl(\))390 515 y Fn(The)i Fl(uri)g Fn(parameter)f(is)i(the)f(URI)h(to)g
+(resolv)o(e.)30 b(The)22 b Fl(tc)g Fn(parameter)f(is)i(the)f(typecode)f
+(that)h(needs)g(to)g(resolv)o(e)g Fl(href)p Fn(;)390
+614 y(this)f(may)e(be)h(needed)f(to)h(properly)e(interpret)g(the)i
+(content)f(of)h(a)h(MIME)e(bodypart,)f(for)h(e)o(xample.)k(The)d
+Fl(ps)g Fn(parameter)390 714 y(is)h(the)f Fl(ParsedSoap)e
+Fn(object)i(that)g(is)g(in)m(v)n(oking)e(the)i(resolution)e(\(this)i
+(allo)n(ws)h(a)f(single)g(resolv)o(er)e(instance)i(to)g(handle)390
+814 y(multiple)g(SO)m(AP)h(parsers\).)390 930 y(F)o(ailure)g(to)h
+(resolv)o(e)f(the)h(URI)g(should)f(result)g(in)h(an)g(e)o(xception)e
+(being)g(raised.)30 b(If)21 b(there)g(is)i(no)e(content,)g(return)g
+Fl(None)p Fn(;)390 1029 y(this)26 b(is)g(not)f(the)g(same)g(as)h(an)f
+(empty)f(string.)40 b(If)25 b(there)f(is)i(content,)f(the)h(data)f
+(returned)e(should)h(be)h(in)g(a)h(form)e(under)n(-)390
+1129 y(standable)c(by)f(the)i(typecode.)0 1276 y(The)f(follo)n(wing)e
+(methods)i(are)g(a)n(v)n(ailable:)0 1423 y Fj(Backtrace)p
+Fl(\()p Fk(elt)q Fl(\))208 1522 y Fn(Returns)g(a)g(human-readable)d
+(\223trace\224)j(from)f(the)h(document)e(root)i(to)g(the)g(speci\002ed)
+g(element.)0 1669 y Fj(FindLocalHREF)p Fl(\()p Fk(hr)m(ef)o(,)e(elt)q
+Fl(\))208 1769 y Fn(Returns)28 b(the)g(element)g(that)g(has)h(an)f
+Fl(id)h Fn(attrib)n(ute)f(whose)g(v)n(alue)f(is)j(speci\002ed)e(by)g
+(the)g Fl(href)g Fn(fragment)f(identi\002er)-5 b(.)49
+b(The)208 1869 y Fl(href)37 b Fk(must)g Fn(be)h(a)f(fragment)f
+(reference)g(\227)h(that)h(is,)k(it)c(must)g(start)f(with)h(a)g(pound)d
+(sign.)76 b(This)38 b(method)e(raises)i(an)208 1968 y
+Fl(EvaluateException)28 b Fn(e)o(xception)g(if)j(the)g(element)f(isn')o
+(t)g(found.)55 b(It)31 b(is)g(mainly)f(for)g(use)h(by)f(the)h(parsing)f
+(methods)208 2068 y(in)20 b(the)g Fl(TypeCode)f Fn(module.)0
+2215 y Fj(GetElementNSdict)p Fl(\()p Fk(elt)q Fl(\))208
+2314 y Fn(Return)i(a)i(dictionary)e(for)g(all)i(the)g(namespace)e
+(entries)h(acti)n(v)o(e)g(at)h(the)f(current)f(element.)31
+b(Each)22 b(dictionary)e(k)o(e)o(y)i(will)h(be)g(the)208
+2414 y(pre\002x)c(and)h(the)g(v)n(alue)f(will)i(be)f(the)h(namespace)e
+(URI.)0 2561 y Fj(GetMyHeaderElements)p Fl(\()p Fc([)p
+Fk(actorlist=None)12 b Fc(])p Fl(\))208 2660 y Fn(Returns)31
+b(a)h(list)g(of)f(all)i(elements)e(in)g(the)h Fl(Header)f
+Fn(that)g(are)h(intended)e(for)g Fk(this)i Fn(SO)m(AP)h(processor)-5
+b(.)58 b(This)31 b(includes)g(all)208 2760 y(elements)22
+b(that)i(either)f(ha)n(v)o(e)f(no)h(SO)m(AP)h Fl(actor)f
+Fn(attrib)n(ute,)g(or)g(whose)g(v)n(alue)g(is)h(either)f(the)g(special)
+g(\223ne)o(xt)g(actor\224)f(v)n(alue)h(or)208 2860 y(in)d(the)g
+Fl(actorlist)f Fn(list)i(of)f(URI')-5 b(s.)0 3007 y Fj(GetDomAndReader)
+p Fl(\(\))208 3106 y Fn(Returns)24 b(a)h(tuple)g(containing)e(the)h
+(dom)g(and)g(reader)g(objects,)i Fl(\(dom,)49 b(reader\))p
+Fn(.)37 b(Unless)26 b(k)o(eepdom)d(is)i(true,)h(the)e(dom)208
+3206 y(and)c(reader)h(objects)g(will)h(go)f(out)g(of)g(scope)g(when)g
+(the)h(P)o(arsedSoap)e(instance)h(is)h(deleted.)28 b(If)21
+b(k)o(eepdom)f(is)i(true,)f(the)h(reader)208 3305 y(object)d(is)i
+(needed)e(to)i(properly)d(clean)i(up)f(the)i(dom)e(tree)h(with)g
+Fl(reader.releaseNode\(dom\))p Fn(.)0 3452 y Fj(IsAFault)p
+Fl(\(\))208 3552 y Fn(Returns)g(true)f(if)i(the)f(message)g(is)h(a)g
+(SO)m(AP)g(f)o(ault.)0 3699 y Fj(Parse)p Fl(\()p Fk(how)p
+Fl(\))208 3798 y Fn(P)o(arses)38 b(the)f(SO)m(AP)i Fl(Body)e
+Fn(according)e(to)j(the)g Fl(how)f Fn(parameter)m(,)j(and)d(returns)g
+(a)h(Python)e(object.)76 b(If)38 b Fl(how)f Fn(is)i(not)e(a)208
+3898 y Fl(TC.TypeCode)18 b Fn(object,)i(then)f(it)i(should)e(be)i(a)f
+(Python)f(class)i(object)f(that)g(has)h(a)f Fl(typecode)g
+Fn(attrib)n(ute.)0 4045 y Fj(ResolveHREF)p Fl(\()p Fk(uri,)e(tc)p
+Fc([)p Fk(,)i(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+4145 y Fn(This)24 b(method)f(is)i(in)m(v)n(ok)o(ed)e(to)h(resolv)o(e)f
+(an)i(absolute)e(URI.)i(If)f(the)g(typecode)f Fl(tc)h
+Fn(has)h(a)f Fl(resolver)g Fn(attrib)n(ute,)g(it)h(will)g(use)208
+4244 y(it)30 b(to)h(resolv)o(e)e(the)h(URI)g(speci\002ed)g(in)g(the)h
+Fl(uri)f Fn(parameter)m(,)g(otherwise)g(it)g(will)h(use)f(its)i(o)n(wn)
+d Fl(resolver)p Fn(,)i(or)f(raise)h(an)208 4344 y Fl(EvaluateException)
+17 b Fn(e)o(xception.)208 4477 y(An)o(y)i Fl(keyword)h
+Fn(parameters)f(will)i(be)f(passed)g(to)h(the)f(chosen)g(resolv)o(er)-5
+b(.)24 b(If)d(no)e(content)h(is)h(a)n(v)n(ailable,)f(it)h(will)g
+(return)e Fl(None)p Fn(.)208 4576 y(If)h(unable)f(to)h(resolv)o(e)g
+(the)g(URI)h(it)g(will)g(raise)f(an)h Fl(EvaluateException)c
+Fn(e)o(xception.)23 b(Otherwise,)d(the)h(resolv)o(er)e(should)208
+4676 y(return)25 b(data)i(in)f(a)i(form)d(acceptable)h(to)h(the)f
+(speci\002ed)h(typecode,)f Fl(tc)p Fn(.)45 b(\(This)26
+b(will)i(almost)e(al)o(w)o(ays)i(be)e(a)h(\002le-lik)o(e)g(object)208
+4776 y(holding)18 b(opaque)h(data;)h(for)f(XML,)h(it)h(may)f(be)g(a)h
+(DOM)f(tree.\))0 4922 y Fj(WhatActorsArePresent)p Fl(\(\))208
+5022 y Fn(Returns)g(a)g(list)h(of)f(the)h(v)n(alues)e(of)h(all)h(the)f
+(SO)m(AP)h Fl(actor)f Fn(attrib)n(utes)g(found)f(in)h(child)g(elements)
+g(of)g(the)g(SO)m(AP)h Fl(Header)p Fn(.)0 5169 y Fj
+(WhatMustIUnderstand)p Fl(\(\))208 5269 y Fn(Returns)26
+b(a)h(list)h(of)f(`)p Fl(\(uri,)48 b(localname\))p Fn(')25
+b(tuples)i(for)f(all)i(elements)e(in)h(the)g(SO)m(AP)h
+Fl(Header)e Fn(that)h(ha)n(v)o(e)f(the)h(SO)m(AP)208
+5368 y Fl(mustUnderstand)18 b Fn(attrib)n(ute)h(set)i(to)g(a)f
+(non-zero)e(v)n(alue.)p 0 5549 3901 4 v 0 5649 a Fi(16)1389
+b(Chapter)24 b(5.)52 b(The)23 b Fl(ParsedSoap)e Fi(module)j(\227)f
+(basic)g(message)g(handling)p eop end
+%%Page: 17 23
+TeXDict begin 17 22 bop 0 83 a Fl(ZSI)32 b Fn(supports)f(multiple)g
+(DOM)i(implementations.)58 b(The)32 b Fl(readerclass)f
+Fn(parameter)f(speci\002es)j(which)e(one)h(to)g(use.)61
+b(The)0 183 y(def)o(ault)21 b(is)i(to)g(use)f(the)g(DOM)h(pro)o(vided)c
+(with)k(the)f(PyXML)g(package)e(de)n(v)o(eloped)g(by)i(the)g(Python)f
+(XML)h(SIG,)g(pro)o(vided)e(through)0 282 y(the)g Fl(PyExpat.Reader)e
+Fn(class)j(in)g(the)f Fl(xml.dom.ext.reader)d Fn(module.)0
+429 y(The)j(speci\002ed)g(reader)f(class)i(must)f(support)f(the)h
+(follo)n(wing)f(methods:)0 576 y Fj(fromString)p Fl(\()p
+Fk(string)p Fl(\))208 676 y Fn(Return)g(a)i(DOM)f(object)g(from)f(a)i
+(string.)0 823 y Fj(fromStream)p Fl(\()p Fk(str)m(eam)p
+Fl(\))208 922 y Fn(Return)e(a)i(DOM)f(object)g(from)f(a)i(\002le-lik)o
+(e)f(stream.)0 1069 y Fj(releaseNode)p Fl(\()p Fk(dom)p
+Fl(\))208 1169 y Fn(Free)g(the)g(speci\002ed)g(DOM)g(object.)0
+1316 y(The)k(DOM)g(object)g(must)g(support)e(the)i(standard)f(Python)g
+(mapping)f(of)i(the)g(DOM)h(Le)n(v)o(el)e(2)h(speci\002cation.)36
+b(While)24 b(only)g(a)g(small)0 1415 y(subset)d(of)g(speci\002cation)g
+(is)h(used,)f(the)g(particular)f(methods)g(and)h(attrib)n(utes)g(used)g
+(by)f Fl(ZSI)i Fn(are)f(a)n(v)n(ailable)f(only)h(by)f(inspecting)h(the)
+0 1515 y(source.)0 1662 y(T)-7 b(o)32 b(use)g(the)g Fl(cDomlette)f
+Fn(DOM)h(pro)o(vided)e(by)h(the)h(4Suite)g(package,)h(use)g(the)f
+Fl(NonvalidatingReader)c Fn(class)33 b(in)g(the)0 1761
+y Fl(Ft.Xml.Domlette)17 b Fn(module.)23 b(Due)c(to)g(name)f(changes)g
+(in)h(the)g(1.0)f(v)o(ersion)g(of)h(4Suite,)f(a)i(simple)f(adapter)f
+(class)h(is)h(required)d(to)0 1861 y(use)j(this)h(DOM)g
+(implementation.)236 2099 y Fe(from)44 b(4Suite.Xml.Domlette)e(import)i
+(NonvalidatingReaderBase)236 2282 y(class)g
+(4SuiteAdapterReader\(NonvalidatingReaderB)o(ase\):)416
+2464 y(def)g(fromString\(self,)e(str\):)595 2556 y(return)i
+(self.parseString\(str\))416 2738 y(def)g(fromStream\(self,)e
+(stream\):)595 2830 y(return)i(self.parseStream\(stream\))416
+3012 y(def)g(releaseNode\(self,)e(node\):)595 3104 y(pass)p
+0 5549 3901 4 v 3808 5649 a Fi(17)p eop end
+%%Page: 18 24
+TeXDict begin 18 23 bop 0 5549 3901 4 v 0 5649 a Fi(18)p
+eop end
+%%Page: 19 25
+TeXDict begin 19 24 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3707
+427 y Fh(SIX)p 0 515 V 1026 978 a Ft(The)57 b Fd(TypeCode)g
+Ft(classes)h(\227)f(data)2807 1227 y(con)l(v)-5 b(ersions)0
+1706 y Fn(The)17 b Fl(TypeCode)f Fn(module)f(de\002nes)i(classes)h
+(used)f(for)f(con)m(v)o(erting)e(data)j(between)f(SO)m(AP)i(data)f(and)
+f(local)h(Python)f(objects.)24 b(Python)0 1805 y(numeric)j(and)g
+(string)h(types,)h(and)f(sequences)f(and)g(dictionaries,)i(are)f
+(supported)e(by)i Fl(ZSI)p Fn(.)f(The)h Fl(TC.TypeCode)f
+Fn(class)i(is)g(the)0 1905 y(parent)19 b(class)i(of)f(all)h(datatypes)e
+(understood)f(by)i Fl(ZSI)p Fn(.)0 2052 y(All)h(typecodes)e(classes)i
+(ha)n(v)o(e)f(the)g(pre\002x)f Fl(TC.)p Fn(,)h(to)g(a)n(v)n(oid)g(name)
+g(clashes.)0 2199 y Fl(ZSI)27 b Fn(pro)o(vides)e(\002ne-grain)h
+(control)f(o)o(v)o(er)h(the)h(names)g(used)f(when)h(parsing)f(and)g
+(serializing)h(XML)g(into)f(local)h(Python)f(objects,)0
+2298 y(through)g(the)i(use)h(of)f(tw)o(o)h(attrib)n(utes:)41
+b(the)28 b Fl(pname)p Fn(,)i(the)e Fl(aname)p Fn(.)49
+b(The)28 b Fl(pname)g Fn(speci\002es)h(the)f(name)g(e)o(xpected)e(on)i
+(the)h(XML)0 2398 y(element)20 b(being)g(parsed)g(and)g(the)h(name)f
+(to)h(use)g(for)f(the)g(output)g(element)g(when)g(serializing.)26
+b(The)21 b Fl(aname)f Fn(is)i(the)e(name)g(to)h(use)g(for)0
+2498 y(the)f(analogous)f(attrib)n(ute)g(in)i(the)f(local)g(Python)f
+(object.)0 2644 y(The)f Fl(pname)f Fn(is)i(the)f(parameter)f(name.)23
+b(It)c(speci\002es)f(the)g(incoming)e(XML)i(element)g(name)f(and)g(the)
+h(def)o(ault)g(v)n(alues)f(for)h(the)g(Python)0 2744
+y(attrib)n(ute)i(and)g(serialized)g(names.)26 b(All)c(typecodes)d(tak)o
+(e)h(the)h Fl(pname)f Fn(ar)o(gument.)k(This)d(name)f(can)g(be)g
+(speci\002ed)h(as)g(either)f(a)h(list)h(or)0 2844 y(a)d(string.)24
+b(When)19 b(speci\002ed)g(as)g(a)g(list,)h(it)g(must)f(ha)n(v)o(e)f(tw)
+o(o)h(elements)g(which)f(are)h(interpreted)e(as)j(a)f
+(\223\(namespace-URI,)d(localname\)\224)0 2943 y(pair)-5
+b(.)24 b(If)17 b(speci\002ed)g(this)h(w)o(ay)-5 b(,)18
+b(both)e(the)i(namespace)e(and)h(the)g(local)g(element)g(name)g(must)g
+(match)g(for)g(the)h(parse)f(to)g(succeed.)24 b(F)o(or)17
+b(the)0 3043 y(Python)j(attrib)n(ute,)g(and)h(when)f(generating)f
+(output,)g(only)h(the)h(\223localname\224)f(is)i(used.)k(If)21
+b(a)g(namespace-URI)f(is)i(speci\002ed)e(then)h(the)0
+3143 y(full)f(quali\002ed)f(name)h(is)h(used)f(for)g(output,)e(and)i
+(it)h(is)g(required)d(for)i(input;)f(this)i Fk(r)m(equir)m(es)g
+Fn(the)f(namespace)f(pre\002x)g(to)h(be)h(speci\002ed.)0
+3289 y(The)h Fl(aname)g Fn(is)h(the)g(attrib)n(ute)f(name.)31
+b(This)22 b(parameter)f(o)o(v)o(errides)g(an)o(y)g(v)n(alue)h(implied)g
+(by)g(the)g Fl(pname)p Fn(.)31 b(T)-7 b(ypecodes)21 b(nested)i(in)f(a)0
+3389 y Fl(TC.Struct)d Fn(or)h Fl(TC.ComplexType)d Fn(can)j(use)g(this)h
+(parameter)d(to)j(specify)e(the)h(tag,)g(dictionary)e(k)o(e)o(y)-5
+b(,)19 b(or)g(instance)h(attrib)n(ute)f(to)0 3489 y(set.)0
+3636 y(The)25 b Fl(nsdict)f Fn(parameter)g(to)h(the)g
+Fl(SoapWriter)f Fn(construct)g(can)h(be)g(used)g(to)g(specify)f
+(pre\002x)h(to)g(namespace-URI)e(mappings,)0 3735 y(these)d(are)g
+(otherwise)g(handled)f(automatically)-5 b(.)0 4063 y
+Fg(6.1)121 b Fb(TC.TypeCode)0 4295 y Fn(The)20 b Fl(TypeCode)f
+Fn(class)i(is)g(the)g(parent)e(class)i(of)f(all)h(typecodes.)0
+4442 y Fo(class)g Fj(TypeCode)p Fl(\()p Fk(**k)o(e)n(ywor)m(ds)p
+Fl(\))208 4542 y Fn(The)e(follo)n(wing)g(k)o(e)o(yw)o(ord)f(ar)o
+(guments)g(may)i(be)g(used:)p 0 5549 3901 4 v 3808 5649
+a Fi(19)p eop end
+%%Page: 20 26
+TeXDict begin 20 25 bop 257 70 a Fo(K)n(eyw)o(ord)p 853
+100 4 100 v 390 w(Default)p 1352 100 V 166 w(Description)p
+208 103 3736 4 v 257 173 a Fl(pname)p 853 203 4 100 v
+498 w(None)p 1352 203 V 198 w Fn(parameter)19 b(name)g(of)h(the)g
+(object)257 272 y Fl(aname)p 853 302 V 498 w(None)p 1352
+302 V 198 w Fn(attrib)n(ute)g(name)f(of)h(the)h(object)257
+372 y Fl(minOccurs)p 853 402 V 372 w(1)p 1352 402 V 274
+w Fn(schema)f(f)o(acet)g(minimum)f(occurances)257 472
+y Fl(maxOccurs)p 853 501 V 372 w(1)p 1352 501 V 274 w
+Fn(schema)h(f)o(acet)g(maximum)f(occurances)257 571 y
+Fl(nillable)p 853 601 V 323 w(False)p 1352 601 V 173
+w Fn(schema)h(f)o(acet)g(is)h(this)g(nillable)f(\()p
+Fl(xsi:nil="true")p Fn(\))257 671 y Fl(typed)p 853 900
+4 299 v 498 w(True)p 1352 900 V 198 w Fn(Output)f(type)g(information)e
+(\(in)j(the)f Fl(xsi:type)g Fn(attrib)n(ute\))g(when)g(serializing.)24
+b(By)1403 770 y(special)31 b(dispensation,)h(typecodes)e(within)h(a)g
+Fl(TC.Struct)f Fn(object)g(inherit)h(this)1403 870 y(from)19
+b(the)h(container)-5 b(.)257 970 y Fl(unique)p 853 1099
+4 200 v 522 w(0)p 1352 1099 V 274 w Fn(If)20 b(true,)f(the)h(object)g
+(is)h(unique)d(and)h(will)i(ne)n(v)o(er)e(be)h(\223aliased\224)f(with)i
+(another)d(object,)1403 1069 y(so)j(the)f Fl(id)g Fn(attrib)n(ute)g
+(need)g(not)f(be)i(output.)257 1169 y Fl(pyclass)p 853
+1298 V 398 w(None)p 1352 1298 V 198 w Fn(when)j(parsing)g(data,)i
+(instances)f(of)g(this)g(class)h(can)f(be)g(created)f(to)h(store)g(the)
+g(data.)1403 1269 y(Def)o(ault)20 b(beha)n(vior)f(is)i(re\003ecti)n(v)o
+(e)e(of)h(speci\002c)g(T)-7 b(ypeCode)19 b(classes.)257
+1368 y Fl(attrs_aname)p 853 1498 V 98 w('_attrs')p 1352
+1498 V 98 w Fn(attrib)n(ute)31 b(name)g(of)h(the)f(object)g(where)g
+(attrib)n(ute)h(v)n(alues)f(are)g(stored.)59 b(Used)32
+b(for)1403 1468 y(serialization)20 b(and)f(parsing.)208
+1596 y(Optional)24 b(elements)i(are)f(those)h(which)f(do)g(not)g(ha)n
+(v)o(e)g(to)h(be)g(an)f(incoming)f(message,)i(or)g(which)f(ha)n(v)o(e)g
+(the)h(XML)f(Schema)208 1695 y Fl(nil)e Fn(attrib)n(ute)g(set.)35
+b(When)24 b(parsing)e(the)h(message)h(as)g(part)f(of)g(a)h
+Fl(Struct)p Fn(,)f(then)g(the)g(Python)g(instance)g(attrib)n(ute)g
+(will)h(not)208 1795 y(be)d(set,)h(or)f(the)g(element)g(will)h(not)f
+(appear)f(as)i(a)g(dictionary)d(k)o(e)o(y)-5 b(.)27 b(When)21
+b(being)g(parsed)f(as)i(a)g(simple)f(type,)g(the)g(v)n(alue)g
+Fl(None)208 1895 y Fn(is)g(returned.)k(When)20 b(serializing)g(an)h
+(optional)e(element,)h(a)h(non-e)o(xistent)e(attrib)n(ute,)h(or)g(a)i
+(v)n(alue)e(of)g Fl(None)g Fn(is)i(tak)o(en)e(to)h(mean)208
+1994 y(not)e(present,)h(and)f(the)i(element)e(is)i(skipped.)0
+2141 y Fj(typechecks)208 2241 y Fn(This)i(is)g(a)h(class)g(attrib)n
+(ute.)32 b(If)23 b(true)g(\(the)f(def)o(ault\))g(then)h(all)g(typecode)
+e(constructors)g(do)i(more)f(rigorous)f(type-checking)f(on)208
+2340 y(their)f(parameters.)0 2487 y Fj(tag)208 2587 y
+Fn(This)35 b(is)h(a)f(class)h(attrib)n(ute.)69 b(Speci\002es)35
+b(the)g(global)f(element)g(declaration)g(this)h(typecode)e(represents,)
+38 b(the)d(v)n(alue)f(is)i(a)208 2687 y(`)p Fl(\(namespace,)47
+b(name\))p Fn(')19 b(tuple.)0 2833 y Fj(type)208 2933
+y Fn(This)44 b(is)h(a)g(class)g(attrib)n(ute.)97 b(Speci\002es)45
+b(the)f(global)f(type)h(de\002nition)f(this)i(typecode)e(represents,)49
+b(the)44 b(v)n(alue)g(is)h(a)208 3033 y(`)p Fl(\(namespace,)i(name\))p
+Fn(')19 b(tuple.)0 3180 y Fj(attribute_typecode_dict)208
+3279 y Fn(This)h(is)h(a)g(class)g(attrib)n(ute.)26 b(This)20
+b(is)h(a)g(dict)g(of)f(`)p Fl(\(URI,)48 b(NCName\))p
+Fn(')19 b(tuple)h(k)o(e)o(ys,)g(the)h(v)n(alues)f(of)g(each)g(is)h(a)g
+(typecode.)i(This)208 3379 y(is)28 b(ho)n(w)f(attrib)n(ute)h
+(declarations)e(other)h(than)g(SO)m(AP)h(and)f(XMLSchema)g(attrib)n
+(ute)g(declarations)g(\(e)o(g.)46 b Fl(xsi:type)p Fn(,)29
+b Fl(id)p Fn(,)208 3478 y Fl(href)p Fn(,)19 b(etc\))i(are)f
+(represented.)0 3625 y Fj(logger)208 3725 y Fn(This)g(is)h(a)g(class)g
+(attrib)n(ute.)j(logger)19 b(instance)h(for)g(this)g(class.)0
+3872 y(The)29 b(follo)n(wing)f(methods)g(are)i(useful)f(for)f
+(de\002ning)h(ne)n(w)g(typecode)e(classes;)36 b(see)30
+b(the)f(section)g(on)g(dynamic)f(typing)h(for)f(more)0
+3971 y(details.)d(In)20 b(all)h(of)f(the)g(follo)n(wing,)e(the)j
+Fl(ps)f Fn(parameter)f(is)i(a)f Fl(ParsedSoap)f Fn(object.)0
+4118 y Fj(checkname)p Fl(\()p Fk(elt,)g(ps)p Fl(\))208
+4218 y Fn(Checks)d(if)g(the)h(name)e(and)h(type)g(of)g(the)g(element)f
+Fl(elt)i Fn(are)f(correct)f(and)h(raises)h(a)f Fl(EvaluateException)e
+Fn(if)i(not.)24 b(Returns)208 4318 y(the)c(element')-5
+b(s)20 b(type)f(as)i(a)g(`)p Fl(\(uri,)49 b(localname\))p
+Fn(')18 b(tuple)i(if)g(so.)0 4464 y Fj(checktype)p Fl(\()p
+Fk(elt,)f(ps)p Fl(\))208 4564 y Fn(Lik)o(e)46 b Fl(checkname\(\))f
+Fn(e)o(xcept)h(that)h(the)g(element)f(name)g(is)h(ignored.)103
+b(This)47 b(method)e(is)j(actually)e(in)m(v)n(ok)o(ed)f(by)208
+4664 y Fl(checkname\(\))19 b Fn(to)i(do)f(the)h(second)f(half)h(of)f
+(its)i(processing,)e(b)n(ut)g(is)i(useful)e(to)h(in)m(v)n(ok)o(e)f
+(directly)-5 b(,)20 b(such)g(as)i(when)e(resolving)208
+4763 y(multi-reference)d(data.)0 4910 y Fj(nilled)p Fl(\()p
+Fk(elt,)i(ps)p Fl(\))208 5010 y Fn(If)33 b(the)h(element)f
+Fl(elt)g Fn(has)h(data,)j(this)d(returns)f Fl(False)p
+Fn(.)64 b(If)34 b(it)g(has)g(no)f(data,)k(and)c(the)g(typecode)f(is)j
+(not)e(optional,)j(an)208 5109 y Fl(EvaluateException)17
+b Fn(is)k(raised;)f(if)h(it)g(is)g(optional,)e(a)h Fl(True)g
+Fn(is)h(returned.)0 5256 y Fj(simple_value)p Fl(\()p
+Fk(elt,)d(ps,)i(mixed=F)-6 b(alse)p Fl(\))208 5356 y
+Fn(Returns)38 b(the)h(te)o(xt)g(content)f(of)h(the)f(element)h
+Fl(elt)p Fn(.)81 b(If)39 b(no)f(v)n(alue)g(is)i(present,)j(or)38
+b(the)h(element)g(has)g(non-te)o(xt)e(chil-)p 0 5549
+3901 4 v 0 5649 a Fi(20)1751 b(Chapter)24 b(6.)51 b(The)24
+b Fl(TypeCode)d Fi(classes)h(\227)h(data)h(con)n(v)n(ersions)p
+eop end
+%%Page: 21 27
+TeXDict begin 21 26 bop 208 83 a Fn(dren,)47 b(an)c Fl
+(EvaluateException)e Fn(is)j(raised.)93 b(If)43 b Fl(mixed)f
+Fn(is)i Fl(False)f Fn(if)g(child)g(elements)g(are)g(disco)o(v)o(ered)e
+(an)208 183 y Fl(EvaluateException)17 b Fn(is)k(raised,)f(else)h(join)f
+(all)h(te)o(xt)f(nodes)f(and)h(return)f(the)h(result.)0
+510 y Fg(6.2)121 b Fb(TC.Any)31 b Fg(\227)i(the)i(basis)e(of)h(dynamic)
+f(typing)0 743 y Fn(SO)m(AP)c(pro)o(vides)d(a)i(\003e)o(xible)g(set)g
+(of)g(serialization)f(rules,)j(ranging)c(from)h(completely)f
+(self-describing)g(to)i(completely)e(opaque,)0 843 y(requiring)18
+b(an)i(e)o(xternal)f(schema.)24 b(F)o(or)c(e)o(xample,)e(the)i(follo)n
+(wing)f(are)h(all)h(possible)e(w)o(ays)i(of)f(encoding)e(an)i(inte)o
+(ger)e(element)i Fl(i)g Fn(with)0 942 y(a)h(v)n(alue)e(of)h
+Fl(12)p Fn(:)0 1227 y Ff(6.2.1)100 b(simple)29 b(data)0
+1430 y Fn(\226)20 b(requires)f(type)h(information)236
+1668 y Fe(<tns:i)44 b(xsi:type="SOAP-ENC:integer">12</tns:i>)236
+1759 y(<tns:i)g(xsi:type="xsd:nonNegativeInteger">12</t)o(ns:i>)236
+1851 y(<SOAP-ENC:integer>12</SOAP-ENC:integer>)236 1942
+y(<tns:i>12</tns:i>)0 2229 y Fn(The)16 b(\002rst)h(three)f(lines)g(are)
+h(e)o(xamples)e(of)h Fk(typed)f Fn(elements.)24 b(If)16
+b Fl(ZSI)g Fn(is)h(ask)o(ed)f(to)h(parse)f(an)o(y)f(of)h(the)g(abo)o(v)
+o(e)f(e)o(xamples,)h(and)f(a)i Fl(TC.Any)0 2328 y Fn(typecode)k(is)i
+(gi)n(v)o(en,)f(it)h(will)g(properly)d(create)i(a)h(Python)f(inte)o
+(ger)f(for)h(the)g(\002rst)h(three,)g(and)f(raise)g(a)h
+Fl(EvaluateException)d Fn(for)0 2428 y(the)g(fourth.)0
+2713 y Ff(6.2.2)100 b(compound)30 b(data)0 2916 y Fn(\226)18
+b(Struct)g(or)g(Array)f(Compound)f(data,)i(such)g(as)h(a)f
+Fl(struct)p Fn(,)g(may)f(also)i(be)f(self-describing)e(\(namespace)g
+(are)i(omitted)g(for)f(clarity\):)236 3063 y Fe(<tns:foo>)416
+3154 y(<tns:i)43 b(xsi:type="SOAP-ENC:integer">12</tns:i>)416
+3245 y(<tns:name)g(xsi:type="SOAP-ENC:string">Hello)c(world</tns:name>)
+236 3337 y(</tns:foo>)0 3623 y Fn(If)20 b(this)h(is)g(parsed)e(with)i
+(a)f Fl(TC.Any)g Fn(typecode,)e(either)i(a)h(Python)e
+Fl(dict)h Fn(is)h(created)e(or)h(if)h Fl(aslist)e Fn(is)i(T)m(rue)f(a)h
+Fl(list)p Fn(:)236 3861 y Fe(ps)45 b(=)f(ParsedSoap\(xml,)f
+(envelope=False\))236 3953 y(print)h(ps.Parse\(TC.Any\(\)\))236
+4044 y({)135 b('name':)43 b(u'Hello)h(world',)178 b('i':)44
+b(12)134 b(})236 4227 y(print)44 b(ps.Parse\(TC.Any\(aslist=True\)\))
+236 4318 y([)h(12,)f(u'Hello)g(world')g(])0 4557 y Fn(Note)20
+b(that)g(one)g(preserv)o(es)f(order)m(,)f(while)j(the)f(other)f
+(preserv)o(es)g(the)h(element)g(names.)0 4842 y Ff(6.2.3)100
+b(class)28 b(descr)q(iption)0 5040 y Fo(class)21 b Fj(Any)p
+Fl(\()p Fk(name)p Fc([)p Fk(,)d(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 5140 y Fn(Used)20 b(for)f(parsing)h(incoming)e(SO)m
+(AP)j(data)f(\(that)g(is)h(typed\),)e(and)h(serializing)f(outgoing)f
+(Python)h(data.)208 5273 y(The)g(follo)n(wing)g(k)o(e)o(yw)o(ord)f(ar)o
+(guments)g(may)i(be)g(used:)p 0 5549 3901 4 v 0 5649
+a Fi(6.2.)52 b Fl(TC.Any)22 b Fi(\227)h(the)h(basis)f(of)g(dynamic)g
+(typing)2189 b(21)p eop end
+%%Page: 22 28
+TeXDict begin 22 27 bop 415 70 a Fo(K)n(eyw)o(ord)p 788
+100 4 100 v 98 w(Default)p 1150 100 V 99 w(Description)p
+365 103 3377 4 v 415 173 a Fl(aslist)p 788 402 4 299
+v 131 w(False)p 1150 402 V 106 w Fn(If)30 b(true,)i(then)e(the)g(data)g
+(is)h(\(recursi)n(v)o(ely\))d(treated)h(as)i(a)g(list)g(of)f(v)n
+(alues.)55 b(The)29 b(de-)1202 272 y(f)o(ault)20 b(is)h(a)g(Python)e
+(dictionary)-5 b(,)18 b(which)h(preserv)o(es)g(parameter)g(names)h(b)n
+(ut)g(loses)h(the)1202 372 y(ordering.)48 b(Ne)n(w)21
+b(in)f(v)o(ersion)f(1.1.)208 500 y(In)f(addition,)f(if)i(the)g(Python)e
+(object)i(being)e(serialized)i(with)g(an)f Fl(Any)h Fn(has)g(a)g
+Fl(typecode)e Fn(attrib)n(ute,)i(then)f(the)h Fl(serialize)208
+600 y Fn(method)29 b(of)i(the)g(typecode)e(will)i(be)g(in)m(v)n(ok)o
+(ed)e(to)i(do)g(the)g(serialization.)56 b(This)31 b(allo)n(ws)g
+(objects)g(to)g(o)o(v)o(erride)d(the)j(def)o(ault)208
+699 y(dynamic)18 b(serialization.)0 846 y(Referring)h(back)h(to)g(the)h
+(compound)c(XML)k(data)f(abo)o(v)o(e,)e(it)j(is)h(possible)e(to)g
+(create)g(a)h(ne)n(w)f(typecode)f(capable)g(of)i(parsing)e(elements)0
+946 y(of)g(type)g Fl(mytype)p Fn(.)25 b(This)19 b(class)i(w)o(ould)e
+(kno)n(w)f(that)i(the)f Fl(i)h Fn(element)f(is)i(an)e(inte)o(ger)m(,)f
+(so)i(that)g(the)f(e)o(xplicit)g(typing)f(becomes)h(optional,)0
+1045 y(rather)g(than)h(required.)0 1330 y Ff(6.2.4)100
+b(Adding)30 b(ne)n(w)e(types)0 1533 y Fn(Most)d(of)f(the)h
+Fl(TypeCodes)f Fn(classes)i(in)f Fl(TC)g Fn(are)f(re)o(gistered)g(with)
+h Fl(Any)p Fn(,)g(making)f(an)g(instance)h(of)f(itself)i(a)n(v)n
+(ailable)e(for)g(dynamic)0 1633 y(typing.)50 b(Ne)n(w)29
+b Fl(TypeCode)f Fn(classes)i(can)f(be)g(created)f(and)g(re)o(gistered)g
+(with)h Fl(Any)g Fn(by)f(using)h Fl(RegisterType)p Fn(.)49
+b(In)29 b(order)e(to)0 1732 y(o)o(v)o(erride)22 b(an)i(e)o(xisting)f
+(entry)g(in)h(the)g(re)o(gistry)f(call)h Fl(RegisterType)e
+Fn(with)i Fl(clobber=True)p Fn(.)35 b(The)24 b(serialization)f(entries)
+h(are)0 1832 y(mappings)c(between)h(b)n(uiltin)g(Python)g(types)g(and)h
+(a)g Fl(TypeCode)e Fn(instance,)i(it)g(is)h(not)e(possible)g(to)h(ha)n
+(v)o(e)f(one)g(Python)g(type)g(map)g(to)0 1932 y(multiple)j(typecodes.)
+38 b(The)25 b(parsing)f(entries)h(are)g(mappings)e(between)i
+Fl(\(namespaceURI,name\))d Fn(tuples,)k(representing)d(the)0
+2031 y Fl(xsi:type)17 b Fn(attrib)n(ute,)h(and)g(a)g
+Fl(TypeCode)f Fn(instance.)24 b(Thus,)18 b(only)f(one)h(instance)g(of)f
+(a)i Fl(TypeCode)e Fn(class)i(can)f(represent)f(a)i(XML)0
+2131 y(Schema)h(type.)k(So)c(this)h(mechanism)e(is)i(not)f(appropriate)
+e(for)h(representing)g(XML)h(Schema)f(element)h(information.)0
+2278 y Fo(class)h Fa(NEWTYPECODE)p Fj(\(TypeCode\))p
+Fl(\()p Fk(...)p Fl(\))208 2377 y Fn(The)f(ne)n(w)h(typecode)f(should)g
+(be)h(deri)n(v)o(ed)e(from)i(the)g Fl(TC.TypeCode)e Fn(class,)k(and)d
+Fl(TypeCode.__init__\(\))e Fn(must)k(be)208 2477 y(in)m(v)n(ok)o(ed)c
+(in)i(the)h(ne)n(w)f(class')-5 b(s)21 b(constructor)-5
+b(.)0 2624 y Fj(parselist)208 2724 y Fn(This)38 b(is)h(a)f(class)h
+(attrib)n(ute,)j(used)37 b(when)h(parsing)e(incoming)h(SO)m(AP)h(data.)
+78 b(It)39 b(should)e(be)g(a)i(sequence)e(of)g(`)p Fl(\(uri,)208
+2823 y(localname\))p Fn(')24 b(tuples)i(to)g(identify)f(the)i
+(datatype.)41 b(If)26 b Fl(uri)g Fn(is)h Fl(None)p Fn(,)h(it)e(is)i
+(tak)o(en)d(to)i(mean)e(either)h(the)g(XML)g(Schema)208
+2923 y(namespace)15 b(or)i(the)g(SO)m(AP)h(encoding)d(namespace;)i
+(this)g(should)f(only)g(be)h(used)g(if)g(adding)e(support)h(for)g
+(additional)g(primiti)n(v)o(e)208 3022 y(types.)24 b(If)d(this)f(list)i
+(is)f(empty)-5 b(,)19 b(then)g(the)i(type)e(of)h(the)h(incoming)d(SO)m
+(AP)j(data)f(is)i(assumed)d(to)i(be)f(correct;)f(an)i(empty)e(list)i
+(also)208 3122 y(means)e(that)i(incoming)d(typed)h(data)h(cannot)g(by)f
+(dynamically)g(parsed.)0 3269 y Fj(errorlist)208 3369
+y Fn(This)26 b(is)i(a)f(class)g(attrib)n(ute,)g(used)g(when)f
+(reporting)e(a)j(parsing)e(error)-5 b(.)44 b(It)26 b(is)i(a)f(te)o(xt)f
+(string)g(naming)f(the)i(datatype)e(that)i(w)o(as)208
+3468 y(e)o(xpected.)c(If)d(not)g(de\002ned,)f Fl(ZSI)h
+Fn(will)h(create)f(this)g(attrib)n(ute)g(from)f(the)i
+Fl(parselist)e Fn(attrib)n(ute)h(when)f(it)i(is)g(needed.)0
+3615 y Fj(seriallist)208 3715 y Fn(This)28 b(is)g(a)g(class)h(attrib)n
+(ute,)g(used)f(when)f(serializing)g(Python)g(objects)g(dynamically)-5
+b(.)46 b(It)28 b(speci\002es)g(what)g(types)g(of)f(object)208
+3814 y(instances)19 b(\(or)f(Python)h(types\))f(this)i(typecode)e(can)h
+(serialize.)25 b(It)19 b(should)f(be)i(a)f(sequence,)f(where)h(each)g
+(element)g(is)h(a)g(Python)208 3914 y(class)25 b(object,)f(a)h(string)f
+(naming)f(the)i(class,)h(or)e(a)h(type)f(object)f(from)h(Python')-5
+b(s)23 b Fl(types)h Fn(module)f(\(if)h(the)h(ne)n(w)f(typecode)f(is)208
+4014 y(serializing)c(a)i(b)n(uilt-in)e(Python)g(type\).)0
+4160 y Fj(parse)p Fl(\()p Fk(elt,)g(ps)p Fl(\))208 4260
+y(ZSI)32 b Fn(in)m(v)n(ok)o(es)f(this)i(method)e(to)h(parse)g(the)h
+Fl(elt)f Fn(element)f(and)h(return)f(its)j(Python)d(v)n(alue.)60
+b(The)32 b Fl(ps)h Fn(parameter)d(is)k(the)208 4360 y
+Fl(ParsedSoap)18 b Fn(object,)i(and)f(can)h(be)h(used)f(for)f
+(dereferencing)e Fl(href)p Fn(')-5 b(s,)20 b(calling)g
+Fl(Backtrace\(\))f Fn(to)h(report)f(errors,)g(etc.)0
+4507 y Fj(serialize)p Fl(\()p Fk(sw)-6 b(,)19 b(pyobj)p
+Fc([)p Fk(,)g(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+4606 y(ZSI)j Fn(in)m(v)n(ok)o(es)g(this)h(method)e(to)h(output)g(a)h
+(Python)e(object)h(to)h(a)g(SO)m(AP)g(stream.)23 b(The)16
+b Fl(sw)f Fn(parameter)f(will)i(be)g(a)g Fl(SoapWriter)208
+4706 y Fn(object,)j(and)h(the)g Fl(pyobj)g Fn(parameter)e(is)j(the)g
+(Python)e(object)g(to)i(serialize.)208 4839 y(The)e(follo)n(wing)g(k)o
+(e)o(yw)o(ord)f(ar)o(guments)g(may)i(be)g(used:)p 0 5549
+3901 4 v 0 5649 a Fi(22)1751 b(Chapter)24 b(6.)51 b(The)24
+b Fl(TypeCode)d Fi(classes)h(\227)h(data)h(con)n(v)n(ersions)p
+eop end
+%%Page: 23 29
+TeXDict begin 23 28 bop 294 70 a Fo(K)n(eyw)o(ord)p 740
+100 4 100 v 256 w(Default)p 1272 100 V 183 w(Description)p
+244 103 3620 4 v 294 173 a Fl(attrtext)p 740 302 4 200
+v 214 w(None)p 1272 302 V 215 w Fn(T)-6 b(e)o(xt)21 b(\(with)g(leading)
+f(space\))g(to)i(output)d(as)j(an)f(attrib)n(ute;)g(this)h(is)g
+(normally)d(used)i(by)1323 272 y(the)f Fl(TC.Array)g
+Fn(class)h(to)f(pass)h(do)n(wn)e(inde)o(xing)f(information.)294
+372 y Fl(name)p 740 501 V 414 w(None)p 1272 501 V 215
+w Fn(Name)e(to)g(use)h(for)e(serialization;)i(def)o(aults)f(to)g(the)h
+(name)e(speci\002ed)h(in)g(the)g(typecode,)1323 472 y(or)k(a)h
+(generated)d(name.)294 571 y Fl(typed)p 740 701 V 248
+w Fk(per)n(-typecode)p 1272 701 V 97 w Fn(Whether)i(or)f(not)h(to)g
+(output)f(type)h(information;)e(the)i(def)o(ault)f(is)i(to)f(use)h(the)
+f(v)n(alue)f(in)1323 671 y(the)h(typecode.)0 813 y(Once)j(the)h(ne)n(w)
+f(typecode)f(class)j(has)f(been)f(de\002ned,)g(it)h(should)f(be)g(re)o
+(gistered)g(with)g Fl(ZSI)p Fn(')-5 b(s)24 b(dynamic)f(type)g(system)h
+(by)f(in)m(v)n(oking)0 912 y(the)d(follo)n(wing)f(function:)0
+1059 y Fj(RegisterType)p Fl(\()p Fk(class)p Fc([)p Fk(,)f(clobber=0)p
+Fc([)p Fk(,)g(**k)o(e)n(ywor)m(ds)12 b Fc(])g(])p Fl(\))208
+1159 y Fn(By)20 b(def)o(ault,)f(it)i(is)g(an)e(error)g(to)h(replace)f
+(an)h(e)o(xisting)g(type)f(re)o(gistration,)f(and)i(an)f(e)o(xception)f
+(will)j(be)f(raised.)25 b(The)19 b Fl(clobber)208 1259
+y Fn(parameter)i(may)i(be)h(gi)n(v)o(en)e(to)h(allo)n(w)g(replacement.)
+33 b(A)24 b(single)f(instance)g(of)g(the)g Fl(class)g
+Fn(object)g(will)h(be)g(created,)f(and)g(the)208 1358
+y Fl(keyword)c Fn(parameters)g(are)h(passed)g(to)h(the)f(constructor)-5
+b(.)0 1505 y(If)19 b(the)g(class)h(is)g(not)e(re)o(gistered,)g(then)g
+(instances)h(of)g(the)g(class)h(cannot)e(be)h(processed)f(as)h(dynamic)
+f(types.)24 b(This)19 b(may)g(be)g(acceptable)0 1605
+y(in)h(some)g(en)m(vironments.)0 1932 y Fg(6.3)121 b
+Fb(TC.SimpleType)0 2165 y Fn(P)o(arent)20 b(class)h(of)f(all)g(simple)h
+(types.)0 2312 y Fj(empty_content)208 2411 y Fn(This)f(is)h(a)g(class)g
+(attrib)n(ute.)j(V)-9 b(alue)20 b(returned)f(when)g(tag)h(or)g(node)f
+(is)j(present,)d(is)i(not)f(nilled,)f(and)h(without)g(te)o(xt)g
+(content.)0 2739 y Fg(6.4)121 b(Str)r(ings)0 2972 y Fn(SO)m
+(AP/XMLSchema)19 b(Strings)h(are)h(Python)e(strings.)0
+3119 y Fo(class)i Fj(String)p Fl(\()p Fk(name)p Fc([)p
+Fk(,)d(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208 3218
+y Fn(The)19 b(parent)h(type)f(of)h(all)h(strings.)208
+3351 y(The)e(follo)n(wing)g(k)o(e)o(yw)o(ord)f(ar)o(guments)g(may)i(be)
+g(used:)378 3480 y Fo(K)n(eyw)o(ord)p 825 3510 4 100
+v 172 w(Default)p 1187 3510 V 99 w(Description)p 328
+3513 3451 4 v 378 3583 a Fl(resolver)p 825 3713 4 200
+v 130 w(None)p 1187 3713 V 131 w Fn(A)33 b(function)e(that)h(can)h
+(resolv)o(e)e(an)i(absolute)f(URI)h(and)f(return)f(its)j(content)d(as)j
+(a)1239 3683 y(string,)19 b(as)i(described)e(in)i(the)f
+Fl(ParsedSoap)f Fn(description.)378 3782 y Fl(strip)p
+825 3812 4 100 v 280 w(True)p 1187 3812 V 131 w Fn(If)h(true,)f
+(leading)h(and)f(trailing)h(whitespace)f(are)i(stripped)e(from)g(the)h
+(content.)0 3948 y Fo(class)h Fj(Enumeration)p Fl(\()p
+Fk(value)p 973 3948 25 4 v 26 w(list,)g(name)p Fc([)p
+Fk(,)e(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208 4047
+y Fn(Lik)o(e)20 b Fl(TC.String)p Fn(,)e(b)n(ut)j(the)f(v)n(alue)f(must)
+i(be)f(a)g(member)f(of)h(the)g Fl(choices)g Fn(sequence)f(of)h(te)o(xt)
+g(strings)0 4194 y(In)h(addition)f(to)i Fl(TC.String)p
+Fn(,)e(the)h(basic)h(string,)f(se)n(v)o(eral)g(subtypes)f(are)i(pro)o
+(vided)c(that)k(transparently)d(handle)i(common)e(encod-)0
+4294 y(ings.)34 b(These)23 b(classes)i(create)e(a)h(temporary)d(string)
+i(object)f(and)h(pass)h(that)f(to)h(the)f Fl(serialize\(\))f
+Fn(method.)33 b(When)23 b(doing)f(RPC)0 4394 y(encoding,)h(and)g
+(checking)f(for)h(non-unique)e(strings,)k(the)f Fl(TC.String)e
+Fn(class)j(must)f(ha)n(v)o(e)g(the)g(original)e(Python)h(string,)h(as)h
+(well)0 4493 y(as)c(the)f(ne)n(w)g(output.)k(This)c(is)h(done)e(by)h
+(adding)f(a)i(parameter)d(to)j(the)f Fl(serialize\(\))f
+Fn(method:)311 4719 y Fo(K)n(eyw)o(ord)p 684 4749 4 100
+v 99 w(Default)p 1047 4749 V 98 w(Description)p 262 4753
+3377 4 v 311 4822 a Fl(orig)p 684 5052 4 299 v 256 w(None)p
+1047 5052 V 131 w Fn(If)e(deri)n(ving)f(a)h(ne)n(w)g(typecode)f(from)g
+(the)i(string)e(class,)j(and)e(the)g(deri)n(v)n(ation)e(creates)j(a)
+1098 4922 y(temporary)c(Python)h(string)h(\(such)g(as)h(by)e
+Fl(Base64String)p Fn(\),)g(than)h(this)h(parameter)1098
+5022 y(is)k(the)f(original)f(string)h(being)f(serialized.)0
+5223 y Fo(class)i Fj(Base64String)p Fl(\()p Fk(name)p
+Fc([)p Fk(,)c(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+5322 y Fn(The)19 b(v)n(alue)h(is)h(encoded)d(in)j(Base-64.)p
+0 5549 3901 4 v 0 5649 a Fi(6.3.)52 b Fl(TC.SimpleType)2968
+b Fi(23)p eop end
+%%Page: 24 30
+TeXDict begin 24 29 bop 0 83 a Fo(class)21 b Fj(HexBinaryString)p
+Fl(\()p Fk(name)p Fc([)p Fk(,)16 b(**k)o(e)n(ywor)m(ds)c
+Fc(])p Fl(\))208 183 y Fn(Each)19 b(byte)h(is)h(encoded)e(as)h(its)i
+(printable)d(v)o(ersion.)0 330 y Fo(class)i Fj(URI)p
+Fl(\()p Fk(name)p Fc([)p Fk(,)d(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 429 y Fn(The)19 b(v)n(alue)h(is)h(URL)g(quoted)e
+(\(e.g.,)g Fl(\04520)h Fn(for)f(the)i(space)f(character\).)0
+576 y(It)g(is)h(often)e(the)g(case)i(that)f(a)g(parameter)e(will)i(be)g
+(typed)f(as)h(a)h(string)e(for)g(transport)g(purposes,)f(b)n(ut)i(will)
+g(in)g(f)o(act)g(ha)n(v)o(e)f(special)h(syntax)0 676
+y(and)i(processing)f(requirements.)29 b(F)o(or)22 b(e)o(xample,)f(a)i
+(string)f(could)g(be)g(used)g(for)g(an)g(XP)o(ath)g(e)o(xpression,)f(b)
+n(ut)h(it)h(is)h(more)d(con)m(v)o(enient)0 775 y(for)f(the)g(Python)f
+(v)n(alue)g(to)i(actually)e(be)h(the)h(compiled)d(e)o(xpression.)24
+b(Here)c(is)h(ho)n(w)f(to)g(do)g(that:)236 1013 y Fe(import)44
+b(xml.xpath.pyxpath)236 1105 y(import)g(xml.xpath.pyxpath.Compile)d(as)
+j(_xpath_compile)236 1287 y(class)g(XPathString\(TC.String\):)416
+1379 y(def)g(__init__\(self,)e(name,)1536 1392 y(**)1626
+1379 y(kw\):)595 1470 y(TC.String.__init__\(self,)f(name,)1985
+1483 y(**)2075 1470 y(kw\))416 1653 y(def)j(parse\(self,)f(elt,)h
+(ps\):)595 1744 y(val)g(=)h(TC.String.parse\(self,)c(elt,)j(ps\))595
+1835 y(try:)774 1927 y(val)h(=)f(_xpath_compile\(val\))595
+2018 y(except:)774 2109 y(raise)g(EvaluateException\("Invalid)d(XPath)j
+(expression",)1312 2201 y(ps.Backtrace\(elt\)\))595 2292
+y(return)g(val)0 2579 y Fn(In)27 b(particular)m(,)h(it)h(is)f(common)e
+(to)i(send)g(XML)f(as)i(a)f(string,)h(using)e(entity)h(encoding)d(to)j
+(protect)f(the)h(ampersand)e(and)h(less-than)0 2678 y(characters.)0
+2825 y Fo(class)21 b Fj(XMLString)p Fl(\()p Fk(name)p
+Fc([)p Fk(,)c(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+2925 y Fn(P)o(arses)19 b(the)h(data)f(as)h(a)g(string,)f(b)n(ut)g
+(returns)g(an)g(XML)g(DOM)h(object.)k(F)o(or)19 b(serialization,)g(tak)
+o(es)g(an)g(XML)h(DOM)f(\(or)g(element)208 3024 y(node\),)f(and)i
+(outputs)f(it)i(as)g(a)f(string.)208 3157 y(The)f(follo)n(wing)g(k)o(e)
+o(yw)o(ord)f(ar)o(guments)g(may)i(be)g(used:)303 3286
+y Fo(K)n(eyw)o(ord)p 900 3316 4 100 v 322 w(Default)p
+1262 3316 V 98 w(Description)p 254 3320 3601 4 v 303
+3389 a Fl(readerclass)p 900 3519 4 200 v 130 w(None)p
+1262 3519 V 130 w Fn(Class)53 b(used)d(to)h(create)f(DOM-creating)f
+(XML)h(readers;)66 b(described)49 b(in)i(the)1313 3489
+y Fl(ParsedSoap)19 b Fn(chapter)-5 b(.)0 3791 y Fg(6.5)121
+b(Integers)0 4024 y Fn(SO)m(AP/XMLSchema)19 b(inte)o(gers)h(are)g
+(Python)f(inte)o(gers.)0 4171 y Fo(class)i Fj(Integer)p
+Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+4271 y Fn(The)19 b(parent)h(type)f(of)h(all)h(inte)o(gers.)j(This)c
+(class)i(handles)d(an)o(y)g(of)h(the)h(se)n(v)o(eral)e(types)h(\(and)f
+(ranges\))g(of)h(SO)m(AP)h(inte)o(gers.)208 4404 y(The)e(follo)n(wing)g
+(k)o(e)o(yw)o(ord)f(ar)o(guments)g(may)i(be)g(used:)415
+4533 y Fo(K)n(eyw)o(ord)p 788 4563 4 100 v 98 w(Default)p
+1150 4563 V 99 w(Description)p 365 4566 3377 4 v 415
+4636 a Fl(format)p 788 4666 4 100 v 206 w(\045d)p 1150
+4666 V 181 w Fn(F)o(ormat)f(string)h(for)g(serializing.)50
+b(Ne)n(w)20 b(in)g(v)o(ersion)f(1.2.)0 4801 y Fo(class)i
+Fj(IEnumeration)p Fl(\()p Fk(c)o(hoices)p Fc([)p Fk(,)16
+b(**k)o(e)n(ywor)m(ds)c Fc(])p Fl(\))208 4901 y Fn(Lik)o(e)20
+b Fl(TC.Integer)p Fn(,)e(b)n(ut)i(the)h(v)n(alue)e(must)h(be)h(a)f
+(member)f(of)h(the)g Fl(choices)g Fn(sequence.)0 5048
+y(A)h(number)d(of)i(sub-classes)g(are)g(de\002ned)g(to)g(handle)f
+(smaller)n(-ranged)f(numbers.)0 5194 y Fo(class)j Fj(Ibyte)p
+Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+5294 y Fn(A)20 b(signed)g(eight-bit)f(v)n(alue.)p 0 5549
+3901 4 v 0 5649 a Fi(24)1751 b(Chapter)24 b(6.)51 b(The)24
+b Fl(TypeCode)d Fi(classes)h(\227)h(data)h(con)n(v)n(ersions)p
+eop end
+%%Page: 25 31
+TeXDict begin 25 30 bop 0 83 a Fo(class)21 b Fj(IunsignedByte)p
+Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+183 y Fn(An)20 b(unsigned)e(eight-bit)h(v)n(alue.)0 330
+y Fo(class)i Fj(Ishort)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 429 y Fn(A)20 b(signed)g(16-bit)f(v)n(alue.)0
+576 y Fo(class)i Fj(IunsignedShort)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m
+(ds)12 b Fc(])p Fl(\))208 676 y Fn(An)20 b(unsigned)e(16-bit)h(v)n
+(alue.)0 823 y Fo(class)i Fj(Iint)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m
+(ds)12 b Fc(])p Fl(\))208 922 y Fn(A)20 b(signed)g(32-bit)f(v)n(alue.)0
+1069 y Fo(class)i Fj(IunsignedInt)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m
+(ds)12 b Fc(])p Fl(\))208 1169 y Fn(An)20 b(unsigned)e(32-bit)h(v)n
+(alue.)0 1316 y Fo(class)i Fj(Ilong)p Fl(\()p Fc([)p
+Fk(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208 1415 y Fn(An)20
+b(signed)f(64-bit)g(v)n(alue.)0 1562 y Fo(class)i Fj(IunsignedLong)p
+Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+1662 y Fn(An)20 b(unsigned)e(64-bit)h(v)n(alue.)0 1808
+y Fo(class)i Fj(IpositiveInteger)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m
+(ds)12 b Fc(])p Fl(\))208 1908 y Fn(A)20 b(v)n(alue)g(greater)f(than)h
+(zero.)0 2055 y Fo(class)h Fj(InegativeInteger)p Fl(\()p
+Fc([)p Fk(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208 2155
+y Fn(A)20 b(v)n(alue)g(less)h(than)f(zero.)0 2301 y Fo(class)h
+Fj(InonPositiveInteger)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 2401 y Fn(A)20 b(v)n(alue)g(less)h(than)f(or)g
+(equal)f(to)i(zero.)0 2548 y Fo(class)g Fj(InonNegativeInteger)p
+Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+2648 y Fn(A)20 b(v)n(alue)g(greater)f(than)h(or)g(equal)f(to)i(zero.)0
+2975 y Fg(6.6)121 b(Floating-point)35 b(Numbers)0 3208
+y Fn(SO)m(AP/XMLSchema)19 b(\003oating)h(point)f(numbers)g(are)h
+(Python)f(\003oats.)0 3355 y Fo(class)i Fj(Decimal)p
+Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+3454 y Fn(The)19 b(parent)h(type)g(of)f(all)i(\003oating)f(point)f
+(numbers.)24 b(This)c(class)h(handles)f(an)o(y)f(of)h(the)h(se)n(v)o
+(eral)e(types)h(\(and)g(ranges\))f(of)h(SO)m(AP)208 3554
+y(\003oating)f(point)g(numbers.)208 3687 y(The)g(follo)n(wing)g(k)o(e)o
+(yw)o(ord)f(ar)o(guments)g(may)i(be)g(used:)415 3816
+y Fo(K)n(eyw)o(ord)p 788 3846 4 100 v 98 w(Default)p
+1150 3846 V 99 w(Description)p 365 3849 3377 4 v 415
+3919 a Fl(format)p 788 3949 4 100 v 206 w(\045f)p 1150
+3949 V 181 w Fn(F)o(ormat)f(string)h(for)g(serializing.)50
+b(Ne)n(w)20 b(in)g(v)o(ersion)f(1.2.)0 4084 y Fo(class)i
+Fj(FPEnumeration)p Fl(\()p Fk(value)p 1073 4084 25 4
+v 26 w(list,)g(name)p Fc([)p Fk(,)e(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 4184 y Fn(Lik)o(e)22 b Fl(TC.Decimal)p
+Fn(,)h(b)n(ut)g(the)g(v)n(alue)f(must)h(be)g(a)g(member)f(of)h(the)g
+Fl(value_list)e Fn(sequence.)32 b(Be)24 b(careful)e(of)h(round-of)n(f)
+208 4284 y(errors)c(if)h(using)g(this)h(class.)0 4430
+y(T)-7 b(w)o(o)20 b(sub-classes)h(are)f(de\002ned)f(to)h(handle)f
+(smaller)n(-ranged)g(numbers.)0 4577 y Fo(class)i Fj(FPfloat)p
+Fl(\()p Fk(name)p Fc([)p Fk(,)c(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 4677 y Fn(An)20 b(IEEE)f(single-precision)f(32-bit)i
+(\003oating)f(point)g(v)n(alue.)0 4824 y Fo(class)i Fj(FPdouble)p
+Fl(\()p Fk(name)p Fc([)p Fk(,)c(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 4923 y Fn(An)20 b(IEEE)f(double-precision)e(64-bit)i
+(\003oating)h(point)f(v)n(alue.)p 0 5549 3901 4 v 0 5649
+a Fi(6.6.)52 b(Floating-point)25 b(Numbers)2755 b(25)p
+eop end
+%%Page: 26 32
+TeXDict begin 26 31 bop 0 86 a Fg(6.7)121 b(Dates)34
+b(and)h(Times)0 319 y Fn(SO)m(AP)25 b(dates)e(and)h(times)g(are)f
+(Python)g(time)h(tuples)g(in)f(UTC)i(\(GMT\),)e(as)h(documented)d(in)j
+(the)g(Python)e Fl(time)i Fn(module.)34 b(T)m(ime)24
+b(is)0 419 y(trick)o(y)-5 b(,)19 b(and)h(processing)f(an)o(ything)f
+(other)i(than)g(a)h(simple)f(absolute)g(time)g(can)h(be)f(dif)n
+(\002cult.)25 b(\(Ev)o(en)19 b(then,)h(timezones)g(lie)h(in)f(w)o(ait)h
+(to)0 518 y(trip)f(up)g(the)g(unw)o(ary)-5 b(.\))23 b(A)e(fe)n(w)f(ca)n
+(v)o(eats)g(are)g(in)g(order:)104 748 y(1.)41 b(Some)18
+b(date)i(and)e(time)i(formats)e(will)i(be)f(parsed)g(into)g(tuples)g
+(that)g(are)g(not)g(v)n(alid)g(time)g(v)n(alues.)24 b(F)o(or)19
+b(e)o(xample,)f(75)h(minutes)g(is)208 848 y(a)h(v)n(alid)g(duration,)e
+(although)g(not)i(a)h(le)o(gal)e(v)n(alue)h(for)f(the)i(minutes)e
+(element)h(of)g(a)g(time)h(tuple.)104 1014 y(2.)41 b(Fractional)19
+b(parts)h(of)g(a)h(second)e(may)h(be)g(lost)h(when)e(parsing,)g(and)h
+(may)f(ha)n(v)o(e)h(e)o(xtra)f(trailing)h(zero')-5 b(s)20
+b(when)g(serializing.)104 1180 y(3.)41 b(Badly-formed)13
+b(time)j(tuples)g(may)f(result)i(in)f(non-sensical)e(v)n(alues)i(being)
+f(serialized;)i(the)f(\002rst)h(six)f(v)n(alues)g(are)g(tak)o(en)f
+(directly)208 1280 y(as)20 b(year)m(,)g(month,)e(day)-5
+b(,)19 b(hour)m(,)g(minute,)g(second)g(in)h(UTC.)104
+1446 y(4.)41 b(Although)17 b(the)i(classes)h Fl(Duration)f
+Fn(and)f Fl(Gregorian)g Fn(are)i(de\002ned,)e(the)o(y)g(are)h(for)g
+(internal)f(use)i(only)e(and)h(should)f(not)h(be)208
+1545 y(included)c(in)h(an)o(y)g Fl(TypeCode)g Fn(you)g(de\002ne.)23
+b(Instead,)16 b(use)h(the)g(classes)g(be)o(ginning)d(with)j(a)g(lo)n
+(wer)f(case)h(g)g(in)g(your)e(typecodes.)0 1775 y(In)20
+b(addition,)f(badly-formed)d(v)n(alues)k(may)g(result)g(in)g
+(non-sensical)f(serializations.)0 1922 y(When)h(serializing,)f(an)i
+(inte)o(gral)e(or)h(\003oating)f(point)g(number)g(is)i(tak)o(en)f(as)h
+(the)f(number)e(of)i(seconds)g(since)g(the)g(epoch,)f(in)h(UTC.)0
+2069 y Fo(class)h Fj(Duration)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)
+12 b Fc(])p Fl(\))208 2169 y Fn(A)21 b(relati)n(v)o(e)f(time)h(period.)
+26 b(Ne)o(gati)n(v)o(e)19 b(durations)g(ha)n(v)o(e)h(all)i(v)n(alues)e
+(less)i(than)e(zero;)h(this)h(mak)o(es)e(it)i(easy)f(to)g(add)f(a)h
+(duration)e(to)208 2268 y(a)h(Python)f(time)i(tuple.)0
+2415 y Fo(class)g Fj(Gregorian)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)
+12 b Fc(])p Fl(\))208 2515 y Fn(An)20 b(absolute)f(time)i(period.)i
+(This)e(class)g(should)e(not)h(be)g(instantiated)f(directly;)h(use)g
+(one)g(of)g(the)g Fl(gXXX)g Fn(classes)h(instead.)0 2662
+y Fo(class)g Fj(gDateTime)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 2761 y Fn(A)20 b(date)g(and)g(time.)0
+2908 y Fo(class)h Fj(gDate)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 3008 y Fn(A)20 b(date.)0 3155 y Fo(class)h
+Fj(gYearMonth)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 3254 y Fn(A)20 b(year)g(and)g(month.)0
+3401 y Fo(class)h Fj(gYear)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 3501 y Fn(A)20 b(year)-5 b(.)0 3648
+y Fo(class)21 b Fj(gMonthDay)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 3747 y Fn(A)20 b(month)f(and)h(day)-5
+b(.)0 3894 y Fo(class)21 b Fj(gDay)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m
+(ds)12 b Fc(])p Fl(\))208 3994 y Fn(A)20 b(day)-5 b(.)0
+4141 y Fo(class)21 b Fj(gTime)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)
+12 b Fc(])p Fl(\))208 4240 y Fn(A)20 b(time.)0 4568 y
+Fg(6.8)121 b(Boolean)0 4800 y Fn(SO)m(AP)21 b(Booleans)f(are)g(Python)f
+(inte)o(gers.)0 4947 y Fo(class)i Fj(Boolean)p Fl(\()p
+Fc([)p Fk(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208 5047
+y Fn(When)18 b(marshaling)e(zero)i(or)g(the)g(w)o(ord)f(\223f)o
+(alse\224)i(is)g(returned)d(as)j Fl(0)g Fn(and)e(an)o(y)h(non-zero)e(v)
+n(alue)h(or)h(the)g(w)o(ord)g(\223true\224)f(is)i(returned)208
+5147 y(as)h Fl(1)p Fn(.)26 b(When)20 b(serializing,)f(the)h(number)f
+Fl(0)h Fn(or)g Fl(1)h Fn(will)g(be)f(generated.)p 0 5549
+3901 4 v 0 5649 a Fi(26)1751 b(Chapter)24 b(6.)51 b(The)24
+b Fl(TypeCode)d Fi(classes)h(\227)h(data)h(con)n(v)n(ersions)p
+eop end
+%%Page: 27 33
+TeXDict begin 27 32 bop 0 86 a Fg(6.9)121 b(XML)0 319
+y Fn(XML)20 b(is)g(a)g(Python)f(DOM)h(element)f(node.)k(If)d(the)f(v)n
+(alue)g(to)h(be)g(serialized)f(is)h(a)g(Python)f(string,)g(then)g(an)h
+Fl(href)f Fn(is)i(generated,)c(with)0 419 y(the)24 b(v)n(alue)f(used)h
+(as)g(the)g(URI.)g(This)g(can)g(be)g(used,)g(for)f(e)o(xample,)g(when)h
+(generating)d(SO)m(AP)k(with)f(attachments.)35 b(Otherwise,)25
+b(the)0 518 y(XML)20 b(is)h(typically)f(put)g(inside)g(a)g(wrapper)f
+(element)h(that)g(sets)h(the)f(proper)f(SO)m(AP)i(encoding)d(style.)0
+665 y(F)o(or)28 b(ef)n(\002cienc)o(y)-5 b(,)29 b(incoming)e(XML)i(is)h
+(returend)d(as)i(a)h(\223pointer\224)d(into)i(the)f(DOM)h(tree)g
+(maintained)e(within)i(the)g Fl(ParsedSoap)0 765 y Fn(object.)f(If)22
+b(that)f(object)g(is)i(going)d(to)i(go)f(out)g(of)g(scope,)h(the)f
+(data)h(will)g(be)f(destro)o(yed)f(and)h(an)o(y)g(XML)h(objects)f(will)
+h(become)e(empty)0 865 y(elements.)45 b(The)26 b(class)i(instance)f(v)n
+(ariable)f Fl(copyit)p Fn(,)h(if)h(non-zero)c(indicates)j(that)g(a)g
+(deep)f(cop)o(y)g(of)h(the)g(XML)g(subtree)f(will)i(be)0
+964 y(made)c(and)f(returned)g(as)i(the)f(v)n(alue.)36
+b(Note)25 b(that)f(it)h(is)g(generally)e(more)g(ef)n(\002cient)h(to)g
+(k)o(eep)g(the)g Fl(ParsedSoap)f Fn(object)h(ali)n(v)o(e)g(until)0
+1064 y(the)c(XML)g(data)h(is)g(no)e(longerneeded.)0 1211
+y Fo(class)i Fj(XML)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 1310 y Fn(This)30 b(typecode)f(represents)h(a)h
+(portion)e(of)h(an)h(XML)f(document)f(embedded)f(in)j(a)g(SO)m(AP)g
+(message.)56 b(The)30 b(v)n(alue)g(is)i(the)208 1410
+y(element)19 b(node.)208 1543 y(The)g(follo)n(wing)g(k)o(e)o(yw)o(ord)f
+(ar)o(guments)g(may)i(be)g(used:)257 1754 y Fo(K)n(eyw)o(ord)p
+704 1784 4 100 v 310 w(Default)p 1342 1784 V 237 w(Description)p
+208 1787 3727 4 v 257 1857 a Fl(copyit)p 704 1886 4 100
+v 199 w Fn(TC.XML.cop)o(yit)p 1342 1886 V 97 w(Return)g(a)g(cop)o(y)g
+(of)g(the)g(parsed)f(data.)257 1956 y Fl(comments)p 704
+1986 V 343 w(0)p 1342 1986 V 344 w Fn(Preserv)o(e)g(comments)g(in)i
+(output.)257 2056 y Fl(inline)p 704 2086 V 443 w(0)p
+1342 2086 V 344 w Fn(The)f(XML)g(sub-tree)f(is)i(single-reference,)d
+(so)i(can)g(be)g(output)f(in-place.)257 2155 y Fl(resolver)p
+704 2285 4 200 v 268 w(None)p 1342 2285 V 269 w Fn(A)30
+b(function)d(that)j(can)f(resolv)o(e)g(an)g(absolute)f(URI)i(and)f
+(return)f(its)j(content)d(as)i(an)1394 2255 y(element)20
+b(node,)e(as)j(described)e(in)i(the)f Fl(ParsedSoap)f
+Fn(description.)257 2355 y Fl(wrapped)p 704 2484 V 393
+w(1)p 1342 2484 V 344 w Fn(If)c(zero,)h(the)f(XML)h(is)g(output)e
+(directly)-5 b(,)15 b(and)g(not)g(within)g(a)h(SO)m(AP)g(wrapper)e
+(element.)1394 2454 y(Ne)n(w)20 b(in)h(v)o(ersion)e(1.2.)0
+2596 y(When)j(serializing,)f(it)h(may)f(be)h(necessary)f(to)h(specify)f
+(which)g(namespace)f(pre\002x)o(es)h(are)h(\223acti)n(v)o(e\224)f(in)h
+(the)f(XML.)h(This)g(is)g(done)f(by)0 2696 y(using)26
+b(the)h Fl(unsuppressedPrefixes)c Fn(parameter)i(when)h(calling)g(the)h
+Fl(serialize\(\))e Fn(method.)43 b(\(This)26 b(will)h(only)f(w)o(ork)0
+2796 y(when)20 b(XML)g(is)h(the)f(top-le)n(v)o(el)f(item)h(being)f
+(serialized,)h(such)g(as)h(when)e(using)h(typecodes)f(and)g
+(document-style)f(interf)o(aces.\))50 3104 y Fo(K)n(eyw)o(ord)p
+1094 3133 4 100 v 770 w(Default)p 1457 3133 V 98 w(Description)p
+0 3137 4049 4 v 50 3207 a Fl(unsuppressedPrefixes)p 1094
+3236 4 100 v 199 w Fn([])p 1457 3236 V 203 w(An)e(array)f(of)g(strings)
+h(identifying)e(the)h(namespace)g(pre\002x)o(es)g(that)h(should)e(be)i
+(output.)0 3511 y Fg(6.10)121 b(Comple)l(xT)-14 b(ype)0
+3744 y Fn(Represents)20 b(the)g(XMLSchema)f(Comple)o(xT)-7
+b(ype)18 b(.)51 b(Ne)n(w)21 b(in)f(v)o(ersion)f(2.0.)0
+3890 y Fo(class)i Fj(ComplexType)p Fl(\()p Fk(pyclass,)c(ofwhat)q
+Fc([)p Fk(,)j(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+3990 y Fn(This)28 b(class)g(de\002nes)g(a)g(compound)d(data)j
+(structure.)47 b(If)28 b Fl(pyclass)f Fn(is)i Fl(None)p
+Fn(,)g(then)f(the)g(data)f(will)i(be)f(marshaled)f(into)g(a)208
+4090 y(Python)d(dictionary)-5 b(,)24 b(and)h(each)g(item)h(in)g(the)f
+Fl(ofwhat)g Fn(sequence)f(speci\002es)i(a)g(\(possible\))f(dictionary)e
+(entry)-5 b(.)40 b(Otherwise,)208 4189 y Fl(pyclass)28
+b Fn(must)i(be)g(a)g(Python)e(class)j(object.)52 b(The)30
+b(data)f(is)i(then)e(marshaled)f(into)i(the)f(object,)j(and)d(each)g
+(item)h(in)g(the)208 4289 y Fl(ofwhat)19 b Fn(sequence)g(speci\002es)i
+(an)f(attrib)n(ute)g(of)g(the)g(instance)g(to)g(set.)208
+4422 y(Note)g(that)g(each)g(typecode)e(in)j Fl(ofwhat)e
+Fn(must)i(ha)n(v)o(e)e(a)i(name.)208 4555 y(The)e(follo)n(wing)g(k)o(e)
+o(yw)o(ord)f(ar)o(guments)g(may)i(be)g(used:)p 0 5549
+3901 4 v 0 5649 a Fi(6.9.)52 b(XML)3448 b(27)p eop end
+%%Page: 28 34
+TeXDict begin 28 33 bop 261 70 a Fo(K)n(eyw)o(ord)p 857
+100 4 100 v 364 w(Default)p 1305 100 V 141 w(Description)p
+211 103 3687 4 v 261 173 a Fl(inorder)p 857 302 4 200
+v 347 w(False)p 1305 302 V 148 w Fn(Items)18 b(within)f(the)h
+(structure)f(must)g(appear)g(in)h(the)f(order)f(speci\002ed)i(in)g(the)
+f Fl(ofwhat)1356 272 y Fn(sequence.)261 372 y Fl(inline)p
+857 501 V 397 w(False)p 1305 501 V 148 w Fn(The)29 b(structure)f(is)i
+(single-reference,)e(so)h(ZSI)g(does)f(not)h(ha)n(v)o(e)f(to)h(use)g
+Fl(href/id)1356 472 y Fn(encodings.)261 571 y Fl(mutable)p
+857 900 4 399 v 347 w(False)p 1305 900 V 148 w Fn(If)f(an)g(object)f
+(is)i(going)d(to)i(be)g(serialized)g(multiple)f(times,)j(and)d(its)i
+(state)f(may)g(be)1356 671 y(modi\002ed)d(between)g(serializations,)i
+(then)e(this)h(k)o(e)o(yw)o(ord)e(should)h(be)h(used,)g(other)n(-)1356
+770 y(wise)20 b(a)f(single)g(instance)g(will)h(be)e(serialized,)h(with)
+g(multiple)g(references)e(to)i(it.)25 b(This)1356 870
+y(ar)o(gument)18 b(implies)i(the)h Fl(inline)e Fn(ar)o(gument.)49
+b(Ne)n(w)20 b(in)g(v)o(ersion)f(1.2.)261 970 y Fl(type)p
+857 1498 4 598 v 522 w(None)p 1305 1498 V 173 w Fn(A)37
+b(`)p Fl(\(uri,)48 b(localname\))p Fn(')34 b(tuple)h(that)h(de\002nes)g
+(the)f(type)h(of)f(the)h(structure.)1356 1069 y(If)41
+b(present,)k(and)c(if)g(the)g(input)f(data)g(has)i(a)f
+Fl(xsi:type)f Fn(attrib)n(ute,)45 b(then)c(the)1356 1169
+y(namespace-quali\002ed)22 b(v)n(alue)h(of)h(that)g(attrib)n(ute)g
+(must)g(match)g(the)g(v)n(alue)g(speci\002ed)1356 1269
+y(by)32 b(this)g(parameter)-5 b(.)59 b(By)33 b(def)o(ault,)h
+(type-checking)29 b(is)j(not)g(done)f(for)g(structures;)1356
+1368 y(matching)22 b(child)h(element)g(names)f(is)j(usually)d(suf)n
+(\002cient)h(and)g(senders)f(rarely)h(pro-)1356 1468
+y(vide)d(type)g(information.)261 1567 y Fl(mixed)p 857
+1597 4 100 v 447 w(False)p 1305 1597 V 148 w Fn(using)g(a)h(mix)o(ed)e
+(content)g(model,)g(allo)n(w)h(te)o(xt)g(and)g(element)f(content.)261
+1667 y Fl(mixed_aname)p 857 1697 V 97 w('_text')p 1305
+1697 V 98 w Fn(if)i(mix)o(ed)e(is)i(T)m(rue,)e(te)o(xt)h(content)g(is)h
+(set)g(in)f(this)h(attrib)n(ute)f(\(k)o(e)o(y\).)208
+1788 y(If)j(the)h Fl(typed)f Fn(k)o(e)o(yw)o(ord)e(is)k(used,)f(then)f
+(its)h(v)n(alue)f(is)i(assigned)e(to)g(all)h(typecodes)f(in)g(the)h
+Fl(ofwhat)f Fn(parameter)-5 b(.)34 b(If)23 b(an)o(y)g(of)208
+1888 y(the)c(typecodes)f(in)i Fl(ofwhat)f Fn(are)h(repeatable,)e(then)h
+(the)h Fl(inorder)f Fn(k)o(e)o(yw)o(ord)f(should)h(not)g(be)g(used)h
+(and)f(the)h Fl(hasextras)208 1987 y Fn(parameter)e Fk(must)j
+Fn(be)f(used.)208 2113 y(F)o(or)f(e)o(xample,)g(the)h(follo)n(wing)f(C)
+i(structure:)444 2330 y Fe(struct)44 b(foo)g({)623 2422
+y(int)g(i;)623 2513 y(char)803 2526 y(*)892 2513 y(text;)444
+2604 y(};)208 2761 y Fn(could)19 b(be)h(declared)f(as)i(follo)n(ws:)444
+2978 y Fe(class)44 b(foo:)623 3069 y(def)g(__init__\(self,)f(name\):)
+802 3160 y(self.name)h(=)g(name)623 3252 y(def)g(__str__\(self\):)802
+3343 y(return)g(str\(\(self.name,)f(self.i,)g(self.text\)\))444
+3526 y(foo.typecode)g(=)h(TC.Struct\(foo,)623 3617 y(\()h
+(TC.Integer\('i'\),)d(TC.String\('text'\))g(\),)623 3708
+y('foo'\))0 4068 y Fg(6.11)121 b(Str)r(uct)0 4301 y Fn(SO)m(AP)24
+b(Struct)e(is)i(a)f(comple)o(x)e(type)h(for)g(accessors)h(identi\002ed)
+f(by)g(name.)32 b(No)23 b(element)f(may)h(ha)n(v)o(e)f(the)h(same)f
+(name)h(as)g(an)o(y)f(other)m(,)0 4400 y(nor)f(may)g(an)o(y)f(element)h
+(ha)n(v)o(e)g(a)h(maxOccurs)e(\277)i(1.)29 b(SO)m(AP)23
+b(Structs)e(are)h(either)f(Python)f(dictionaries)h(or)g(instances)g(of)
+g(application-)0 4500 y(speci\002ed)f(classes.)0 4821
+y Fg(6.12)121 b(Arr)o(a)l(ys)0 5054 y Fn(SO)m(AP)22 b(arrays)f(are)h
+(Python)e(lists;)k(multi-dimensional)19 b(arrays)i(are)h(lists)h(of)e
+(lists)i(and)e(are)h(indistinguishable)d(from)i(a)h(SO)m(AP)g(array)0
+5154 y(of)17 b(arrays.)24 b(Arrays)17 b(may)f(be)i Fk(spar)o(se)p
+Fn(,)g(in)g(which)f(case)h(each)f(element)g(in)g(the)h(array)e(is)j(a)e
+(tuple)h(of)f(`)p Fl(\(subscript,)47 b(data\))p Fn(')17
+b(pairs.)0 5253 y(If)j(an)g(array)f(is)j(not)d(sparse,)h(a)h
+(speci\002ed)f Fk(\002ll)g Fn(element)g(will)h(be)f(used)g(for)g(the)g
+(missing)g(v)n(alues.)0 5400 y Fo(Curr)o(ently)f(only)h
+(singly-dimensioned)h(arrays)e(ar)o(e)h(supported.)p
+0 5549 3901 4 v 0 5649 a Fi(28)1751 b(Chapter)24 b(6.)51
+b(The)24 b Fl(TypeCode)d Fi(classes)h(\227)h(data)h(con)n(v)n(ersions)p
+eop end
+%%Page: 29 35
+TeXDict begin 29 34 bop 0 83 a Fo(class)21 b Fj(Array)p
+Fl(\()p Fk(atype)o(,)d(ofwhat)q Fc([)p Fk(,)i(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 183 y Fn(The)20 b Fl(atype)h Fn(parameter)f(is)i(a)f
+Fl(\(URI,NCName\))f Fn(tuple)h(representing)e(the)i(SO)m(AP)h(array)e
+(type.)28 b(The)20 b Fl(ofwhat)h Fn(parameter)208 282
+y(is)g(a)f(typecode)f(describing)f(the)j(array)e(elements.)0
+429 y(The)h(follo)n(wing)e(k)o(e)o(yw)o(ord)h(ar)o(guments)f(may)i(be)g
+(used:)225 655 y Fo(K)n(eyw)o(ord)p 771 685 4 100 v 271
+w(Default)p 1133 685 V 99 w(Description)p 175 689 3551
+4 v 225 758 a Fl(childnames)p 771 788 4 100 v 129 w(None)p
+1133 788 V 131 w Fn(Def)o(ault)g(name)f(to)i(use)f(for)g(the)g(child)g
+(elements.)225 858 y Fl(dimensions)p 771 888 V 204 w(1)p
+1133 888 V 206 w Fn(The)g(number)e(of)i(dimensions)f(in)h(the)g(array)
+-5 b(.)225 958 y Fl(fill)p 771 988 V 429 w(None)p 1133
+988 V 131 w Fn(The)20 b(v)n(alue)f(to)h(use)h(when)e(an)i(array)e
+(element)g(is)j(omitted.)225 1057 y Fl(mutable)p 771
+1286 4 299 v 254 w(False)p 1133 1286 V 106 w Fn(If)27
+b(an)h(object)f(is)i(going)e(to)h(be)f(serialized)h(multiple)f(times,)j
+(and)d(its)i(state)g(may)e(be)1185 1157 y(modi\002ed)d(between)h
+(serializations,)i(then)e(this)i(k)o(e)o(yw)o(ord)d(should)g(be)i
+(used,)h(other)n(-)1185 1257 y(wise)21 b(a)f(single)g(instance)g(will)h
+(be)f(serialized,)g(with)g(multiple)g(references)f(to)h(it.)225
+1356 y Fl(nooffset)p 771 1486 4 200 v 304 w(0)p 1133
+1486 V 206 w Fn(Do)j(not)g(use)h(the)f(SO)m(AP)h Fl(offset)f
+Fn(attrib)n(ute)g(so)h(skip)f(leading)f(elements)h(with)h(the)1185
+1456 y(same)c(v)n(alue)g(as)h Fl(fill)p Fn(.)225 1555
+y Fl(sparse)p 771 1585 4 100 v 304 w(False)p 1133 1585
+V 106 w Fn(The)f(array)f(is)i(sparse.)225 1655 y Fl(size)p
+771 1685 V 429 w(None)p 1133 1685 V 131 w Fn(An)f(inte)o(ger)f(or)h
+(list)h(of)f(inte)o(gers)g(that)g(speci\002es)g(the)h(maximum)d(array)h
+(dimensions.)225 1755 y Fl(undeclared)p 771 1785 V 104
+w(False)p 1133 1785 V 106 w Fn(The)h(SO)m(AP)h(`)p Fl(arrayType)p
+Fn(')d(attrib)n(ute)i(need)f(not)h(appear)-5 b(.)0 2057
+y Fg(6.13)121 b(Apache)35 b(Datatype)0 2290 y Fn(The)30
+b(Apache)g(SO)m(AP)h(project,)h(urlhttp://xml.apache.or)o(g/soap)o
+(/inde)n(x.)o(html,)27 b(has)k(de\002ned)e(a)i(popular)e(SO)m(AP)j
+(datatype)d(in)i(the)0 2390 y Fl(http://xml.apache.org/xml-soap)15
+b Fn(namespace,)k(a)h Fl(Map)p Fn(.)0 2536 y(The)f Fl(Map)h
+Fn(type)f(is)i(encoded)c(as)k(a)f(list)g(of)g Fl(item)f
+Fn(elements.)24 b(Each)c Fl(item)f Fn(has)h(a)g Fl(key)f
+Fn(and)g Fl(value)h Fn(child)f(element;)g(these)h(children)0
+2636 y(must)g(ha)n(v)o(e)g(SO)m(AP)h(type)f(information.)i(An)e(Apache)
+g(Map)g(is)h(either)e(a)i(Python)e(dictionary)g(or)g(a)i(list)g(of)f
+(tw)o(o-element)f(tuples.)0 2783 y Fo(class)i Fj(Apache.Map)p
+Fl(\()p Fk(name)p Fc([)p Fk(,)c(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 2883 y Fn(An)20 b(Apache)f(map.)25
+b(Note)20 b(that)g(the)g(class)h(name)f(is)h(dotted.)0
+3029 y(The)f(follo)n(wing)e(k)o(e)o(yw)o(ord)h(ar)o(guments)f(may)i(be)
+g(used:)311 3256 y Fo(K)n(eyw)o(ord)p 684 3286 V 99 w(Default)p
+1047 3286 V 98 w(Description)p 262 3289 3377 4 v 311
+3359 a Fl(aslist)p 684 3389 4 100 v 231 w(0)p 1047 3389
+V 206 w Fn(Use)h(a)f(list)i(of)e(tuples)g(rather)f(than)h(a)g
+(dictionary)-5 b(.)p 0 5549 3901 4 v 0 5649 a Fi(6.13.)52
+b(Apache)24 b(Datatype)2935 b(29)p eop end
+%%Page: 30 36
+TeXDict begin 30 35 bop 0 5549 3901 4 v 0 5649 a Fi(30)p
+eop end
+%%Page: 31 37
+TeXDict begin 31 36 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3495
+427 y Fh(SEVEN)p 0 515 V 270 978 a Ft(The)57 b Fd(SoapWriter)g
+Ft(module)g(\227)g(ser)s(ializing)3498 1227 y(data)0
+1706 y Fn(The)20 b(SoapWriter)h(class)g(is)h(used)f(to)g(output)e(SO)m
+(AP)j(messages.)27 b(Note)20 b(that)h(its)h(output)e(is)h(encoded)e(as)
+j(UTF-8;)f(when)f(transporting)0 1805 y(SO)m(AP)h(o)o(v)o(er)e(HTTP)h
+(it)h(is)g(therefore)e(important)f(to)j(set)g(the)f Fl(charset)f
+Fn(attrib)n(ute)h(of)g(the)g Fl(Content-Type)f Fn(header)-5
+b(.)0 1952 y(The)20 b Fl(SoapWriter)f Fn(class)i(reserv)o(es)e(some)h
+(namespace)f(pre\002x)o(es:)680 2155 y Fo(Pr)o(e\002x)p
+1126 2185 4 100 v 286 w(URI)p 630 2188 2641 4 v 680 2258
+a Fl(SOAP-ENV)p 1126 2288 4 100 v 98 w
+(http://schemas.xmlsoap.org/soap/env)o(elope)o(/)680
+2358 y(SOAP-ENC)p 1126 2388 V 98 w(http://schemas.xmlsoap.org/soap/enc)
+o(oding)o(/)680 2457 y(ZSI)p 1126 2487 V 348 w
+(http://www.zolera.com/schemas/ZSI/)680 2557 y(xsd)p
+1126 2587 V 348 w(http://www.w3.org/2001/XMLSchema)680
+2657 y(xsi)p 1126 2687 V 348 w(http://www.w3.org/2001/XMLSchema-in)o
+(stanc)o(e)0 2812 y Fo(class)i Fj(SoapWriter)p Fl(\()p
+Fk(optional**k)o(e)n(ywor)m(d)o(s)p Fl(\))208 2911 y
+Fn(The)e(follo)n(wing)g(k)o(e)o(yw)o(ord)f(ar)o(guments)g(may)i(be)g
+(used:)257 3120 y Fo(K)n(eyw)o(ord)p 953 3150 V 589 w(Default)p
+1650 3150 V 266 w(Description)p 208 3154 4035 4 v 257
+3223 a Fl(encodingStyle)p 953 3353 4 200 v 307 w Fn(None)p
+1650 3353 V 307 w(If)39 b(not)g Fl(None)p Fn(,)44 b(then)39
+b(use)h(the)g(speci\002ed)f(v)n(alue)f(as)j(the)e(v)n(alue)g(for)g(the)
+g(SO)m(AP)1702 3323 y Fl(encodingStyle)18 b Fn(attrib)n(ute.)51
+b(Ne)n(w)20 b(in)g(v)o(ersion)f(1.2.)257 3423 y Fl(envelope)p
+953 3452 4 100 v 547 w(True)p 1650 3452 V 298 w Fn(Create)h(a)h(SO)m
+(AP)g(En)m(v)o(elope)39 b(Ne)n(w)20 b(in)g(v)o(ersion)f(1.2.)257
+3522 y Fl(nsdict)p 953 3552 V 697 w({})p 1650 3552 V
+348 w Fn(Dictionary)g(of)h(namespaces)f(to)h(declare)g(in)g(the)g(SO)m
+(AP)h Fl(Envelope)257 3622 y(header)p 953 3652 V 647
+w(True)p 1650 3652 V 298 w Fn(create)f(a)g(SO)m(AP)i
+Fl(Header)d Fn(element)257 3721 y Fl(outputclass)p 953
+3751 V 198 w(ElementProxy)p 1650 3751 V 97 w Fn(wrapper)g(around)f(DOM)
+i(or)g(other)g(XML)g(library)-5 b(.)0 3863 y(Creating)24
+b(a)h Fl(SoapWriter)e Fn(object)h(with)g Fl(envelope)f
+Fn(set)j(to)e Fl(False)g Fn(results)h(in)f(an)h(object)e(that)i(can)f
+(be)g(used)g(for)g(serializing)0 3963 y(objects)c(into)g(a)h(string.)0
+4110 y Fj(serialize)p Fl(\()p Fk(pyobj)p Fc([)p Fk(,)c(typecode=None)p
+Fc([)p Fk(,)h(r)l(oot=None)p Fc([)p Fk(,)h(header)p 2056
+4110 25 4 v 28 w(pyobjs=None)p Fc([)p Fk(,)g(**k)o(e)n(ywor)m(ds)12
+b Fc(])g(])g(])g(])p Fl(\))208 4210 y Fn(This)19 b(method)f(serializes)
+h(the)g Fl(pyobj)g Fn(Python)f(object)h(as)h(directed)e(by)h(the)g
+Fl(typecode)f Fn(typecode)f(object.)24 b(If)19 b Fl(typecode)208
+4309 y Fn(is)i(omitted,)f(then)g Fl(pyobj)g Fn(should)g(be)g(a)i
+(Python)d(object)h(instance)g(of)h(a)g(class)g(that)g(has)g(a)g
+Fl(typecode)f Fn(attrib)n(ute.)26 b(It)20 b(returns)208
+4409 y Fl(self)p Fn(,)d(so)g(that)g(serializations)g(can)f(be)h
+(chained)f(together)m(,)g(or)g(so)i(that)f(the)g Fl(close\(\))f
+Fn(method)f(can)i(be)g(in)m(v)n(ok)o(ed.)22 b(The)17
+b Fl(root)208 4509 y Fn(parameter)k(may)i(be)g(used)g(to)h(e)o
+(xplicitly)e(indicate)h(the)g(root)f(\(main)h(element\))f(of)h(a)h(SO)m
+(AP)g(encoding,)e(or)h(indicate)f(that)i(the)208 4608
+y(item)18 b(is)i(not)e(the)h(root.)24 b(If)18 b(speci\002ed,)h(it)g
+(should)f(ha)n(v)o(e)g(the)h(numeric)e(v)n(alue)h(of)h(zero)f(or)g
+(one.)24 b(An)o(y)18 b(other)g(k)o(e)o(yw)o(ord)f(parameters)208
+4708 y(are)j(passed)g(to)g(the)g(typecode')-5 b(s)19
+b Fl(serialize)g Fn(method.)0 4855 y Fj(close)p Fl(\(\))208
+4954 y Fn(In)m(v)n(ok)o(es)h(all)i(the)g(callbacks,)f(if)h(an)o(y)-5
+b(.)28 b(The)21 b Fl(close)g Fn(operations)f(can)i(only)e(happen)g
+(once,)h(if)h(in)m(v)n(ok)o(ed)e(a)i(second)f(time)g(it)i(will)208
+5054 y(just)d(return.)k(This)c(method)f(will)i(be)f(in)m(v)n(ok)o(ed)f
+(automatically)f(if)j(the)f(object)g(is)h(deleted.)0
+5201 y Fj(__str__)p Fl(\(\))208 5300 y Fn(In)m(v)n(ok)o(es)39
+b(the)i Fl(close)f Fn(method,)45 b(and)40 b(returns)g(a)h(string)g
+(representation)e(of)h(the)h(serialized)g(object.)86
+b(Assumes)41 b(that)208 5400 y Fl(serialize)19 b Fn(has)h(been)g(in)m
+(v)n(ok)o(ed.)p 0 5549 3901 4 v 3808 5649 a Fi(31)p eop
+end
+%%Page: 32 38
+TeXDict begin 32 37 bop 0 83 a Fn(The)20 b(follo)n(wing)e(methods)i
+(are)g(primarily)e(useful)i(for)g(those)g(writing)f(ne)n(w)h
+(typecodes.)0 230 y Fj(AddCallback)p Fl(\()p Fk(func,)d(ar)m(g)p
+Fl(\))208 330 y Fn(Used)29 b(by)g(typecodes)f(when)h(serializing,)i
+(allo)n(ws)f(them)f(to)h(add)f(output)f(after)h(the)h(SO)m(AP)g
+Fl(Body)g Fn(is)g(written)f(b)n(ut)h(before)208 429 y(the)24
+b(SO)m(AP)h Fl(Envelope)f Fn(is)h(closed.)37 b(The)24
+b(function)f Fl(func\(\))h Fn(will)h(be)f(called)h(with)f(the)h
+Fl(SoapWriter)e Fn(object)h(and)g(the)208 529 y(speci\002ed)19
+b Fl(arg)i Fn(ar)o(gument,)c(which)j(may)g(be)g(a)g(tuple.)0
+676 y Fj(Forget)p Fl(\()p Fk(obj)p Fl(\))208 775 y Fn(F)o(or)o(get)e
+(that)j Fl(obj)f Fn(has)g(been)g(seen)g(before.)k(This)c(is)h(useful)f
+(when)f(repeatedly)g(serializing)g(a)i(mutable)e(object.)0
+922 y Fj(Known)p Fl(\()p Fk(obj)p Fl(\))208 1022 y Fn(If)h
+Fl(obj)g Fn(has)g(been)g(seen)g(before)f(\(based)g(on)h(its)h(Python)e
+Fl(id)p Fn(\),)h(return)f Fl(1)p Fn(.)25 b(Otherwise,)20
+b(remember)e Fl(obj)j Fn(and)e(return)g Fl(0)p Fn(.)0
+1169 y Fj(ReservedNS)p Fl(\()p Fk(pr)m(e\002x,)e(uri)p
+Fl(\))208 1268 y Fn(Returns)j(true)f(if)i(the)f(speci\002ed)g
+(namespace)f Fl(prefix)h Fn(and)f Fl(uri)h Fn(collide)g(with)h(those)f
+(used)g(by)f(the)h(implementation.)0 1415 y Fj(writeNSDict)p
+Fl(\()p Fk(nsdict)q Fl(\))208 1515 y Fn(Outputs)g Fl(nsdict)h
+Fn(as)h(a)f(namespace)f(dictionary)-5 b(.)26 b(It)21
+b(is)h(assumed)f(that)g(an)g(XML)g(start-element)g(is)h(pending)d(on)i
+(the)g(output)208 1614 y(stream.)p 0 5549 3901 4 v 0
+5649 a Fi(32)1725 b(Chapter)23 b(7.)52 b(The)23 b Fl(SoapWriter)f
+Fi(module)i(\227)f(ser)q(ializing)h(data)p eop end
+%%Page: 33 39
+TeXDict begin 33 38 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3535
+427 y Fh(EIGHT)p 0 515 V 391 978 a Ft(The)57 b Fd(Fault)g
+Ft(module)g(\227)g(repor)8 b(ting)57 b(errors)0 1468
+y Fn(SO)m(AP)28 b(de\002nes)f(a)g Fk(fault)g Fn(message)g(as)h(the)f(w)
+o(ay)g(for)f(a)i(recipient)e(to)h(indicate)g(it)g(w)o(as)h(unable)e(to)
+h(process)g(a)g(message.)46 b(The)27 b Fl(ZSI)0 1568
+y(Fault)20 b Fn(class)h(encapsulates)e(this.)0 1715 y
+Fo(class)i Fj(Fault)p Fl(\()p Fk(code)o(,)d(string)p
+Fc([)p Fk(,)i(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+1814 y Fn(The)26 b Fk(code)g Fn(parameter)f(is)j(a)f(te)o(xt)f(string)h
+(identifying)d(the)j(SO)m(AP)h(f)o(ault)e(code,)i(a)f
+(namespace-quali\002ed)c(name.)44 b(The)27 b(class)208
+1914 y(attrib)n(ute)18 b Fl(Fault.Client)g Fn(can)h(be)g(used)g(to)h
+(indicate)f(a)g(problem)f(with)h(an)h(incoming)d(message,)i
+Fl(Fault.Server)f Fn(can)208 2013 y(be)24 b(used)g(to)h(indicate)f(a)g
+(problem)f(occurred)g(while)h(processing)f(the)i(request,)f(or)g
+Fl(Fault.MU)g Fn(can)g(be)h(used)f(to)g(indicate)g(a)208
+2113 y(problem)g(with)j(the)g(SO)m(AP)g Fl(mustUnderstand)d
+Fn(attrib)n(ute.)44 b(The)26 b Fk(string)h Fn(parameter)e(is)j(a)f
+(human-readable)22 b(te)o(xt)27 b(string)208 2213 y(describing)18
+b(the)i(f)o(ault.)208 2346 y(The)f(follo)n(wing)g(k)o(e)o(yw)o(ord)f
+(ar)o(guments)g(may)i(be)g(used:)369 2475 y Fo(K)n(eyw)o(ord)p
+834 2505 4 100 v 190 w(Default)p 1196 2505 V 99 w(Description)p
+319 2508 3470 4 v 369 2578 a Fk(actor)p 834 2707 4 200
+v 372 w Fl(None)p 1196 2707 V 131 w Fn(A)25 b(string)g(identifying)e
+(the)i Fl(actor)g Fn(attrib)n(ute)f(that)h(caused)g(the)g(problem)e
+(\(usually)1248 2677 y(because)c(it)i(is)g(unkno)n(wn\).)369
+2777 y Fk(detail)p 834 3006 4 299 v 358 w Fl(None)p 1196
+3006 V 131 w Fn(A)k(sequence)f(of)h(elements)f(to)h(output)f(in)h(the)g
+Fl(detail)f Fn(element;)j(it)f(may)e(also)h(be)1248 2877
+y(a)d(te)o(xt)h(string,)f(in)g(which)g(case)g(it)h(is)g(output)e
+(as-is,)j(and)d(should)g(therefore)g(be)h(XML)1248 2976
+y(te)o(xt.)369 3076 y Fk(header)m(detail)p 834 3205 4
+200 v 129 w Fl(None)p 1196 3205 V 131 w Fn(Data,)i(treated)e(the)h
+(same)g(as)h(the)f Fl(detail)f Fn(k)o(e)o(yw)o(ord,)f(to)j(be)e(output)
+g(in)h(the)g(SO)m(AP)1248 3175 y(header)-5 b(.)24 b(See)d(the)f(follo)n
+(wing)e(paragraph.)208 3304 y(If)j(the)h(f)o(ault)f(occurred)f(in)h
+(the)h(SO)m(AP)g Fl(Header)p Fn(,)f(the)h(speci\002cation)f(requires)f
+(that)i(the)g(detail)f(be)h(sent)g(back)e(as)j(an)e(element)208
+3403 y(within)26 b(the)h(SO)m(AP)g Fl(Header)f Fn(element.)44
+b(Unfortunately)-5 b(,)25 b(the)h(SO)m(AP)i(speci\002cation)e(does)g
+(not)g(describe)g(ho)n(w)g(to)h(encode)208 3503 y(this;)20
+b Fl(ZSI)h Fn(de\002nes)f(and)f(uses)i(a)f Fl(ZSI:detail)f
+Fn(element,)h(which)f(is)j(analogous)c(to)i(the)g(SO)m(AP)i
+Fl(detail)d Fn(element.)0 3650 y(The)h(follo)n(wing)e(attrib)n(utes)j
+(are)f(read-only:)0 3797 y Fj(actor)208 3896 y Fn(A)g(te)o(xt)g(string)
+g(holding)f(the)h(v)n(alue)f(of)h(the)g(SO)m(AP)i Fl(faultactor)c
+Fn(element.)0 4043 y Fj(code)208 4143 y Fn(A)i(te)o(xt)g(string)g
+(holding)f(the)h(v)n(alue)f(of)h(the)g(SO)m(AP)i Fl(faultcode)d
+Fn(element.)0 4290 y Fj(detail)208 4389 y Fn(A)i(te)o(xt)g(string)g(or)
+g(sequence)f(of)h(elements)g(containing)f(holding)f(the)j(v)n(alue)e
+(of)h(the)g(SO)m(AP)i Fl(detail)d Fn(element,)h(when)g(a)n(v)n(ail-)208
+4489 y(able.)0 4636 y Fj(headerdetail)208 4735 y Fn(A)29
+b(te)o(xt)g(string)g(or)g(sequence)g(of)g(elements)g(containing)e
+(holding)h(the)h(v)n(alue)f(of)h(the)h Fl(ZSI)f Fn(header)f(detail)h
+(element,)i(when)208 4835 y(a)n(v)n(ailable.)0 4982 y
+Fj(string)208 5081 y Fn(A)20 b(te)o(xt)g(string)g(holding)f(the)h(v)n
+(alue)f(of)h(the)g(SO)m(AP)i Fl(faultstring)c Fn(element.)0
+5228 y Fj(AsSOAP)p Fl(\()p Fc([)p Fk(,)h(**kw)12 b Fc(])p
+Fl(\))208 5328 y Fn(This)29 b(method)f(serializes)i(the)f
+Fl(Fault)g Fn(object)g(into)g(a)h(SO)m(AP)h(message.)52
+b(The)29 b(message)g(is)i(returned)c(as)j(a)g(string.)52
+b(An)o(y)p 0 5549 3901 4 v 3808 5649 a Fi(33)p eop end
+%%Page: 34 40
+TeXDict begin 34 39 bop 208 83 a Fn(k)o(e)o(yw)o(ord)26
+b(ar)o(guments)h(are)i(passed)g(to)f(the)h Fl(SoapWriter)f
+Fn(constructor)-5 b(.)100 b(Ne)n(w)29 b(in)g(v)o(ersion)f(1.1;)k(the)d
+(old)f Fl(AsSoap\(\))208 183 y Fn(method)18 b(is)k(still)f(a)n(v)n
+(ailable.)0 330 y(If)f(other)f(data)h(is)h(going)e(to)h(be)g(sent)g
+(with)h(the)f(f)o(ault,)f(the)i(follo)n(wing)d(tw)o(o)i(methods)f(can)h
+(be)g(used.)25 b(Because)20 b(some)g(data)g(might)f(need)0
+429 y(to)h(be)g(output)f(in)i(the)f(SO)m(AP)h Fl(Header)p
+Fn(,)e(serializing)h(a)h(f)o(ault)f(is)h(a)g(tw)o(o-step)e(process.)0
+576 y Fj(DataForSOAPHeader)p Fl(\(\))208 676 y Fn(This)c(method)f
+(returns)g(a)i(te)o(xt)f(string)g(that)g(can)g(be)g(included)f(as)i
+(the)f Fl(header)g Fn(parameter)e(for)i(constructing)e(a)j
+Fl(SoapWriter)208 775 y Fn(object.)0 922 y Fj(serialize)p
+Fl(\()p Fk(sw)p Fl(\))208 1022 y Fn(This)k(method)f(outputs)g(the)h(f)o
+(ault)g(object)g(onto)f(the)i Fk(sw)g Fn(object,)e(which)h(is)h(a)g
+Fl(SoapWriter)e Fn(instance.)0 1169 y(Some)h(con)m(v)o(enience)d
+(functions)i(are)h(a)n(v)n(ailable)g(to)g(create)g(a)h
+Fl(Fault)e Fn(from)g(common)g(conditions.)0 1316 y Fj(FaultFromActor)p
+Fl(\()p Fk(uri)p Fc([)p Fk(,)e(actor)d Fc(])p Fl(\))208
+1415 y Fn(This)19 b(function)e(could)g(be)i(used)g(when)f(an)g
+(application)g(recei)n(v)o(es)g(a)h(message)g(that)f(has)h(a)h(SO)m(AP)
+f Fl(Header)f Fn(element)h(directed)208 1515 y(to)h(an)h(actor)f(that)h
+(cannot)f(be)g(processed.)26 b(The)20 b Fk(uri)i Fn(parameter)d
+(identi\002es)h(the)h(actor)-5 b(.)27 b(The)20 b Fk(actor)j
+Fn(parameter)c(can)h(be)h(used)f(to)208 1614 y(specify)f(a)i(URI)f
+(that)h(identi\002es)f(the)g(application,)f(if)h(it)h(is)g(not)f(the)g
+(ultimate)g(recipient)f(of)h(the)g(SO)m(AP)h(message.)0
+1761 y Fj(FaultFromException)p Fl(\()p Fk(e)n(x,)c(inheader)r
+Fc([)p Fk(,)h(tb)p Fc([)p Fk(,)i(actor)14 b Fc(])e(])p
+Fl(\))208 1861 y Fn(This)27 b(function)f(creates)i(a)g
+Fl(Fault)f Fn(from)g(a)h(general)e(Python)h(e)o(xception.)45
+b(A)28 b(SO)m(AP)h(\223serv)o(er\224)d(f)o(ault)i(is)g(created.)47
+b(The)27 b Fk(e)n(x)208 1961 y Fn(parameter)k(should)g(be)i(the)f
+(Python)g(e)o(xception.)60 b(The)32 b Fk(inheader)h Fn(parameter)e
+(should)g(be)i(true)f(if)h(the)g(error)e(w)o(as)i(found)208
+2060 y(on)28 b(a)h(SO)m(AP)g Fl(Header)f Fn(element.)50
+b(The)28 b(optional)f Fk(tb)i Fn(parameter)e(may)h(be)g(a)h(Python)f
+(traceback)f(object,)j(as)f(returned)e(by)208 2160 y(`)p
+Fl(sys.exc_info\(\)[2])p Fn('.)d(The)c Fk(actor)j Fn(parameter)d(can)g
+(be)h(used)g(to)g(specify)g(a)g(URI)h(that)f(identi\002es)g(the)g
+(application,)e(if)208 2259 y(it)h(is)i(not)d(the)i(ultimate)f
+(recipient)f(of)h(the)g(SO)m(AP)h(message.)0 2406 y Fj
+(FaultFromFaultMessage)p Fl(\()p Fk(ps)p Fl(\))208 2506
+y Fn(This)29 b(function)g(creates)g(a)i Fl(Fault)e Fn(from)g(a)h
+Fl(ParsedSoap)e Fn(object)h(passed)h(in)g(as)h Fk(ps)p
+Fn(.)54 b(It)30 b(should)e(only)h(be)h(used)f(if)i(the)208
+2606 y Fl(IsAFault\(\))18 b Fn(method)h(returned)g(true.)0
+2752 y Fj(FaultFromNotUnderstood)p Fl(\()p Fk(uri,)d(localname)o(,)p
+Fc([)p Fk(,)i(actor)c Fc(])p Fl(\))208 2852 y Fn(This)26
+b(function)e(could)h(be)g(used)h(when)f(an)h(application)e(recei)n(v)o
+(es)h(a)h(message)g(with)g(the)g(SO)m(AP)h Fl(mustUnderstand)c
+Fn(at-)208 2952 y(trib)n(ute)17 b(that)h(it)g(does)g(not)f(understand.)
+22 b(The)c Fk(uri)g Fn(and)f Fk(localname)f Fn(parameters)h(should)g
+(identify)f(the)i(unkno)n(wn)d(element.)24 b(The)208
+3051 y Fk(actor)f Fn(parameter)d(can)h(be)h(used)f(to)g(specify)g(a)h
+(URI)g(that)g(identi\002es)f(the)h(application,)e(if)i(it)g(is)g(not)g
+(the)f(ultimate)g(recipient)g(of)208 3151 y(the)f(SO)m(AP)h(message.)0
+3298 y Fj(FaultFromZSIException)p Fl(\()p Fk(e)n(x)p
+Fc([)p Fk(,)c(actor)d Fc(])p Fl(\))208 3397 y Fn(This)k(function)e
+(creates)i(a)h Fl(Fault)f Fn(object)f(from)g(a)i Fl(ZSI)f
+Fn(e)o(xception,)e Fl(ParseException)h Fn(or)g Fl(EvaluateException)p
+Fn(,)208 3497 y(passed)29 b(in)h(as)h Fk(e)n(x)p Fn(.)55
+b(A)30 b(SO)m(AP)h(\223client\224)e(f)o(ault)h(is)h(created.)53
+b(The)30 b Fk(actor)h Fn(parameter)e(can)g(be)h(used)g(to)g(specify)f
+(a)h(URI)g(that)208 3597 y(identi\002es)20 b(the)g(application,)e(if)j
+(it)g(is)g(not)f(the)g(ultimate)g(recipient)f(of)h(the)g(SO)m(AP)h
+(message.)p 0 5549 3901 4 v 0 5649 a Fi(34)1958 b(Chapter)24
+b(8.)52 b(The)23 b Fl(Fault)f Fi(module)i(\227)f(repor)s(ting)h(errors)
+p eop end
+%%Page: 35 41
+TeXDict begin 35 40 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3614
+427 y Fh(NINE)p 0 515 V 598 978 a Ft(The)57 b Fd(resolvers)g
+Ft(module)g(\227)g(f)-6 b(etching)2798 1227 y(remote)57
+b(data)0 1706 y Fn(The)33 b Fl(resolvers)f Fn(module)g(pro)o(vides)g
+(some)h(functions)f(and)h(classes)h(that)g(can)f(be)g(used)g(as)h(the)g
+Fl(resolver)e Fn(attrib)n(ute)h(for)0 1805 y Fl(TC.String)c
+Fn(or)g Fl(TC.XML)g Fn(typecodes.)53 b(The)o(y)29 b(process)g(an)h
+(absolute)f(URL,)h(as)g(described)f(abo)o(v)o(e,)h(and)f(return)g(the)h
+(content.)0 1905 y(Because)d(the)h Fl(resolvers)e Fn(module)f(can)i
+(import)f(a)i(number)d(of)i(other)g(lar)o(ge)f(modules,)h(it)h(must)f
+(be)h(imported)d(directly)-5 b(,)28 b(as)f(in)0 2005
+y(`)p Fl(from)49 b(ZSI)g(import)g(resolvers)p Fn('.)0
+2151 y(These)22 b(\002rst)h(tw)o(o)g(functions)e(pass)i(the)f(URI)h
+(directly)e(to)i(the)f Fl(urlopen)g Fn(function)e(in)j(the)f
+Fl(urllib)g Fn(module.)30 b(Therefore,)20 b(if)j(used)0
+2251 y(directly)17 b(as)i(resolv)o(ers,)f(a)h(client)f(could)f(direct)h
+(the)g(SO)m(AP)h(application)e(to)i(fetch)e(an)o(y)h(\002le)h(on)f(the)
+g(netw)o(ork)f(or)h(local)g(disk.)24 b(Needless)0 2351
+y(to)c(say)-5 b(,)20 b(this)h(could)e(pose)h(a)h(security)e(risks.)0
+2498 y Fj(Opaque)p Fl(\()p Fk(uri,)g(tc,)i(ps)p Fc([)p
+Fk(,)f(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208 2597
+y Fn(This)20 b(function)e(returns)h(the)g(data)h(contained)e(at)j(the)f
+(speci\002ed)f Fl(uri)h Fn(as)h(a)f(Python)f(string.)24
+b(Base-64)19 b(decoding)f(will)j(be)f(done)208 2697 y(if)g(necessary)-5
+b(.)24 b(The)c Fl(tc)g Fn(and)g Fl(ps)g Fn(parameters)f(are)h(ignored;)
+f(the)h Fl(keywords)f Fn(are)h(passed)g(to)h(the)f Fl(urlopen)f
+Fn(method.)0 2844 y Fj(XML)p Fl(\()p Fk(uri,)h(tc,)g(ps)p
+Fc([)p Fk(,)g(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+2943 y Fn(This)21 b(function)f(returns)h(a)g(list)i(of)e(the)h(child)f
+(element)g(nodes)f(of)h(the)h(XML)f(document)f(at)i(the)f(speci\002ed)h
+Fl(uri)p Fn(.)28 b(The)21 b Fl(tc)h Fn(and)208 3043 y
+Fl(ps)e Fn(parameters)f(are)h(ignored;)e(the)j Fl(keywords)e
+Fn(are)h(passed)g(to)h(the)f Fl(urlopen)f Fn(method.)0
+3190 y(The)h Fl(NetworkResolver)e Fn(class)j(pro)o(vides)d(a)j
+(simple-minded)d(w)o(ay)i(to)g(limit)h(the)f(URI')-5
+b(s)21 b(that)g(will)g(be)f(resolv)o(ed.)0 3337 y Fo(class)h
+Fj(NetworkResolver)p Fl(\()p Fc([)p Fk(pr)m(e\002xes=None)12
+b Fc(])p Fl(\))208 3436 y Fn(The)23 b Fl(prefixes)f Fn(parameter)g(is)j
+(a)f(list)h(of)e(strings)g(de\002ning)g(the)g(allo)n(wed)g(pre\002x)o
+(es)g(of)g(an)o(y)g(URI')-5 b(s.)36 b(If)23 b(ask)o(ed)h(to)f(fetch)h
+(the)208 3536 y(content)19 b(for)g(a)i(URI)g(that)f(does)g(start)h
+(with)f(one)g(of)f(the)i(pre\002x)o(es,)e(it)i(will)g(raise)f(an)g(e)o
+(xception.)208 3669 y(In)i(addition)g(to)h Fl(Opaque)g
+Fn(and)f Fl(XML)h Fn(methods,)f(this)i(class)g(pro)o(vides)d(a)i
+Fl(Resolve)g Fn(method)e(that)i(e)o(xamines)f(the)h(typecode)208
+3768 y(to)d(determine)f(what)h(type)g(of)f(data)i(is)g(desired.)0
+3915 y(If)27 b(the)f(SO)m(AP)i(application)d(is)j(gi)n(v)o(en)d(a)i
+(multi-part)f(MIME)g(document,)g(the)h Fl(MIMEResolver)e
+Fn(class)j(can)e(be)h(used)f(to)h(process)0 4015 y(SO)m(AP)21
+b(with)f(Attachments.)0 4162 y(The)50 b Fl(MIMEResolver)f
+Fn(class)j(will)f(read)f(the)h(entire)f(multipart)f(MIME)i(document,)k
+(noting)50 b(an)o(y)g Fl(Content-ID)f Fn(or)0 4261 y
+Fl(Content-Location)29 b Fn(headers)h(that)i(appear)e(on)h(the)g
+(headers)f(of)h(an)o(y)g(of)g(the)g(message)g(parts,)j(and)d(use)g
+(them)g(to)g(resolv)o(e)0 4361 y(an)o(y)19 b Fl(href)h
+Fn(attrib)n(utes)h(that)f(appear)f(in)h(the)g(SO)m(AP)h(message.)0
+4508 y Fo(class)g Fj(MIMEResolver)p Fl(\()p Fk(ct,)d(f)12
+b Fc([)p Fk(,)20 b(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+4608 y Fn(The)19 b Fl(ct)h Fn(parameter)e(is)j(a)f(string)g(that)g
+(contains)f(the)h(v)n(alue)f(of)g(the)h(MIME)f Fl(Content-Type)g
+Fn(header)-5 b(.)24 b(The)19 b Fl(f)h Fn(parameter)f(is)208
+4707 y(the)h(input)f(stream,)h(which)g(should)f(be)h(positioned)f(just)
+h(after)g(the)g(message)h(headers.)208 4840 y(The)e(follo)n(wing)g(k)o
+(e)o(yw)o(ord)f(ar)o(guments)g(may)i(be)g(used:)p 0 5549
+3901 4 v 3808 5649 a Fi(35)p eop end
+%%Page: 36 42
+TeXDict begin 36 41 bop 378 70 a Fo(K)n(eyw)o(ord)p 825
+100 4 100 v 172 w(Default)p 1187 100 V 99 w(Description)p
+328 103 3451 4 v 378 173 a Fl(seekable)p 825 402 4 299
+v 205 w(0)p 1187 402 V 206 w Fn(Whether)25 b(or)g(not)g(the)g(input)g
+(stream)g(is)h(seekable;)i(passed)d(to)h(the)f(constructor)f(for)1239
+272 y(the)19 b(internal)f Fl(multifile)g Fn(object.)49
+b(Changed)18 b(in)h(v)o(ersion)f(2.0:)h(def)o(ault)f(had)h(been)1239
+372 y(1.)378 472 y Fl(next)p 825 601 4 200 v 330 w(None)p
+1187 601 V 131 w Fn(A)k(resolv)o(er)f(object)g(that)i(will)f(be)g(ask)o
+(ed)g(to)g(resolv)o(e)f(the)h(URI)h(if)f(it)h(is)g(not)f(found)e(in)
+1239 571 y(the)f(MIME)g(document.)48 b(Ne)n(w)21 b(in)f(v)o(ersion)f
+(1.1.)378 671 y Fl(uribase)p 825 900 4 299 v 180 w(None)p
+1187 900 V 131 w Fn(The)j(base)h(URI)g(to)g(be)g(used)f(when)g
+(resolving)f(relati)n(v)o(e)h(URI')-5 b(s;)25 b(this)e(will)h
+(typically)1239 770 y(be)30 b(the)h(v)n(alue)f(of)g(the)h
+Fl(Content-Location)d Fn(header)m(,)j(if)g(present.)112
+b(Ne)n(w)31 b(in)1239 870 y(v)o(ersion)19 b(1.1.)0 1012
+y(In)h(addition)f(to)h(to)h(the)f Fl(Opaque)p Fn(,)f
+Fl(Resolve)p Fn(,)g(and)h Fl(XML)g Fn(methods)f(as)i(described)e(abo)o
+(v)o(e,)f(the)j(follo)n(wing)d(method)h(is)i(a)n(v)n(ailable:)0
+1159 y Fj(GetSOAPPart)p Fl(\(\))208 1259 y Fn(This)f(method)f(returns)g
+(a)i(stream)f(containing)e(the)i(SO)m(AP)h(message)f(te)o(xt.)0
+1405 y(The)g(follo)n(wing)e(attrib)n(utes)j(are)f(read-only:)0
+1552 y Fj(parts)208 1652 y Fn(An)65 b(array)g(of)g(tuples,)76
+b(one)65 b(for)g(each)g(MIME)g(bodypart)e(found.)159
+b(Each)65 b(tuple)g(has)h(tw)o(o)g(elements,)76 b(a)208
+1752 y Fl(mimetools.Message)26 b Fn(object)j(which)f(contains)h(the)g
+(headers)f(for)h(the)g(bodypart,)g(and)f(a)i Fl(StringIO)e
+Fn(object)g(con-)208 1851 y(taining)19 b(the)h(data.)0
+1998 y Fj(id_dict)208 2098 y Fn(A)32 b(dictionary)d(whose)i(k)o(e)o(ys)
+h(are)f(the)h(v)n(alues)f(of)g(an)o(y)g Fl(Content-ID)f
+Fn(headers,)j(and)e(whose)g(v)n(alue)g(is)h(the)g(appropriate)208
+2197 y Fl(parts)19 b Fn(tuple.)0 2344 y Fj(loc_dict)208
+2444 y Fn(A)k(dictionary)e(whose)i(k)o(e)o(ys)g(are)g(the)g(v)n(alues)f
+(of)h(an)o(y)f Fl(Content-Location)f Fn(headers,)i(and)f(whose)h(v)n
+(alue)f(is)i(the)f(appro-)208 2543 y(priate)c Fl(parts)h
+Fn(tuple.)p 0 5549 3901 4 v 0 5649 a Fi(36)1575 b(Chapter)24
+b(9.)52 b(The)23 b Fl(resolvers)e Fi(module)k(\227)e(f)n(etching)g
+(remote)g(data)p eop end
+%%Page: 37 43
+TeXDict begin 37 42 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3661
+427 y Fh(TEN)p 0 515 V 1602 978 a Ft(Dispatching)57 b(and)g(In)l(v)-5
+b(oking)0 1468 y Fn(Ne)n(w)20 b(in)h(v)o(ersion)e(1.1.)0
+1615 y Fl(ZSI)f Fn(is)g(focused)e(on)h(parsing)g(and)g(generating)f(SO)
+m(AP)i(messages,)g(and)f(pro)o(vides)f(limited)h(f)o(acilities)h(for)f
+(dispatching)f(to)i(the)f(appro-)0 1714 y(priate)22 b(message)g
+(handler)-5 b(.)29 b(This)23 b(is)g(because)e Fl(ZSI)h
+Fn(w)o(orks)g(within)g(man)o(y)f(client)h(and)g(serv)o(er)f(en)m
+(vironments,)e(and)j(the)g(dispatching)0 1814 y(styles)f(for)e(these)i
+(dif)n(ferent)d(en)m(vironments)g(can)i(be)g(v)o(ery)f(dif)n(ferent.)0
+1961 y(Ne)n(v)o(ertheless,)f Fl(ZSI)g Fn(includes)g(some)h(dispatch)f
+(and)g(in)m(v)n(ocation)e(functions.)23 b(T)-7 b(o)19
+b(use)g(them,)f(the)o(y)g(must)h(be)f(e)o(xplicitly)g(imported,)f(as)0
+2061 y(sho)n(wn)i(in)i(the)f(e)o(xample)f(at)h(the)h(start)f(of)g(this)
+h(document.)0 2207 y(The)d(implementation)e(\(and)h(names\))h(of)g(the)
+g(these)h(classes)g(re\003ects)f(the)h(orientation)d(of)i(using)g(SO)m
+(AP)h(for)f(remote)f(procedure)f(calls)0 2307 y(\(RPC\).)0
+2454 y(Both)k(client)g(and)g(serv)o(er)f(share)h(a)h(class)g(that)f
+(de\002nes)g(the)h(mechanism)d(a)j(client)f(uses)h(to)f(authenticate)f
+(itself.)0 2601 y Fo(class)i Fj(AUTH)p Fl(\(\))208 2700
+y Fn(This)j(class)h(de\002nes)e(constants)h(used)g(to)g(identify)f(ho)n
+(w)g(the)h(client)g(authenticated:)31 b Fl(none)24 b
+Fn(if)g(no)g(authentication)e(w)o(as)j(pro-)208 2800
+y(vided;)k Fl(httpbasic)d Fn(if)i(HTTP)f(basic)h(authentication)d(w)o
+(as)j(used,)g(or)f Fl(zsibasic)f Fn(if)i Fl(ZSI)f Fn(basic)g
+(authentication)e(\(see)208 2900 y(belo)n(w\)\))18 b(w)o(as)j(used.)0
+3047 y(The)i Fl(ZSI)f Fn(schema)h(\(see)g(the)g(last)h(chapter)d(of)i
+(this)g(manual\))f(de\002nes)g(a)i(SO)m(AP)f(header)f(element,)h
+Fl(BasicAuth)p Fn(,)f(that)h(contains)f(a)0 3146 y(name)e(and)f(passw)o
+(ord.)24 b(This)d(is)g(similar)f(to)g(the)g(HTTP)h(basic)f
+(authentication)e(header)m(,)h(e)o(xcept)g(that)h(it)h(can)f(be)g(used)
+g(independently)0 3246 y(from)f(an)h(HTTP)h(transport.)0
+3573 y Fg(10.1)121 b(Dispatching)0 3806 y Fn(The)28 b
+Fl(ZSI.dispatch)e Fn(module)h(allo)n(ws)i(you)e(to)i(e)o(xpose)e
+(Python)g(functions)g(as)i(a)f(web)g(service.)49 b(The)28
+b(module)f(pro)o(vides)g(the)0 3906 y(infrastructure)20
+b(to)i(parse)g(the)g(request,)f(dispatch)g(to)i(the)f(appropriate)d
+(handler)m(,)i(and)g(then)h(serialize)g(an)o(y)f(return)g(v)n(alue)g
+(back)g(to)i(the)0 4005 y(client.)35 b(The)23 b(v)n(alue)g(returned)f
+(by)h(the)g(function)f(will)j(be)e(serialized)g(back)g(to)h(the)f
+(client.)35 b(If)24 b(an)f(e)o(xception)f(occurs,)h(a)h(SO)m(AP)h(f)o
+(ault)0 4105 y(will)c(be)f(sent)h(back)e(to)h(the)h(client.)0
+4390 y Ff(10.1.1)101 b(Dispatch)28 b(Beha)n(viors)0 4593
+y Fn(By)22 b(def)o(ault)e(the)i(callback)e(is)j(in)m(v)n(ok)o(ed)c
+(with)j(the)f(p)o(yobj)f(representation)g(of)h(the)g(body)f(root)h
+(element,)f(and)h(it)h(is)h(e)o(xpected)c(to)j(return)0
+4692 y(a)28 b(self-describing)d(request)i(\(w/typecode\).)44
+b(P)o(arsing)27 b(is)h(done)e(via)i(a)f(typecode)f(from)g(typesmodule,)
+h(or)g(An)o(y)-5 b(.)45 b(Other)27 b(k)o(e)o(yw)o(ord)0
+4792 y(options)19 b(are)h(a)n(v)n(ailable)g(in)g(dispatch)g(mechanisms)
+f(\(see)i(belo)n(w\))e(that)h(result)g(in)h(dif)n(ferent)d(beha)n(vior)
+-5 b(.)0 5060 y Fi(r)r(pc)0 5263 y Fn(An)19 b(rpc)f(service)g(will)h
+(ignore)e(the)i(body)e(root)h(\(RPC)h(Wrapper\))e(of)h(the)h(request,)f
+(and)g(parse)g(all)h(\224parts\224)f(of)h(message)f(via)g(indi)n
+(vidual)0 5363 y(typecodes.)55 b(The)30 b(callback)g(function)f(is)j(e)
+o(xpected)d(to)i(return)f(the)g(parts)h(of)f(the)h(message)g(in)g(a)g
+(dict)g(or)f(a)h(list.)58 b(The)30 b(dispatch)p 0 5549
+3901 4 v 3808 5649 a Fi(37)p eop end
+%%Page: 38 44
+TeXDict begin 38 43 bop 0 83 a Fn(mechanism)22 b(will)i(try)f(to)h
+(serialize)g(it)g(as)g(a)g(Struct)f(b)n(ut)g(if)h(this)g(is)g(not)f
+(possible)g(it)i(will)f(be)f(serialized)g(as)h(an)g(Array)-5
+b(.)33 b(P)o(arsing)23 b(done)0 183 y(via)d(a)h(typecode)d(from)h
+(typesmodule,)f(or)i(An)o(y)-5 b(.)24 b(Not)c(compatible)f(with)h
+Fk(docstyle)p Fn(.)0 451 y Fi(docstyle)0 654 y Fn(Callback)15
+b(is)i(in)m(v)n(ok)o(ed)d(with)h(a)h(P)o(arsedSoap)f(instance)g
+(representing)e(the)j(request,)g(and)f(the)g(return)g(v)n(alue)f(is)j
+(serialized)e(with)h(an)g(XML)0 753 y(typecode)h(\(DOM\).)g(The)h
+(result)h(in)f(wrapped)f(as)i(an)g(rpc-style)e(message,)h(with)h
+Fk(Response)e Fn(appended)g(to)h(the)g(request)g(wrapper)-5
+b(.)23 b(Not)0 853 y(compatible)c(with)h Fk(rpc)p Fn(.)0
+1138 y Ff(10.1.2)101 b(Special)29 b(Modules)0 1341 y
+Fn(These)20 b(are)g(k)o(e)o(yw)o(ord)e(options)i(a)n(v)n(ailable)f(to)i
+(all)g(dispatch)e(mechansism)g(\(see)i(belo)n(w\).)0
+1609 y Fi(modules)0 1812 y Fn(Dispatch)g(is)i(based)e(solely)g(on)g
+(the)h(name)f(of)g(the)g(root)g(element)g(in)h(the)f(incoming)f(SO)m
+(AP)i(request;)g(the)f(request)g(URL)h(is)h(ignored.)0
+1912 y(These)e(modules)f(will)i(be)f(search)g(for)f(a)i(matching)e
+(function.)26 b(If)21 b(no)f(modules)g(are)i(speci\002ed,)e(only)h(the)
+g Fl(__main__)f Fn(module)g(will)0 2011 y(be)g(searched.)0
+2280 y Fi(typesmodule)0 2483 y Fn(Used)28 b(for)g(parsing.)47
+b(This)28 b(module)f(should)g(contain)g(class)i(de\002nitions)e(with)h
+(the)g Fl(typecode)f Fn(attrib)n(ute)h(set)h(to)f(a)g
+Fl(TypeCode)0 2582 y Fn(instance.)i(By)22 b(def)o(ault,)g(a)g(class)h
+(de\002nition)e(matching)g(the)h(root)f(element)g(name)h(will)g(be)g
+(retrie)n(v)o(ed)f(or)g(the)h(An)o(y)g(typecode)e(will)j(be)0
+2682 y(used.)i(If)20 b(using)f Fk(rpc)p Fn(,)i(each)e(child)h(of)g(the)
+g(root)g(element)f(will)i(be)f(used)g(to)h(retrie)n(v)o(e)e(a)h(class)i
+(de\002nition)d(of)g(the)i(same)f(name.)0 2967 y Ff(10.1.3)101
+b(Dispatch)28 b(Mechanisms)0 3170 y Fn(Three)e(dispatch)g(mechanisms)f
+(are)i(pro)o(vided:)35 b(one)26 b(supports)f(standard)h(CGI)h(scripts,)
+h(one)e(runs)g(a)h(dedicated)e(serv)o(er)h(based)g(on)0
+3269 y(the)19 b Fl(BaseHTTPServer)e Fn(module,)g(and)i(the)f(third)h
+(uses)g(the)g(JonPY)g(package,)e Fl(http://jonpy.sourceforge.net)p
+Fn(,)d(to)0 3369 y(support)19 b(F)o(astCGI.)0 3516 y
+Fj(AsServer)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 3615 y Fn(This)21 b(creates)g(a)g
+Fl(HTTPServer)f Fn(object)g(with)h(a)h(request)e(handler)g(that)h(only)
+f(supports)g(the)h(\223POST\224)g(method.)26 b(Dispatch)21
+b(is)208 3715 y(based)e(solely)h(on)g(the)g(name)g(of)g(the)g(root)g
+(element)f(in)i(the)f(incoming)e(SO)m(AP)j(request;)f(the)g(request)g
+(URL)g(is)h(ignored.)208 3848 y(The)e(follo)n(wing)g(k)o(e)o(yw)o(ord)f
+(ar)o(guments)g(may)i(be)g(used:)257 4059 y Fo(K)n(eyw)o(ord)p
+853 4089 4 100 v 464 w(Default)p 1501 4089 V 242 w(Description)p
+208 4092 3886 4 v 257 4162 a Fl(port)p 853 4192 4 100
+v 672 w(80)p 1501 4192 V 324 w Fn(Port)g(to)g(listen)h(on.)257
+4261 y Fl(addr)p 853 4291 V 672 w('')p 1501 4291 V 324
+w Fn(Address)e(to)i(listen)f(on.)257 4361 y Fl(docstyle)p
+853 4391 V 397 w(False)p 1501 4391 V 249 w Fn(Exhibit)f(the)h
+Fk(docstyle)g Fn(beha)n(vior)-5 b(.)257 4461 y Fl(rpc)p
+853 4490 V 647 w(False)p 1501 4490 V 249 w Fn(Exhibit)19
+b(the)h Fk(rpc)g Fn(beha)n(vior)-5 b(.)257 4560 y Fl(modules)p
+853 4590 V 298 w(\(__main__,\))p 1501 4590 V 98 w Fn(List)20
+b(of)g(modules)f(containing)g(functions)g(that)h(can)g(be)g(in)m(v)n
+(ok)o(ed.)257 4660 y Fl(typesmodule)p 853 4789 4 200
+v 98 w(\(__main__,\))p 1501 4789 V 98 w Fn(This)e(module)e(is)j(used)f
+(for)g(parsing,)f(it)i(contains)e(class)i(de\002nitions)e(that)h
+(specify)g(the)1553 4759 y Fl(typecode)h Fn(attrib)n(ute.)257
+4859 y Fl(nsdict)p 853 4889 4 100 v 572 w({})p 1501 4889
+V 324 w Fn(Namespace)g(dictionary)f(to)j(send)f(in)g(the)g(SO)m(AP)h
+Fl(Envelope)0 5024 y Fj(AsCGI)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)
+12 b Fc(])p Fl(\))208 5124 y Fn(This)20 b(method)e(parses)i(the)g(CGI)g
+(input)g(and)f(in)m(v)n(ok)o(es)g(a)h(function)e(that)i(has)g(the)g
+(same)g(name)g(as)g(the)g(top-le)n(v)o(el)e(SO)m(AP)j(request)208
+5224 y(element.)208 5356 y(The)e(follo)n(wing)g(k)o(e)o(yw)o(ord)f(ar)o
+(guments)g(may)i(be)g(used:)p 0 5549 3901 4 v 0 5649
+a Fi(38)2399 b(Chapter)23 b(10.)52 b(Dispatching)24 b(and)g(In)n(v)n
+(oking)p eop end
+%%Page: 39 45
+TeXDict begin 39 44 bop 257 161 a Fo(K)n(eyw)o(ord)p
+853 191 4 100 v 464 w(Default)p 1501 191 V 242 w(Description)p
+208 194 3886 4 v 257 264 a Fl(rpc)p 853 294 4 100 v 647
+w(False)p 1501 294 V 249 w Fn(Exhibit)19 b(the)h Fk(rpc)g
+Fn(beha)n(vior)-5 b(.)257 364 y Fl(modules)p 853 394
+V 298 w(\(__main__,\))p 1501 394 V 98 w Fn(List)20 b(of)g(modules)f
+(containing)g(functions)g(that)h(can)g(be)g(in)m(v)n(ok)o(ed.)257
+463 y Fl(typesmodule)p 853 593 4 200 v 98 w(\(__main__,\))p
+1501 593 V 98 w Fn(This)e(module)e(is)j(used)f(for)g(parsing,)f(it)i
+(contains)e(class)i(de\002nitions)e(that)h(specify)g(the)1553
+563 y Fl(typecode)h Fn(attrib)n(ute.)257 663 y Fl(nsdict)p
+853 692 4 100 v 572 w({})p 1501 692 V 324 w Fn(Namespace)g(dictionary)f
+(to)j(send)f(in)g(the)g(SO)m(AP)h Fl(Envelope)0 828 y
+Fj(AsHandler)p Fl(\()p Fk(r)m(equest=None)p Fc([)p Fk(,)c(**k)o(e)n
+(ywor)m(ds)12 b Fc(])p Fl(\))208 927 y Fn(This)20 b(method)f(is)i(used)
+f(within)g(a)g(JonPY)h(handler)d(to)j(do)e(dispatch.)208
+1055 y(The)g(follo)n(wing)g(k)o(e)o(yw)o(ord)f(ar)o(guments)g(may)i(be)
+g(used:)257 1261 y Fo(K)n(eyw)o(ord)p 853 1291 V 464
+w(Default)p 1501 1291 V 242 w(Description)p 208 1294
+3886 4 v 257 1364 a Fl(request)p 853 1394 4 100 v 472
+w(None)p 1501 1394 V 274 w Fn(modp)o(ython)d(HTTPRequest)i(instance.)
+257 1464 y Fl(modules)p 853 1494 V 298 w(\(__main__,\))p
+1501 1494 V 98 w Fn(List)h(of)g(modules)f(containing)g(functions)g
+(that)h(can)g(be)g(in)m(v)n(ok)o(ed.)257 1563 y Fl(docstyle)p
+853 1593 V 397 w(False)p 1501 1593 V 249 w Fn(Exhibit)f(the)h
+Fk(docstyle)g Fn(beha)n(vior)-5 b(.)257 1663 y Fl(rpc)p
+853 1693 V 647 w(False)p 1501 1693 V 249 w Fn(Exhibit)19
+b(the)h Fk(rpc)g Fn(beha)n(vior)-5 b(.)257 1762 y Fl(typesmodule)p
+853 1892 4 200 v 98 w(\(__main__,\))p 1501 1892 V 98
+w Fn(This)18 b(module)e(is)j(used)f(for)g(parsing,)f(it)i(contains)e
+(class)i(de\002nitions)e(that)h(specify)g(the)1553 1862
+y Fl(typecode)h Fn(attrib)n(ute.)257 1962 y Fl(nsdict)p
+853 1992 4 100 v 572 w({})p 1501 1992 V 324 w Fn(Namespace)g
+(dictionary)f(to)j(send)f(in)g(the)g(SO)m(AP)h Fl(Envelope)0
+2127 y Fj(AsJonPy)p Fl(\()p Fk(r)m(equest=None)p Fc([)p
+Fk(,)c(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208 2227
+y Fn(This)20 b(method)f(is)i(used)f(within)g(a)g(JonPY)h(handler)d(to)j
+(do)e(dispatch.)208 2354 y(The)g(follo)n(wing)g(k)o(e)o(yw)o(ord)f(ar)o
+(guments)g(may)i(be)g(used:)257 2560 y Fo(K)n(eyw)o(ord)p
+853 2590 V 464 w(Default)p 1501 2590 V 242 w(Description)p
+208 2593 3886 4 v 257 2663 a Fl(request)p 853 2693 4
+100 v 472 w(None)p 1501 2693 V 274 w Fn(jonp)o(y)f(Request)h(instance.)
+257 2763 y Fl(modules)p 853 2793 V 298 w(\(__main__,\))p
+1501 2793 V 98 w Fn(List)g(of)g(modules)f(containing)g(functions)g
+(that)h(can)g(be)g(in)m(v)n(ok)o(ed.)257 2862 y Fl(docstyle)p
+853 2892 V 397 w(False)p 1501 2892 V 249 w Fn(Exhibit)f(the)h
+Fk(docstyle)g Fn(beha)n(vior)-5 b(.)257 2962 y Fl(rpc)p
+853 2992 V 647 w(False)p 1501 2992 V 249 w Fn(Exhibit)19
+b(the)h Fk(rpc)g Fn(beha)n(vior)-5 b(.)257 3062 y Fl(typesmodule)p
+853 3191 4 200 v 98 w(\(__main__,\))p 1501 3191 V 98
+w Fn(This)18 b(module)e(is)j(used)f(for)g(parsing,)f(it)i(contains)e
+(class)i(de\002nitions)e(that)h(specify)g(the)1553 3161
+y Fl(typecode)h Fn(attrib)n(ute.)257 3261 y Fl(nsdict)p
+853 3291 4 100 v 572 w({})p 1501 3291 V 324 w Fn(Namespace)g
+(dictionary)f(to)j(send)f(in)g(the)g(SO)m(AP)h Fl(Envelope)208
+3384 y Fn(The)e(follo)n(wing)g(code)g(sho)n(ws)i(a)f(sample)g(use:)444
+3603 y Fe(import)44 b(jon.fcgi)444 3694 y(from)g(ZSI)g(import)g
+(dispatch)444 3786 y(import)g(MyHandler)444 3968 y(class)g
+(Handler\(cgi.Handler\):)623 4060 y(def)g(process\(self,)f(req\):)802
+4151 y(dispatch.AsJonPy\(modules=\(MyHandler,\),)c(request=req\))444
+4334 y(jon.fcgi.Server\({jon.fcgi.FCGI_RESPOND)o(ER:)g
+(Handler}\).run\(\))0 4671 y Ff(10.1.4)101 b(Other)27
+b(Dispatch)i(Stuff)0 4855 y Fj(GetClientBinding)p Fl(\(\))208
+4954 y Fn(More)19 b(sophisticated)g(scripts)i(may)e(w)o(ant)i(to)f(use)
+g(access)h(the)f(client)h(binding)d(object,)h(which)h(encapsulates)f
+(all)i(information)208 5054 y(about)i(the)i(client)f(in)m(v)n(oking)e
+(the)j(script.)38 b(This)24 b(function)f(returns)h Fl(None)g
+Fn(or)g(the)h(binding)e(information,)f(an)j(object)f(of)g(type)208
+5154 y Fl(ClientBinding)p Fn(,)18 b(described)h(belo)n(w)-5
+b(.)0 5300 y Fo(class)21 b Fj(ClientBinding)p Fl(\()p
+Fk(...)p Fl(\))208 5400 y Fn(This)f(object)g(contains)f(information)f
+(about)h(the)h(client.)25 b(It)c(is)g(created)e(internally)g(by)h
+Fl(ZSI)p Fn(.)p 0 5549 3901 4 v 0 5649 a Fi(10.1.)52
+b(Dispatching)3139 b(39)p eop end
+%%Page: 40 46
+TeXDict begin 40 45 bop 0 83 a Fj(GetAuth)p Fl(\(\))208
+183 y Fn(This)15 b(returns)g(a)h(tuple)g(containing)e(information)f
+(about)i(the)g(client)h(identity)-5 b(.)22 b(The)16 b(\002rst)g
+(element)f(will)i(be)f(one)f(of)g(the)h(constants)208
+282 y(from)i(the)h Fl(AUTH)g Fn(class)h(described)e(abo)o(v)o(e.)23
+b(F)o(or)c(HTTP)g(or)g Fl(ZSI)g Fn(basic)g(authentication,)f(the)h(ne)o
+(xt)f(tw)o(o)i(elements)f(will)g(be)h(the)208 382 y(name)f(and)h(passw)
+o(ord)f(pro)o(vided)f(by)i(the)g(client.)0 529 y Fj(GetNS)p
+Fl(\(\))208 628 y Fn(Returns)g(the)g(namespace)f(URI)h(that)h(the)f
+(client)g(is)h(using,)f(or)g(an)g(empty)f(string.)25
+b(This)20 b(can)g(be)g(useful)g(for)f(v)o(ersioning.)0
+775 y Fj(GetRequest)p Fl(\(\))208 875 y Fn(Returns)h(the)g
+Fl(ParsedSoap)f Fn(object)g(of)h(the)g(incoming)f(request.)0
+1022 y(The)h(follo)n(wing)e(attrib)n(ute)i(is)h(read-only:)0
+1169 y Fj(environ)208 1268 y Fn(A)f(dictionary)f(of)h(the)g(en)m
+(vironment)d(v)n(ariables.)24 b(This)c(is)i(most)e(useful)f(when)h
+Fl(AsCGI\(\))f Fn(is)j(used.)0 1596 y Fg(10.2)121 b(The)34
+b Fb(client)e Fg(module)i(\227)f(sending)i(SO)n(AP)d(messages)0
+1829 y Fl(ZSI)23 b Fn(includes)e(a)i(module)f(to)g(connect)g(to)g(a)h
+(SO)m(AP)h(serv)o(er)e(o)o(v)o(er)f(HTTP)-9 b(,)22 b(send)g(requests,)h
+(and)f(parse)g(the)h(response.)31 b(It)23 b(is)g(b)n(uilt)g(on)0
+1928 y(the)i(standard)g(Python)f Fl(httplib)h Fn(and)f
+Fl(Cookie)h Fn(modules.)40 b(It)25 b(must)h(be)f(e)o(xplicitly)g
+(imported,)f(as)j(in)e(`)p Fl(from)49 b(ZSI.client)0
+2028 y(import)g(AUTH,Binding)p Fn('.)0 2313 y Ff(10.2.1)p
+383 2313 30 4 v 136 w(Binding)0 2512 y Fo(class)21 b
+Fj(_Binding)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 2612 y Fn(This)19 b(class)h(encapsulates)e(a)h
+(connection)e(to)j(a)f(serv)o(er)m(,)f(kno)n(wn)f(as)j(a)g
+Fk(binding)p Fn(.)j(A)c(single)g(binding)e(may)i(be)g(used)g(for)f
+(multiple)208 2711 y(RPC)j(calls.)26 b(Between)20 b(calls,)h
+(modi\002ers)e(may)h(be)g(used)g(to)g(change)f(the)h(URL)h(being)e
+(posted)h(to,)g(etc.)208 2844 y(Cookies)i(are)h(also)g(supported;)f(if)
+i(a)f(response)f(comes)g(back)h(with)g(a)g Fl(Set-Cookie)f
+Fn(header)m(,)f(it)j(will)g(be)e(parsed)g(and)h(used)208
+2944 y(in)d(subsequent)f(interactions.)208 3077 y(The)g(follo)n(wing)g
+(k)o(e)o(yw)o(ord)f(ar)o(guments)g(may)i(be)g(used:)257
+3288 y Fo(K)n(eyw)o(ord)p 903 3318 4 100 v 865 w(Default)p
+2253 3318 V 592 w(Description)p 208 3321 3808 4 v 257
+3391 a Fl(auth)p 903 3620 4 299 v 824 w(\(AUTH.none,\))p
+2253 3620 V 423 w Fn(A)28 b(tuple)f(with)h(authentication)d
+(information;)j(the)g(\002rst)2304 3490 y(v)n(alue)39
+b(should)g(be)g(one)g(of)g(the)h(constants)f(from)f(the)2304
+3590 y Fl(AUTH)20 b Fn(class.)257 3689 y Fl(nsdict)p
+903 3819 4 200 v 973 w({})p 2253 3819 V 674 w Fn(Namespace)57
+b(dictionary)f(to)i(send)f(in)h(the)f(SO)m(AP)2304 3789
+y Fl(Envelope)257 3889 y(soapaction)p 903 3919 4 100
+v 773 w('')p 2253 3919 V 674 w Fn(V)-9 b(alue)20 b(for)g(the)g
+Fl(SOAPAction)f Fn(HTTP)h(header)-5 b(.)257 3988 y Fl(readerclass)p
+903 4118 4 200 v 673 w(None)p 2253 4118 V 624 w Fn(Class)23
+b(used)e(to)g(create)g(DOM-creating)e(XML)i(readers;)2304
+4088 y(see)g(the)f(description)f(in)h(the)h Fl(ParsedSoap)d
+Fn(class.)257 4188 y Fl(writerclass)p 903 4317 V 673
+w(None)p 2253 4317 V 624 w Fn(ElementProxy)23 b(Class)j(used)f(to)g
+(create)f(XML)h(writers;)2304 4287 y(see)c(the)f(description)f(in)h
+(the)h Fl(SoapWriter)d Fn(class.)257 4387 y Fl(tracefile)p
+903 4516 V 773 w(None)p 2253 4516 V 624 w Fn(An)33 b(object)f(with)g(a)
+h Fl(write)f Fn(method,)i(where)e(pack)o(et)2304 4486
+y(traces)21 b(will)g(be)f(recorded.)257 4586 y Fl(transport)p
+903 4616 4 100 v 248 w Fn(HTTPConnection/HTTPSConnection)p
+2253 4616 V 94 w(transport)f(class)257 4686 y Fl(transdict)p
+903 4716 V 831 w Fm(fg)p 2253 4716 V 682 w Fn(k)o(e)o(yw)o(ord)g(ar)o
+(guments)f(for)h(connection)f(initialization)257 4785
+y Fl(url)p 903 4815 V 1122 w Fn(n/a)p 2253 4815 V 673
+w(URL)j(to)f(post)h(to.)257 4885 y Fl(wsAddressURI)p
+903 5015 4 200 v 633 w Fn(None)p 2253 5015 V 633 w(URI,)37
+b(identi\002es)e(the)h(WS-Address)g(speci\002cation)f(to)2304
+4985 y(use.)26 b(By)20 b(def)o(ault)g(it')-5 b(s)21 b(not)f(used.)257
+5084 y Fl(sig_handler)p 903 5114 4 100 v 683 w Fn(None)p
+2253 5114 V 633 w(XML)h(Signature)e(handler)m(,)f(must)i(sign)g(and)g
+(v)o(erify)-5 b(.)208 5212 y(If)29 b(using)g(SSL,)h(the)g
+Fl(cert_file)e Fn(and)h Fl(key_file)g Fn(k)o(e)o(yw)o(ord)f(parameters)
+g(may)i(also)f(be)h(used.)53 b(F)o(or)29 b(details)h(see)h(the)208
+5312 y(documentation)17 b(for)i(the)i Fl(httplib)e Fn(module.)p
+0 5549 3901 4 v 0 5649 a Fi(40)2399 b(Chapter)23 b(10.)52
+b(Dispatching)24 b(and)g(In)n(v)n(oking)p eop end
+%%Page: 41 47
+TeXDict begin 41 46 bop 0 83 a Fn(Once)28 b(a)g Fl(_Binding)g
+Fn(object)f(has)h(been)g(created,)h(the)f(follo)n(wing)e(modi\002ers)i
+(are)g(a)n(v)n(ailable.)48 b(All)29 b(of)f(them)f(return)g(the)h
+(binding)0 183 y(object,)19 b(so)i(that)f(multiple)g(modi\002ers)f(can)
+h(be)g(chained)f(together)-5 b(.)0 330 y Fj(AddHeader)p
+Fl(\()p Fk(header)c(,)17 b(value)p Fl(\))208 429 y Fn(Output)i(the)h
+(speci\002ed)g Fl(header)g Fn(and)f Fl(value)h Fn(with)h(the)f(HTTP)g
+(headers.)0 576 y Fj(SetAuth)p Fl(\()p Fk(style)o(,)f(name)o(,)h
+(passwor)m(d)r Fl(\))208 676 y Fn(The)c Fl(style)g Fn(should)f(be)i
+(one)f(of)g(the)h(constants)f(from)f(the)i Fl(AUTH)f
+Fn(class)h(described)f(abo)o(v)o(e.)22 b(The)16 b(remaining)f
+(parameters)g(will)208 775 y(v)n(ary)k(depending)e(on)j(the)g
+Fl(style)p Fn(.)25 b(Currently)19 b(only)g(basic)i(authentication)d
+(data)i(of)g(name)f(and)h(passw)o(ord)f(are)i(supported.)0
+922 y Fj(SetNS)p Fl(\()p Fk(uri)p Fl(\))208 1022 y Fn(Set)f(the)h(def)o
+(ault)e(namespace)g(for)h(the)g(request)f(to)i(the)f(speci\002ed)g
+Fl(uri)p Fn(.)0 1169 y Fj(SetURL)p Fl(\()p Fk(url)p Fl(\))208
+1268 y Fn(Set)g(the)h(URL)f(where)g(the)g(post)g(is)h(made)f(to)g
+Fl(url)p Fn(.)0 1415 y Fj(ResetHeaders)p Fl(\(\))208
+1515 y Fn(Remo)o(v)o(e)f(an)o(y)g(headers)g(that)i(were)f(added)f(by)g
+Fl(AddHeader\(\))p Fn(.)0 1662 y(The)h(follo)n(wing)e(attrib)n(ute)i
+(may)g(also)g(be)h(modi\002ed:)0 1808 y Fj(trace)208
+1908 y Fn(If)e(this)h(attrib)n(ute)f(is)h(not)f Fl(None)p
+Fn(,)g(it)h(should)f(be)g(an)g(object)g(with)h(a)f Fl(write)g
+Fn(method,)f(where)h(pack)o(et)g(traces)g(will)i(be)e(recorded.)0
+2055 y(Once)j(the)f(necessary)g(parameters)g(ha)n(v)o(e)g(been)g
+(speci\002ed)h(\(at)f(a)i(minimum,)d(the)i(URL)g(must)g(ha)n(v)o(e)f
+(been)g(gi)n(v)o(en)g(in)h(the)f(constructor)0 2155 y(are)f(through)e
+Fl(SetURL)p Fn(\),)h(in)m(v)n(ocations)g(can)h(be)g(made.)0
+2301 y Fj(RPC)p Fl(\()p Fk(url,)g(opname)o(,)e(pyobj,)h(r)m
+(eplytype=None)p Fc([)p Fk(,)g(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 2401 y Fn(This)24 b(is)h(the)g(highest-le)n(v)o(el)d
+(in)m(v)n(ocation)h(method.)36 b(It)24 b(calls)h Fl(Send\(\))f
+Fn(to)h(send)f Fl(pyobj)g Fn(to)g(the)h(speci\002ed)f
+Fl(url)g Fn(to)h(perform)208 2501 y(the)20 b Fl(opname)f
+Fn(operation,)f(and)i(calls)h Fl(Receive\(\))e Fn(e)o(xpecting)f(to)j
+(get)f(a)h(reply)e(of)h(the)g(speci\002ed)g Fl(replytype)p
+Fn(.)208 2634 y(This)28 b(method)e(will)j(raise)f(a)h
+Fl(TypeError)d Fn(if)j(the)f(response)f(does)g(not)h(appear)f(to)h(be)g
+(a)g(SO)m(AP)h(message,)g(or)f(if)g(is)h(v)n(alid)208
+2733 y(SO)m(AP)21 b(b)n(ut)f(contains)f(a)i(f)o(ault.)0
+2880 y Fj(Send)p Fl(\()p Fk(url,)e(opname)o(,)g(pyboj)p
+Fc([)p Fk(,)g(**k)o(e)n(ywor)m(ds)12 b Fc(])p Fl(\))208
+2980 y Fn(This)21 b(sends)g(the)h(speci\002ed)f Fl(pyobj)g
+Fn(to)g(the)g(speci\002ed)g Fl(url)p Fn(,)h(in)m(v)n(oking)d(the)i
+Fl(opname)g Fn(method.)27 b(The)21 b Fl(url)g Fn(can)g(be)g
+Fl(None)g Fn(if)208 3079 y(it)f(w)o(as)h(speci\002ed)f(in)g(the)g
+Fl(Binding)f Fn(constructor)f(or)i(if)g Fl(SetURL)f Fn(has)i(been)e
+(called.)25 b(See)20 b(belo)n(w)f(for)h(a)g(shortcut)f(v)o(ersion)g(of)
+208 3179 y(this)h(method.)208 3312 y(The)f(follo)n(wing)g(k)o(e)o(yw)o
+(ord)f(ar)o(guments)g(may)i(be)g(used:)257 3523 y Fo(K)n(eyw)o(ord)p
+1254 3553 4 100 v 1080 w(Default)p 2331 3553 V 456 w(Description)p
+208 3556 3886 4 v 257 3626 a Fl(auth_header)p 1254 3755
+4 200 v 888 w(None)p 2331 3755 V 488 w Fn(String)e(\(containing)f
+(presumably)g(serialized)h(XML\))h(to)2383 3725 y(output)g(as)i(an)f
+(authentication)e(header)-5 b(.)257 3825 y(SO)m(AP)21
+b Fl(Envelope)f(nsdict)p 1254 3954 V 537 w({})p 2331
+3954 V 539 w Fn(Namespace)57 b(dictionary)f(to)h(send)h(in)f(the)h(SO)m
+(AP)2383 3924 y Fl(Envelope)257 4024 y(requesttypecode)p
+1254 4054 4 100 v 737 w Fn(n/a)p 2331 4054 V 537 w(T)-7
+b(ypecode)18 b(specifying)h(ho)n(w)h(to)g(serialize)g(the)h(data.)257
+4124 y Fl(soapaction)p 1254 4154 V 548 w Fn(Obtained)e(from)g(the)i
+Fl(Binding)p 2331 4154 V 98 w Fn(V)-9 b(alue)20 b(for)f(the)i
+Fl(SOAPAction)d Fn(HTTP)j(header)-5 b(.)0 4266 y(Methods)19
+b(are)h(a)n(v)n(ailable)g(to)h(determine)d(the)i(type)g(of)g(response)f
+(that)i(came)f(back:)0 4413 y Fj(IsSOAP)p Fl(\(\))208
+4512 y Fn(Returns)g(true)h(if)g(the)g(message)g(appears)f(to)h(be)g(a)g
+(SO)m(AP)h(message.)27 b(\(Some)20 b(serv)o(ers)g(return)g(an)h(HTML)f
+(page)h(under)e(certain)208 4612 y(error)g(conditions.\))0
+4759 y Fj(IsAFault)p Fl(\(\))208 4858 y Fn(Returns)h(true)f(if)i(the)f
+(message)g(is)h(a)g(SO)m(AP)g(f)o(ault.)0 5005 y(Ha)n(ving)h
+(determined)e(the)j(type)f(of)g(the)g(message)h(\(or)m(,)f(more)f(lik)o
+(ely)-5 b(,)22 b(assuming)g(it)h(w)o(as)h(good)d(and)h(catching)f(an)h
+(e)o(xception)f(if)h(not\),)0 5105 y(the)17 b(follo)n(wing)e(methods)h
+(are)h(a)n(v)n(ailable)f(to)h(actually)f(parse)h(the)f(data.)24
+b(The)o(y)16 b(will)h(continue)f(to)h(return)e(the)i(same)g(v)n(alue)f
+(until)h(another)0 5205 y(message)j(is)h(sent.)0 5351
+y Fj(ReceiveRaw)p Fl(\(\))p 0 5549 3901 4 v 0 5649 a
+Fi(10.2.)52 b(The)23 b Fl(client)f Fi(module)j(\227)e(sending)h(SO)n
+(AP)g(messages)1737 b(41)p eop end
+%%Page: 42 48
+TeXDict begin 42 47 bop 208 83 a Fn(Returns)20 b(the)g(unparsed)e
+(message)i(body)-5 b(.)0 230 y Fj(ReceiveSoap)p Fl(\(\))208
+330 y Fn(Returns)24 b(a)i Fl(ParsedSOAP)d Fn(object)i(containing)e(the)
+i(parsed)f(message.)39 b(Raises)26 b(a)f Fl(TypeError)f
+Fn(if)h(the)g(message)g(w)o(asn')o(t)208 429 y(SO)m(AP)-9
+b(.)0 576 y Fj(ReceiveFault)p Fl(\(\))208 676 y Fn(Returns)28
+b(a)g Fl(Fault)g Fn(object)g(containing)f(the)h(SO)m(AP)h(f)o(ault)f
+(message.)49 b(Raises)30 b(a)f Fl(TypeError)e Fn(if)h(the)h(message)f
+(did)g(not)208 775 y(contain)19 b(a)h(f)o(ault.)0 922
+y Fj(Receive)p Fl(\()p Fk(r)m(eplytype=None)p Fl(\))208
+1022 y Fn(P)o(arses)29 b(a)g(SO)m(AP)h(message.)50 b(The)28
+b Fl(replytype)g Fn(speci\002es)h(ho)n(w)f(to)h(parse)g(the)g(data.)50
+b(If)29 b(it)g(s)h Fl(None)p Fn(,)g(dynamic)e(parsing)208
+1121 y(will)c(be)g(used,)g(usually)f(resulting)f(in)i(a)g(Python)f
+(list.)36 b(If)24 b Fl(replytype)e Fn(is)j(a)f(Python)e(class,)k(then)d
+(the)h(class')-5 b(s)24 b Fl(typecode)208 1221 y Fn(attrib)n(ute)19
+b(will)i(be)f(used,)g(otherwise)g Fl(replytype)f Fn(is)i(tak)o(en)f(to)
+g(be)g(the)g(typecode)f(to)h(use)h(for)e(parsing)g(the)h(data.)0
+1368 y(Once)f(a)g(reply)g(has)g(been)f(parsed)h(\(or)f(its)i(type)f(e)o
+(xamined\),)e(the)i(follo)n(wing)f(read-only)e(attrib)n(utes)j(are)g(a)
+n(v)n(ailable.)25 b(Their)18 b(v)n(alues)h(will)0 1468
+y(remain)g(unchanged)f(until)i(another)e(reply)i(is)h(parsed.)0
+1614 y Fj(reply_code)208 1714 y Fn(The)e(HTTP)i(reply)e(code,)g(a)i
+(number)-5 b(.)0 1861 y Fj(reply_headers)208 1961 y Fn(The)19
+b(HTTP)i(headers,)e(as)i(a)f Fl(mimetools)f Fn(object.)0
+2107 y Fj(reply_msg)208 2207 y Fn(A)h(te)o(xt)g(string)g(containing)e
+(the)j(HTTP)f(reply)f(te)o(xt.)0 2492 y Ff(10.2.2)101
+b(Binding)0 2695 y Fn(If)24 b(an)h(attrib)n(ute)f(is)h(fetched)e(other)
+h(than)g(one)g(of)g(those)g(described)g(in)g Fl(_Binding)p
+Fn(,)h(it)g(is)g(tak)o(en)f(to)h(be)f(the)h Fl(opname)e
+Fn(of)i(a)f(remote)0 2794 y(procedure,)k(and)f(a)i(callable)f(object)g
+(is)h(returned.)47 b(This)29 b(object)f(dynamically)e(parses)i(its)h
+(ar)o(guments,)f(recei)n(v)o(es)g(the)g(reply)-5 b(,)29
+b(and)0 2894 y(parses)20 b(that.)0 3041 y Fo(class)h
+Fj(Binding)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m(ds)12
+b Fc(])p Fl(\))208 3141 y Fn(F)o(or)19 b(other)h(k)o(e)o(yw)o(ord)e(ar)
+o(guments)g(see)j Fl(_Binding)p Fn(.)719 3256 y Fo(K)n(eyw)o(ord)p
+1315 3286 4 100 v 321 w(Default)p 1677 3286 V 99 w(Description)p
+669 3289 2771 4 v 719 3359 a Fl(typesmodule)p 1315 3389
+4 100 v 129 w(None)p 1677 3389 V 131 w Fn(See)f(e)o(xplanation)e(in)i
+(Dispatching)0 3500 y Fj(opname)p Fl(\()p Fk(*ar)m(gs)p
+Fl(\))208 3599 y Fn(Using)g(this)g(shortcut)g(requires)f(that)h(the)g
+Fk(url)h Fn(attrib)n(ute)f(is)h(set,)g(either)e(throught)g(the)h
+(constructor)e(or)i Fl(SetURL\(\))p Fn(.)0 3884 y Ff(10.2.3)101
+b(NamedP)l(ar)o(amBinding)0 4087 y Fn(If)24 b(an)h(attrib)n(ute)f(is)h
+(fetched)e(other)h(than)g(one)g(of)g(those)g(described)g(in)g
+Fl(_Binding)p Fn(,)h(it)g(is)g(tak)o(en)f(to)h(be)f(the)h
+Fl(opname)e Fn(of)i(a)f(remote)0 4187 y(procedure,)k(and)f(a)i
+(callable)f(object)g(is)h(returned.)47 b(This)29 b(object)f
+(dynamically)e(parses)i(its)h(ar)o(guments,)f(recei)n(v)o(es)g(the)g
+(reply)-5 b(,)29 b(and)0 4286 y(parses)20 b(that.)0 4433
+y Fo(class)h Fj(NamedParamBinding)p Fl(\()p Fc([)p Fk(**k)o(e)n(ywor)m
+(ds)12 b Fc(])p Fl(\))208 4533 y Fn(F)o(or)19 b(other)h(k)o(e)o(yw)o
+(ord)e(ar)o(guments)g(see)j Fl(_Binding)p Fn(.)719 4648
+y Fo(K)n(eyw)o(ord)p 1315 4678 V 321 w(Default)p 1677
+4678 V 99 w(Description)p 669 4681 2771 4 v 719 4751
+a Fl(typesmodule)p 1315 4781 4 100 v 129 w(None)p 1677
+4781 V 131 w Fn(See)f(e)o(xplanation)e(in)i(Dispatching)0
+4893 y Fj(opname)p Fl(\()p Fk(**kwar)m(gs)p Fl(\))208
+4993 y Fn(Using)g(this)g(shortcut)g(requires)f(that)h(the)g
+Fk(url)h Fn(attrib)n(ute)f(is)h(set,)g(either)e(throught)g(the)h
+(constructor)e(or)i Fl(SetURL\(\))p Fn(.)p 0 5549 3901
+4 v 0 5649 a Fi(42)2399 b(Chapter)23 b(10.)52 b(Dispatching)24
+b(and)g(In)n(v)n(oking)p eop end
+%%Page: 43 49
+TeXDict begin 43 48 bop 0 83 3901 9 v 3503 230 a Fi(CHAPTER)3422
+427 y Fh(ELEVEN)p 0 515 V 2778 978 a Ft(Bib)l(liog)n(r)n(aph)-6
+b(y)p 0 5549 3901 4 v 3808 5649 a Fi(43)p eop end
+%%Page: 44 50
+TeXDict begin 44 49 bop 0 5549 3901 4 v 0 5649 a Fi(44)p
+eop end
+%%Page: 45 51
+TeXDict begin 45 50 bop 2362 794 a Ft(BIBLIOGRAPHY)42
+1605 y Fn([1])40 b(This)20 b(is)h(the)g(\002rst)f(item)h(in)f(the)g
+(Bibliography)-5 b(.)42 1772 y([2])40 b(This)20 b(is)h(the)g(second)e
+(item)h(in)h(the)f(Bibliography)-5 b(.)p 0 5549 3901
+4 v 3808 5649 a Fi(45)p eop end
+%%Page: 46 52
+TeXDict begin 46 51 bop 0 5549 3901 4 v 0 5649 a Fi(46)p
+eop end
+%%Page: 47 53
+TeXDict begin 47 52 bop 0 83 3901 9 v 3480 230 a Fi(APPENDIX)3814
+427 y Fh(A)p 0 515 V 2402 978 a Ft(CGI)57 b(Scr)s(ipt)h(Arr)n(a)-6
+b(y)0 1506 y Fg(A.1)120 b(Intro)0 1739 y Fn(This)19 b(is)h(an)e(e)o
+(xample)g(of)g(a)h(simple)g(web)g(service)f(CGI)h(Script.)25
+b(The)18 b(service)h(returns)e(and)i(e)o(xpects)f(SO)m(AP)h(Arrays)g
+(\(p)o(ython)d Fl(list)p Fn(\).)0 1839 y(A)21 b(sample)f(soap)g(trace)g
+(is)h(pro)o(vided)c(belo)n(w)-5 b(.)24 b(In)c(this)h(e)o(xample)e(the)h
+(CGI)h(script)f(is)h(dispatched)e(as)i(a)f Fk(rpc)h Fn(service.)0
+2124 y Ff(A.1.1)100 b(r)s(pc)28 b(wr)o(apper)0 2327 y
+Fn(The)f(wrapper)e(element)h(of)h(the)g(request)f(is)i(the)f(dispatch)g
+(k)o(e)o(y)f(to)h(the)g(callback)f(function,)h(the)g(child)f(elements)h
+(are)g(passes)g(as)h(a)0 2426 y Fl(list)e Fn(or)f Fl(dict)h
+Fn(of)g(v)n(alues)f(to)h(the)g(callback)f(function.)40
+b(The)26 b(callback)f(function)f(is)j(e)o(xpected)d(to)i(return)f(a)h
+Fl(list)g Fn(or)f Fl(dict)h Fn(of)0 2526 y(v)n(alues,)20
+b(the)g(response)f(wrapper)g(is)i(by)f(def)o(ault)f(set)i(to)f(the)h
+(request)e(wrapper)g(name)g(appended)f Fk(Response)p
+Fn(.)0 2853 y Fg(A.2)120 b(CGI)34 b(Scr)r(ipt)0 3178
+y Fe(#!/usr/local/bin/python2.4)0 3269 y(#)45 b(SOAP)f(Array)0
+3451 y(def)g(hello\(\):)179 3543 y(return)g(["Hello,)g(world"])0
+3725 y(def)g(echo\()404 3738 y(*)449 3725 y(args\):)179
+3817 y(return)g(args)0 3999 y(def)g(sum\()359 4012 y(*)404
+3999 y(args\):)179 4091 y(sum)h(=)f(0)179 4182 y(for)h(i)f(in)h(args:)f
+(sum)g(+=)h(i)179 4273 y(return)f([sum])0 4456 y(def)g(average\()539
+4469 y(*)584 4456 y(args\):)179 4547 y(return)g([sum\()718
+4560 y(*)763 4547 y(args\))f(/)i(len\(args\)])0 4730
+y(from)f(ZSI)g(import)g(dispatch)0 4821 y(dispatch.AsCGI\(rpc=True\))p
+0 5549 3901 4 v 3808 5649 a Fi(47)p eop end
+%%Page: 48 54
+TeXDict begin 48 53 bop 0 86 a Fg(A.3)120 b(client)33
+b(test)h(scr)r(ipt)0 410 y Fe(#!/usr/bin/env)42 b(python)0
+502 y(#)j(client.py)0 593 y(import)f(sys)0 684 y(from)g(ZSI.client)f
+(import)h(Binding)0 776 y(b)h(=)f
+(Binding\(url='http://127.0.0.1/cgi-bin/sim)o(ple',)38
+b(tracefile=sys.stdout\))0 867 y(print)44 b(b.hello\(\))0
+958 y(try:)179 1050 y(print)g(b.hello\(1\))0 1141 y(except)g
+(Exception,)f(ex:)179 1232 y(print)h("Fault:)g(",)g(ex)0
+1415 y(print)g(b.echo\("whatever",)e("hi",)i(1,)g(2\))0
+1506 y(print)g(b.sum\()539 1519 y(*)584 1506 y([2)674
+1519 y(*)719 1506 y(i)f(for)h(i)h(in)f(range\(5\)]\))0
+1598 y(print)g(b.average\()719 1611 y(*)764 1598 y([2)854
+1611 y(*)899 1598 y(i)e(for)j(i)f(in)h(range\(5\)]\))0
+2046 y Fg(A.4)120 b(SO)n(AP)32 b(T)-14 b(r)o(ace)0 2296
+y Ff(A.4.1)100 b(hello)0 2590 y Fe($)45 b(./client.py)0
+2681 y(Hello:)89 b(_________________________________)39
+b(Wed)44 b(Oct)89 b(4)45 b(17:36:33)f(2006)g(REQUEST:)0
+2773 y(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap)o
+(.org/s)o(oap/en)o(coding)o(/")0 2864 y
+(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)o(elope/)o
+(")0 2955 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+3047 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+3138 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e")0
+3229 y(SOAP-ENV:encodingStyle="http://schemas.)o(xmlsoa)o(p.org/)o
+(soap/e)o(ncodin)o(g/">)0 3321 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0
+3412 y(<SOAP-ENV:Body>)0 3503 y(<hello)i
+(SOAP-ENC:arrayType="xsd:anyType[0]"></)o(hello>)0 3595
+y(</SOAP-ENV:Body>)0 3686 y(</SOAP-ENV:Envelope>)0 3869
+y(_________________________________)39 b(Wed)45 b(Oct)89
+b(4)44 b(17:36:34)g(2006)g(RESPONSE:)0 3960 y(200)0 4143
+y(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap)o(.org/s)
+o(oap/en)o(coding)o(/")0 4234 y
+(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)o(elope/)o
+(")0 4325 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+4417 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+4508 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e">)
+0 4599 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0 4691 y(<SOAP-ENV:Body>)0
+4782 y(<helloResponse)g(SOAP-ENC:arrayType="xsd:anyType[1]">)0
+4873 y(<element)h(id="o671b0")g(xsi:type="xsd:string">Hello,)e
+(world</element>)0 4965 y(</helloResponse>)0 5056 y(</SOAP-ENV:Body>)0
+5147 y(</SOAP-ENV:Envelope>)0 5239 y([u'Hello,)i(world'])p
+0 5549 3901 4 v 0 5649 a Fi(48)2712 b(Appendix)24 b(A.)47
+b(CGI)22 b(Scr)q(ipt)i(Arr)o(a)n(y)p eop end
+%%Page: 49 55
+TeXDict begin 49 54 bop 0 83 a Ff(A.4.2)100 b(hello)29
+b(f)m(ault)0 377 y Fe(_________________________________)39
+b(Wed)45 b(Oct)89 b(4)44 b(17:36:34)g(2006)g(REQUEST:)0
+469 y(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap)o
+(.org/s)o(oap/en)o(coding)o(/")0 560 y
+(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)o(elope/)o
+(")0 651 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+743 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+834 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e")0
+925 y(SOAP-ENV:encodingStyle="http://schemas.)o(xmlsoa)o(p.org/)o
+(soap/e)o(ncodin)o(g/">)0 1017 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0
+1108 y(<SOAP-ENV:Body>)0 1199 y(<hello)i
+(SOAP-ENC:arrayType="xsd:anyType[1]">)0 1290 y(<element)f
+(id="o1803988")g(xsi:type="xsd:int">1</element>)0 1382
+y(</hello>)0 1473 y(</SOAP-ENV:Body>)0 1564 y(</SOAP-ENV:Envelope>)0
+1656 y(_________________________________)c(Wed)45 b(Oct)89
+b(4)44 b(17:36:35)g(2006)g(RESPONSE:)0 1747 y(500)0 1930
+y(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap)o(.org/s)
+o(oap/en)o(coding)o(/")0 2021 y
+(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)o(elope/)o
+(")0 2112 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+2204 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+2295 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e">)
+0 2386 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0 2478 y(<SOAP-ENV:Body>)0
+2569 y(<SOAP-ENV:Fault>)0 2660 y
+(<faultcode>SOAP-ENV:Server</faultcode>)0 2752 y
+(<faultstring>Processing)f(Failure</faultstring>)0 2843
+y(<detail>)0 2934 y(<ZSI:FaultDetail>)0 3026 y
+(<ZSI:string>exceptions:TypeError)e(hello\(\))44 b(takes)g(no)h
+(arguments)e(\(1)h(given\)</ZSI:string>)0 3117 y
+(<ZSI:trace>build/bdist.darwin-8.8.0-Pow)o(er_Mac)o(intosh)o(/egg/Z)o
+(SI/dis)o(patch.)o(py:86:)o(_Dispa)o(tch</Z)o(SI:tra)o(ce>)0
+3208 y(</ZSI:FaultDetail>)0 3300 y(</detail>)0 3391 y
+(</SOAP-ENV:Fault>)0 3482 y(</SOAP-ENV:Body>)0 3574 y
+(</SOAP-ENV:Envelope>)0 3756 y(Fault:)89 b(Processing)43
+b(Failure)0 3848 y(exceptions:TypeError)0 3939 y(hello\(\))h(takes)g
+(no)g(arguments)f(\(1)i(given\))0 4030 y([trace:)f
+(build/bdist.darwin-8.8.0-Power_Macint)o(osh/eg)o(g/ZSI/)o(dispat)o
+(ch.py:)o(86:_Di)o(spatch)o(])p 0 5549 3901 4 v 0 5649
+a Fi(A.4.)52 b(SO)n(AP)24 b(T)-10 b(r)o(ace)3155 b(49)p
+eop end
+%%Page: 50 56
+TeXDict begin 50 55 bop 0 83 a Ff(A.4.3)100 b(echo)0
+377 y Fe(_________________________________)39 b(Wed)45
+b(Oct)89 b(4)44 b(17:36:35)g(2006)g(REQUEST:)0 469 y
+(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap)o(.org/s)o
+(oap/en)o(coding)o(/")0 560 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o
+(org/so)o(ap/env)o(elope/)o(")0 651 y
+(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+743 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+834 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e")0
+925 y(SOAP-ENV:encodingStyle="http://schemas.)o(xmlsoa)o(p.org/)o
+(soap/e)o(ncodin)o(g/">)0 1017 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0
+1108 y(<SOAP-ENV:Body>)0 1199 y(<echo)i
+(SOAP-ENC:arrayType="xsd:anyType[4]">)0 1290 y(<element)f(id="o644c0")g
+(xsi:type="xsd:string">whatever</element>)0 1382 y(<element)g
+(id="o644e0")g(xsi:type="xsd:string">hi</element>)0 1473
+y(<element)g(id="o1803988")g(xsi:type="xsd:int">1</element>)0
+1564 y(<element)g(id="o180397c")g(xsi:type="xsd:int">2</element>)0
+1656 y(</echo>)0 1747 y(</SOAP-ENV:Body>)0 1838 y(</SOAP-ENV:Envelope>)
+0 1930 y(_________________________________)c(Wed)45 b(Oct)89
+b(4)44 b(17:36:36)g(2006)g(RESPONSE:)0 2021 y(200)0 2204
+y(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap)o(.org/s)
+o(oap/en)o(coding)o(/")0 2295 y
+(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)o(elope/)o
+(")0 2386 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+2478 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+2569 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e">)
+0 2660 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0 2752 y(<SOAP-ENV:Body>)0
+2843 y(<echoResponse)h(SOAP-ENC:arrayType="xsd:anyType[4]">)0
+2934 y(<element)g(id="o4f4290")g
+(xsi:type="xsd:string">whatever</element>)0 3026 y(<element)g
+(id="o4f4338")g(xsi:type="xsd:string">hi</element>)0
+3117 y(<element)g(id="o1803988")g(xsi:type="xsd:int">1</element>)0
+3208 y(<element)g(id="o180397c")g(xsi:type="xsd:int">2</element>)0
+3300 y(</echoResponse>)0 3391 y(</SOAP-ENV:Body>)0 3482
+y(</SOAP-ENV:Envelope>)0 3665 y([u'whatever',)g(u'hi',)g(1,)i(2])p
+0 5549 3901 4 v 0 5649 a Fi(50)2712 b(Appendix)24 b(A.)47
+b(CGI)22 b(Scr)q(ipt)i(Arr)o(a)n(y)p eop end
+%%Page: 51 57
+TeXDict begin 51 56 bop 0 83 a Ff(A.4.4)100 b(sum)0 377
+y Fe(_________________________________)39 b(Wed)45 b(Oct)89
+b(4)44 b(17:36:36)g(2006)g(REQUEST:)0 469 y(<SOAP-ENV:Envelope)e
+(xmlns:SOAP-ENC="http://schemas.xmlsoap)o(.org/s)o(oap/en)o(coding)o
+(/")0 560 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)o
+(elope/)o(")0 651 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o
+(")0 743 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+834 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e")0
+925 y(SOAP-ENV:encodingStyle="http://schemas.)o(xmlsoa)o(p.org/)o
+(soap/e)o(ncodin)o(g/">)0 1017 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0
+1108 y(<SOAP-ENV:Body>)0 1199 y(<sum)i
+(SOAP-ENC:arrayType="xsd:anyType[5]">)0 1290 y(<element)f
+(id="o1803994")g(xsi:type="xsd:int">0</element>)0 1382
+y(<element)g(id="o180397c")g(xsi:type="xsd:int">2</element>)0
+1473 y(<element)g(id="o1803964")g(xsi:type="xsd:int">4</element>)0
+1564 y(<element)g(id="o180394c")g(xsi:type="xsd:int">6</element>)0
+1656 y(<element)g(id="o1803934")g(xsi:type="xsd:int">8</element>)0
+1747 y(</sum>)0 1838 y(</SOAP-ENV:Body>)0 1930 y(</SOAP-ENV:Envelope>)0
+2021 y(_________________________________)c(Wed)45 b(Oct)89
+b(4)44 b(17:36:37)g(2006)g(RESPONSE:)0 2112 y(200)0 2295
+y(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap)o(.org/s)
+o(oap/en)o(coding)o(/")0 2386 y
+(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)o(elope/)o
+(")0 2478 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+2569 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+2660 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e">)
+0 2752 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0 2843 y(<SOAP-ENV:Body>)0
+2934 y(<sumResponse)h(SOAP-ENC:arrayType="xsd:anyType[1]">)0
+3026 y(<element)g(id="o18038a4")g(xsi:type="xsd:int">20</element>)0
+3117 y(</sumResponse>)0 3208 y(</SOAP-ENV:Body>)0 3300
+y(</SOAP-ENV:Envelope>)0 3391 y([20])p 0 5549 3901 4
+v 0 5649 a Fi(A.4.)52 b(SO)n(AP)24 b(T)-10 b(r)o(ace)3155
+b(51)p eop end
+%%Page: 52 58
+TeXDict begin 52 57 bop 0 83 a Ff(A.4.5)100 b(a)n(v)n(er)o(age)0
+377 y Fe(_________________________________)39 b(Wed)45
+b(Oct)89 b(4)44 b(17:36:37)g(2006)g(REQUEST:)0 469 y
+(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap)o(.org/s)o
+(oap/en)o(coding)o(/")0 560 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o
+(org/so)o(ap/env)o(elope/)o(")0 651 y
+(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+743 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+834 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e")0
+925 y(SOAP-ENV:encodingStyle="http://schemas.)o(xmlsoa)o(p.org/)o
+(soap/e)o(ncodin)o(g/">)0 1017 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0
+1108 y(<SOAP-ENV:Body>)0 1199 y(<average)h
+(SOAP-ENC:arrayType="xsd:anyType[5]">)0 1290 y(<element)g
+(id="o1803994")g(xsi:type="xsd:int">0</element>)0 1382
+y(<element)g(id="o180397c")g(xsi:type="xsd:int">2</element>)0
+1473 y(<element)g(id="o1803964")g(xsi:type="xsd:int">4</element>)0
+1564 y(<element)g(id="o180394c")g(xsi:type="xsd:int">6</element>)0
+1656 y(`<element)g(id="o1803934")g(xsi:type="xsd:int">8</element>)0
+1747 y(</average>)0 1838 y(</SOAP-ENV:Body>)0 1930 y
+(</SOAP-ENV:Envelope>)0 2021 y(_________________________________)c(Wed)
+45 b(Oct)89 b(4)44 b(17:36:38)g(2006)g(RESPONSE:)0 2112
+y(200)0 2295 y(<SOAP-ENV:Envelope)e
+(xmlns:SOAP-ENC="http://schemas.xmlsoap)o(.org/s)o(oap/en)o(coding)o
+(/")0 2386 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)
+o(elope/)o(")0 2478 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)
+o(")0 2569 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+2660 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e">)
+0 2752 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0 2843 y(<SOAP-ENV:Body>)0
+2934 y(<averageResponse)g(SOAP-ENC:arrayType="xsd:anyType[1]">)0
+3026 y(<element)h(id="o1803964")g(xsi:type="xsd:int">4</element>)0
+3117 y(</averageResponse>)0 3208 y(</SOAP-ENV:Body>)0
+3300 y(</SOAP-ENV:Envelope>)0 3391 y([4])p 0 5549 3901
+4 v 0 5649 a Fi(52)2712 b(Appendix)24 b(A.)47 b(CGI)22
+b(Scr)q(ipt)i(Arr)o(a)n(y)p eop end
+%%Page: 53 59
+TeXDict begin 53 58 bop 0 83 3901 9 v 3480 230 a Fi(APPENDIX)3814
+427 y Fh(B)p 0 515 V 2344 978 a Ft(CGI)58 b(Scr)s(ipt)f(Str)s(uct)0
+1506 y Fg(B)n(.1)120 b(Intro)0 1739 y Fn(This)19 b(is)g(an)g(e)o
+(xample)e(of)h(a)h(simple)f(web)h(service)f(CGI)h(Script.)24
+b(The)18 b(service)g(returns)g(and)g(e)o(xpects)g(SO)m(AP)h(Structs)g
+(\(p)o(ython)d Fl(dict)p Fn(\).)0 1839 y(A)21 b(sample)f(soap)g(trace)g
+(is)h(pro)o(vided)c(belo)n(w)-5 b(.)24 b(In)c(this)h(e)o(xample)e(the)h
+(CGI)h(script)f(is)h(dispatched)e(as)i(a)f Fk(rpc)h Fn(service.)0
+2124 y Ff(B)n(.1.1)100 b(r)s(pc)28 b(wr)o(apper)0 2327
+y Fn(The)f(wrapper)e(element)h(of)h(the)g(request)f(is)i(the)f
+(dispatch)g(k)o(e)o(y)f(to)h(the)g(callback)f(function,)h(the)g(child)f
+(elements)h(are)g(passes)g(as)h(a)0 2426 y Fl(list)e
+Fn(or)f Fl(dict)h Fn(of)g(v)n(alues)f(to)h(the)g(callback)f(function.)
+40 b(The)26 b(callback)f(function)f(is)j(e)o(xpected)d(to)i(return)f(a)
+h Fl(list)g Fn(or)f Fl(dict)h Fn(of)0 2526 y(v)n(alues,)20
+b(the)g(response)f(wrapper)g(is)i(by)f(def)o(ault)f(set)i(to)f(the)h
+(request)e(wrapper)g(name)g(appended)f Fk(Response)p
+Fn(.)0 2853 y Fg(B)n(.2)120 b(CGI)33 b(Scr)r(ipt)0 3178
+y Fe(#!/usr/local/bin/python2.4)0 3269 y(#)45 b(SOAP)f(Struct)0
+3451 y(def)g(hello\(\):)179 3543 y(return)g({"value":"Hello,)e(world"})
+0 3725 y(def)i(echo\()404 3738 y(**)494 3725 y(kw\):)179
+3817 y(return)g(kw)0 3999 y(def)g(sum\()359 4012 y(**)449
+3999 y(kw\):)179 4091 y(sum)h(=)f(0)179 4182 y(for)h(i)f(in)h
+(kw.values\(\):)e(sum)h(+=)g(i)0 4273 y(return)g({"value":sum})0
+4456 y(def)g(average\()539 4469 y(**)629 4456 y(kw\):)0
+4547 y(d)h(=)f(sum\()359 4560 y(**)449 4547 y(kw\))179
+4639 y(return)g(d["value"])f(=)i(d["value"]/len\(kw\))0
+4821 y(from)f(ZSI)g(import)g(dispatch)0 4913 y
+(dispatch.AsCGI\(rpc=True\))p 0 5549 3901 4 v 3808 5649
+a Fi(53)p eop end
+%%Page: 54 60
+TeXDict begin 54 59 bop 0 86 a Fg(B)n(.3)120 b(client)33
+b(test)h(scr)r(ipt)0 502 y Fe(#!/usr/bin/env)42 b(python)0
+593 y(import)i(sys,time)0 684 y(from)g(ZSI.client)f(import)h
+(NamedParamBinding)e(as)j(NPBinding)0 867 y(b)g(=)f
+(NPBinding\(url='http://127.0.0.1/cgi-bin/s)o(oapstr)o(uct',)38
+b(tracefile=sys.stdout\))0 958 y(print)44 b("Hello:)g(",)g(b.hello\(\))
+0 1050 y(print)g("Echo:)g(",)g(b.echo\(name="josh",)e(year=2006,)h
+(pi=3.14,)h(time=time.gmtime\(\)\))0 1141 y(print)g("Sum:)g(",)g
+(b.sum\(one=1,)f(two=2,)h(three=3\))0 1232 y(print)g("Average:)f(",)i
+(b.average\(one=100,)d(two=200,)h(three=300,)g(four=400\))0
+1773 y Fg(B)n(.4)120 b(SO)n(AP)32 b(T)-14 b(r)o(ace)0
+2022 y Ff(B)n(.4.1)100 b(hello)p 0 5549 3901 4 v 0 5649
+a Fi(54)2691 b(Appendix)24 b(B)n(.)47 b(CGI)22 b(Scr)q(ipt)i(Str)q(uct)
+p eop end
+%%Page: 55 61
+TeXDict begin 55 60 bop 0 83 3901 9 v 3480 230 a Fi(APPENDIX)3814
+427 y Fh(C)p 0 515 V 1182 978 a Ft(Complete)58 b(Lo)m(w)f(Le)-6
+b(v)h(el)56 b(Example)0 1506 y Fg(C)l(.1)121 b(Intro)0
+1739 y Fn(This)20 b(is)h(a)g(complete)e(e)o(xample)g(of)h(using)g(the)g
+(lo)n(w)g(le)n(v)o(el)g(soap)g(utilities)h(in)f Fl(ZSI)g
+Fn(to)h(implement)d(a)j(web)f(service.)0 2067 y Fg(C)l(.2)121
+b(code)0 2316 y Ff(C)m(.2.1)100 b(httpser)s(v)n(er)28
+b(scr)q(ipt)0 2519 y Fn(Minimal)20 b(http)g(serv)o(er)f(e)o(xample,)g
+(opens)g(up)h(a)g(ne)n(w)g(process)g(to)g(do)g(the)g(SO)m(AP)i
+(processing.)236 2757 y Fe(#!/usr/bin/env)43 b(python)236
+2849 y(#)i(file:)f(httpserver.py)236 2940 y(import)g(os)236
+3031 y(from)g(subprocess)g(import)f(Popen,)h(PIPE)236
+3123 y(from)g(BaseHTTPServer)f(import)h(BaseHTTPRequestHandler,)d
+(HTTPServer)236 3305 y(class)j
+(RequestHandler\(BaseHTTPRequestHandler\):)416 3396 y(def)g
+(do_POST\(self\):)595 3488 y(length)g(=)g
+(int\(self.headers['content-length']\))595 3579 y(xml_in)g(=)g
+(self.rfile.read\(length\))595 3670 y(p)h(=)f
+(Popen\(os.path.join\(os.path.curdir,)39 b('player.py'\),)1043
+3762 y(shell=True,)k(stdin=PIPE,)g(stdout=PIPE\))595
+3944 y(\(stdout,)g(stderr\))h(=)h(p.communicate\(xml_in\))595
+4036 y(code)f(=)h(200)595 4127 y(if)f(stdout.find\('Fault'\))e(>=)i(0:)
+h(code)f(=)h(500)595 4218 y(self.send_response\(code\))595
+4310 y(self.send_header\('Content-type',)39 b('text/xml;)k
+(charset="utf-8"'\))595 4401 y(self.send_header\('Content-Length',)c
+(str\(len\(stdout\)\)\))595 4492 y(self.end_headers\(\))595
+4584 y(self.wfile.write\(stdout\))595 4675 y(self.wfile.flush\(\))236
+4858 y(if)45 b(__name__)e(==)i('__main__':)416 4949 y(server)e(=)i
+(HTTPServer\(\('localhost',)c(8080\),)j(RequestHandler\))416
+5040 y(server.serve_forever\(\))p 0 5549 3901 4 v 3808
+5649 a Fi(55)p eop end
+%%Page: 56 62
+TeXDict begin 56 61 bop 0 83 a Ff(C)m(.2.2)100 b(typecode)29
+b(module)0 377 y Fe(#)45 b(file:)f(typecode.py)0 469
+y(#)h(CHECK)f(PYTHONPATH:)f(Must)h(be)g(able)g(to)h(import)0
+560 y(class)f(Player:)179 651 y(def)h(__init__\(self,)1031
+664 y(*)1076 651 y(args\):)359 743 y(if)f(not)g(len\(args\):)g(return)
+359 834 y(self.Name)f(=)i(args[0])359 925 y(self.Scores)e(=)h(args[1:])
+0 1017 y(Player.typecode)e(=)j(TC.Struct\(Player,)d([)1435
+1108 y(TC.String\('Name'\),)1435 1199 y(TC.Array\('Integer',)f
+(TC.Integer\(\),)i('Scores',)g(undeclared=True\),)1435
+1290 y(],)h('GetAverage'\))0 1382 y(class)g(Average:)179
+1473 y(def)h(__init__\(self,)d(average=None\):)359 1564
+y(self.average)g(=)j(average)0 1656 y(Average.typecode)d(=)j
+(TC.Struct\(Average,)d([)1435 1747 y(TC.Integer\('average'\),)1435
+1838 y(],)i('GetAverageResponse'\))p 0 5549 3901 4 v
+0 5649 a Fi(56)2220 b(Appendix)24 b(C)n(.)46 b(Complete)24
+b(Lo)o(w)f(Le)n(v)n(el)g(Example)p eop end
+%%Page: 57 63
+TeXDict begin 57 62 bop 0 83 a Ff(C)m(.2.3)100 b(pla)m(y)n(er)29
+b(scr)q(ipt)0 377 y Fe(#!/usr/bin/env)42 b(python)0 469
+y(#)j(file:)f(player.py)0 560 y(from)g(ZSI)g(import)717
+573 y(*)0 651 y(import)g(sys)0 743 y(IN,)g(OUT)h(=)f(sys.stdin,)f
+(sys.stdout)0 834 y(try:)179 925 y(ps)i(=)f(ParsedSoap\(IN\))0
+1017 y(except)g(ParseException,)e(e:)179 1108 y
+(OUT.write\(FaultFromZSIException\(e\).AsSOAP)o(\(\)\))179
+1199 y(sys.exit\(1\))0 1290 y(except)i(Exception,)f(e:)179
+1382 y(#)i(Faulted)f(while)g(processing;)f(we)h(assume)g(it's)g(in)h
+(the)f(header.)179 1473 y(OUT.write\(FaultFromException\(e,)c
+(1\).AsSOAP\(\)\))179 1564 y(sys.exit\(1\))0 1747 y(#)45
+b(We)f(are)g(not)h(prepared)e(to)i(handle)f(any)g(actors)g(or)g
+(mustUnderstand)f(elements,)0 1838 y(#)i(so)f(we'll)g(arbitrarily)f
+(fault)h(back)g(with)g(the)h(first)f(one)g(we)h(found.)0
+1930 y(a)g(=)f(ps.WhatActorsArePresent\(\))0 2021 y(if)g(len\(a\):)179
+2112 y(OUT.write\(FaultFromActor\(a[0]\).AsSOAP\(\)\))179
+2204 y(sys.exit\(1\))0 2295 y(mu)g(=)h(ps.WhatMustIUnderstand\(\))0
+2386 y(if)f(len\(mu\):)179 2478 y(uri,)g(localname)g(=)g(mu[0])179
+2569 y(OUT.write\(FaultFromNotUnderstood\(uri,)39 b
+(localname\).AsSOAP\(\)\))179 2660 y(sys.exit\(1\))0
+2843 y(from)44 b(typecode)g(import)f(Player,)h(Average)0
+2934 y(try:)179 3026 y(player)g(=)h(ps.Parse\(Player.typecode\))0
+3117 y(except)f(EvaluateException,)e(e:)179 3208 y
+(OUT.write\(FaultFromZSIException\(e\).AsSOAP)o(\(\)\))179
+3300 y(sys.exit\(1\))0 3482 y(try:)179 3574 y(total)i(=)h(0)179
+3665 y(for)g(value)f(in)g(player.Scores:)f(total)h(=)g(total)g(+)h
+(value)179 3756 y(result)f(=)h(Average\(total)d(/)j
+(len\(player.Scores\)\))179 3848 y(sw)g(=)f(SoapWriter\(\))179
+3939 y(sw.serialize\(result,)e(Average.typecode\))179
+4030 y(sw.close\(\))179 4122 y(OUT.write\(str\(sw\)\))0
+4213 y(except)i(Exception,)f(e:)179 4304 y
+(OUT.write\(FaultFromException\(e,)d(0,)k
+(sys.exc_info\(\)[2]\).AsSOAP\(\)\))179 4396 y(sys.exit\(1\))p
+0 5549 3901 4 v 0 5649 a Fi(C)n(.2.)51 b(code)3427 b(57)p
+eop end
+%%Page: 58 64
+TeXDict begin 58 63 bop 0 83 a Ff(C)m(.2.4)100 b(client)28
+b(test)f(scr)q(ipt)0 377 y Fe(#!/usr/bin/env)42 b(python2.4)0
+469 y(#file:)i(client.py)0 560 y(from)g(ZSI)g(import)717
+573 y(*)0 651 y(from)g(ZSI.wstools.Namespaces)d(import)j(SCHEMA)0
+743 y(from)g(typecode)g(import)f(Player,)h(Average)0
+925 y(if)g(__name__)g(==)g('__main__':)179 1017 y(import)g(sys)179
+1108 y(from)g(ZSI.client)g(import)f(Binding)179 1199
+y(b)i(=)g(Binding\(url='http://localhost:8080',)38 b
+(tracefile=sys.stdout\))179 1290 y(pyobj)44 b(=)h(b.RPC\(None,)e(None,)
+h(Player\("Josh",10,20,30\),)d(replytype=Average\))179
+1382 y(print)j(pyobj)179 1473 y(print)g(pyobj.__dict__)p
+0 5549 3901 4 v 0 5649 a Fi(58)2220 b(Appendix)24 b(C)n(.)46
+b(Complete)24 b(Lo)o(w)f(Le)n(v)n(el)g(Example)p eop
+end
+%%Page: 59 65
+TeXDict begin 59 64 bop 0 88 a Fg(C)l(.3)121 b(SO)n(AP)32
+b(T)-14 b(r)o(ace)0 338 y Ff(C)m(.3.1)100 b(GetA)l(v)n(er)o(age)0
+632 y Fe($./client.py)0 723 y(_________________________________)39
+b(Thu)45 b(Oct)89 b(5)44 b(14:57:39)g(2006)g(REQUEST:)0
+814 y(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap)o
+(.org/s)o(oap/en)o(coding)o(/")0 906 y
+(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)o(elope/)o
+(")0 997 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+1088 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+1180 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e">)
+0 1271 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0 1362 y(<SOAP-ENV:Body>)0
+1454 y(<GetAverage>)0 1545 y(<Name)i(xsi:type="xsd:string">Josh</Name>)
+0 1636 y(<Scores>)0 1728 y(<element>10</element>)0 1819
+y(<element>20</element>)0 1910 y(<element>30</element>)0
+2002 y(</Scores>)0 2093 y(</GetAverage>)0 2184 y(</SOAP-ENV:Body>)0
+2276 y(</SOAP-ENV:Envelope>)0 2367 y(_________________________________)
+39 b(Thu)45 b(Oct)89 b(5)44 b(14:57:39)g(2006)g(RESPONSE:)0
+2458 y(200)0 2550 y(OK)0 2641 y(-------)0 2732 y(Server:)g
+(BaseHTTP/0.3)e(Python/2.5)0 2824 y(Date:)i(Thu,)g(05)h(Oct)f(2006)g
+(21:57:39)g(GMT)0 2915 y(Content-type:)f(text/xml;)g(charset="utf-8")0
+3006 y(Content-Length:)f(431)0 3189 y(<SOAP-ENV:Envelope)g
+(xmlns:SOAP-ENC="http://schemas.xmlsoap)o(.org/s)o(oap/en)o(coding)o
+(/")0 3280 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)
+o(elope/)o(")0 3371 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)
+o(")0 3463 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+3554 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e">)
+0 3645 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0 3737 y(<SOAP-ENV:Body>)0
+3828 y(<GetAverageResponse>)0 3919 y(<average>20</average>)0
+4011 y(</GetAverageResponse>)0 4102 y(</SOAP-ENV:Body>)0
+4193 y(</SOAP-ENV:Envelope>)0 4376 y(<__main__.Average)g(instance)h(at)
+i(0x5f9760>)0 4467 y({'average':)e(20})0 4892 y Ff(C)m(.3.2)100
+b(f)m(ault)0 5095 y Fn(Purposely)19 b(send)h(a)g(incorrect)f
+Fk(Nae)i Fn(element)e(for)h(the)g Fk(Name)p Fn(.)p 0
+5549 3901 4 v 0 5649 a Fi(C)n(.3.)51 b(SO)n(AP)25 b(T)-10
+b(r)o(ace)3152 b(59)p eop end
+%%Page: 60 66
+TeXDict begin 60 65 bop 236 174 a Fe($./client.py)236
+266 y(_________________________________)40 b(Thu)k(Oct)89
+b(5)45 b(14:33:25)e(2006)h(REQUEST:)236 357 y(<SOAP-ENV:Envelope)e
+(xmlns:SOAP-ENC="http://schemas.xmlsoap.)o(org/so)o(ap/enc)o(oding/)o
+(")236 448 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.o)o(rg/soa)o
+(p/enve)o(lope/")236 540 y(xmlns:ZSI="http://www.zolera.com/schemas)o
+(/ZSI/")236 631 y(xmlns:xsd="http://www.w3.org/2001/XMLSch)o(ema")236
+722 y(xmlns:xsi="http://www.w3.org/2001/XMLSch)o(ema-in)o(stance)o(">)
+236 814 y(<SOAP-ENV:Header></SOAP-ENV:Header>)236 905
+y(<SOAP-ENV:Body>)236 996 y(<GetAverage>)236 1088 y(<Nae)i
+(xsi:type="xsd:string">Josh</Nae>)236 1179 y(<Scores>)236
+1270 y(<element>10</element>)236 1362 y(<element>20</element>)236
+1453 y(<element>30</element>)236 1544 y(</Scores>)236
+1636 y(</GetAverage>)236 1727 y(</SOAP-ENV:Body>)236
+1818 y(</SOAP-ENV:Envelope>)236 2001 y
+(_________________________________)c(Thu)k(Oct)89 b(5)45
+b(14:33:26)e(2006)h(RESPONSE:)236 2092 y(500)236 2183
+y(Internal)g(Server)g(Error)236 2366 y(<SOAP-ENV:Envelope)e
+(xmlns:SOAP-ENC="http://schemas.xmlsoap.)o(org/so)o(ap/enc)o(oding/)o
+(")236 2457 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.o)o(rg/soa)o
+(p/enve)o(lope/")236 2549 y(xmlns:ZSI="http://www.zolera.com/schemas)o
+(/ZSI/")236 2640 y(xmlns:xsd="http://www.w3.org/2001/XMLSch)o(ema")236
+2731 y(xmlns:xsi="http://www.w3.org/2001/XMLSch)o(ema-in)o(stance)o(">)
+236 2823 y(<SOAP-ENV:Header></SOAP-ENV:Header>)236 2914
+y(<SOAP-ENV:Body>)236 3005 y(<SOAP-ENV:Fault>)236 3097
+y(<faultcode>SOAP-ENV:Client</faultcode>)236 3188 y
+(<faultstring>Unparseable)f(message</faultstring>)236
+3279 y(<detail><Eoe440>&lt;ZSI:ParseFaultDetail)o(&gt;)236
+3371 y(&lt;ZSI:string&gt;Element)g("Name")j(missing)f(from)h
+(complexType&lt;/ZSI:string&gt;)236 3462 y
+(&lt;ZSI:trace&gt;/SOAP-ENV:Envelope/SOAP)o(-ENV:B)o(ody/Ge)o(tAvera)o
+(ge&lt;)o(/ZSI:t)o(race&g)o(t;)236 3553 y
+(&lt;/ZSI:ParseFaultDetail&gt;</Eoe440></)o(detail)o(>)236
+3645 y(</SOAP-ENV:Fault>)236 3736 y(</SOAP-ENV:Body>)236
+3827 y(</SOAP-ENV:Envelope>)236 3919 y(Traceback)g(\(most)g(recent)f
+(call)i(last\):)326 4010 y(File)f("./player_client.py",)d(line)k(25,)f
+(in)g(?)416 4101 y(pyobj)g(=)g(b.RPC\(None,)f(None,)h
+(Player\("Josh",10,20,30\),)d(replytype=Average\))326
+4193 y(File)j("/private/var/www/htdocs/guide/client.p)o(y",)39
+b(line)44 b(176,)g(in)h(RPC)326 4375 y(File)f
+("/private/var/www/htdocs/guide/client.p)o(y",)39 b(line)44
+b(420,)g(in)h(Receive)236 4558 y(ZSI.FaultException:)d(Unparseable)h
+(message)236 4649 y(<Element)h(Node)g(at)g(5f9f58:)g(Name='Eoe440')f
+(with)h(0)g(attributes)g(and)g(1)h(children>)p 0 5549
+3901 4 v 0 5649 a Fi(60)2220 b(Appendix)24 b(C)n(.)46
+b(Complete)24 b(Lo)o(w)f(Le)n(v)n(el)g(Example)p eop
+end
+%%Page: 61 67
+TeXDict begin 61 66 bop 0 83 3901 9 v 3480 230 a Fi(APPENDIX)3814
+427 y Fh(D)p 0 515 V 2453 978 a Ft(Pic)l(kler)57 b(e)-6
+b(xample)0 1506 y Fg(D)e(.1)120 b(Intro)0 1739 y Fn(This)20
+b(is)h(an)g(e)o(xample)d(of)i(a)h(stateful)f(mod)p 1211
+1739 25 4 v 29 w(p)o(ython)e(web)i(service.)0 2067 y
+Fg(D)-8 b(.2)120 b(code)0 2316 y Ff(D)-7 b(.2.1)100 b(typecode)29
+b(module)0 2519 y Fn(Module)19 b(containing)f(comple)o(x)h(type)g
+(typecode.)236 2757 y Fe(#)45 b(Complex)f(type)g(definition)236
+2849 y(from)g(ZSI)h(import)954 2862 y(*)236 2940 y(class)f(Person:)326
+3031 y(def)g(__init__\(self,)f(name=None,)g(age=0\):)416
+3123 y(self.name)g(=)i(name)416 3214 y(self.age)e(=)i(age)236
+3396 y(Person.typecode)e(=)h(TC.Struct\(Person,)1492
+3488 y([TC.String\('name'\),)1536 3579 y
+(TC.InonNegativeInteger\('age'\)],)1492 3670 y
+(pname=\('urn:MyApp','Person'\)\))0 4094 y Ff(D)-7 b(.2.2)100
+b(pic)n(kler)28 b(scr)q(ipt)0 4297 y Fn(Con\002gure)19
+b(appache)g(to)h(use)g(this)h(script)f(with)h(mod)p 1531
+4297 V 28 w(p)o(ython)e(PythonHandler)-5 b(.)p 0 5549
+3901 4 v 3808 5649 a Fi(61)p eop end
+%%Page: 62 68
+TeXDict begin 62 67 bop 236 174 a Fe(#)45 b(pickler.py)236
+266 y(import)f(pickle,)g(new)236 357 y(from)g(mod_python)g(import)f
+(apache)236 448 y(from)h(ZSI)h(import)f(dispatch)236
+540 y(import)g(MyComplexTypes)236 722 y(#)h(my)f(web)h(service)e(that)i
+(returns)e(a)i(complex)e(structure)236 814 y(def)i
+(getPerson\(name=None\):)326 905 y(#fp)f(=)h
+(open\('/tmp/\045s.person.pickle'\045Person.na)o(me,)39
+b('r'\))326 996 y(fp)44 b(=)h
+(open\('/tmp/\045s.person.pickle'\045name,)39 b('r'\))326
+1088 y(#return)44 b(pickle.load\(fp\))326 1179 y(p)h(=)f
+(pickle.load\(fp\))326 1270 y(print)g("PERSON:)f(",)i(p)326
+1362 y(print)f("typecode:)f(",)i(p.typecode)326 1453
+y(return)f(p)236 1636 y(#)h(my)f(web)h(service)e(that)i(accepts)e(a)i
+(complex)e(structure)236 1727 y(def)i(savePerson\(Person\):)326
+1818 y(print)f("PERSON:)f(",)i(Person)326 1910 y(fp)f(=)h
+(open\('/tmp/\045s.person.pickle'\045Person.nam)o(e,)39
+b('w'\))326 2001 y(pickle.dump\(Person,)j(fp\))326 2092
+y(fp.close\(\))326 2183 y(return)i({})236 2366 y(mod)h(=)f
+(__import__\('encodings.utf_8',)c(globals\(\),)j(locals\(\),)h(')2837
+2379 y(*)2882 2366 y('\))236 2457 y(mod)h(=)f
+(__import__\('encodings.utf_16_be',)39 b(globals\(\),)44
+b(locals\(\),)f(')3016 2470 y(*)3061 2457 y('\))236 2731
+y(handles)h(=)h(new.module\('handles'\))236 2823 y(handles.getPerson)d
+(=)j(getPerson)236 2914 y(handles.savePerson)d(=)j(savePerson)236
+3005 y(def)g(handler\(req\):)416 3097 y
+(dispatch.AsHandler\(modules=\(handles,)o(\),)39 b(request=req,)k
+(typesmodule=MyComplexTypes,)d(rpc=True\))416 3188 y(return)j
+(apache.OK)0 3703 y Ff(D)-7 b(.2.3)100 b(client:)34 b(in)n(v)n(ok)n(e)
+28 b(sa)n(v)n(eP)-5 b(erson)0 3906 y Fi(scr)q(ipt)0 4200
+y Fe(import)44 b(sys)0 4291 y(from)g(ZSI.client)f(import)h(Binding)0
+4382 y(from)g(MyComplexTypes)f(import)g(Person)0 4565
+y(b)i(=)f(Binding\(url='http://localhost/test3/pickl)o(er.py')o(,)39
+b(tracefile=sys.stdout\))0 4656 y(person)44 b(=)g
+(Person\('christopher',)e(26\))0 4748 y(b.savePerson\(person\))p
+0 5549 3901 4 v 0 5649 a Fi(62)2743 b(Appendix)24 b(D)-6
+b(.)46 b(pic)n(kler)24 b(e)n(xample)p eop end
+%%Page: 63 69
+TeXDict begin 63 68 bop 0 83 a Fi(SO)n(AP)24 b(T)-10
+b(r)o(ace)0 377 y Fe(_________________________________)39
+b(Wed)45 b(Oct)f(11)g(13:10:05)g(2006)g(REQUEST:)0 469
+y(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap)o(.org/s)
+o(oap/en)o(coding)o(/")0 560 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.)
+o(org/so)o(ap/env)o(elope/)o(")0 651 y
+(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+743 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+834 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e")0
+925 y(SOAP-ENV:encodingStyle="http://schemas.)o(xmlsoa)o(p.org/)o
+(soap/e)o(ncodin)o(g/">)0 1017 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0
+1108 y(<SOAP-ENV:Body>)0 1199 y(<savePerson)h(xmlns:ns1="urn:MyApp">)0
+1290 y(<ns1:Person><name)f(xsi:type="xsd:string">christopher</name)o(>)
+0 1382 y(<age)i(xsi:type="xsd:nonNegativeInteger">26</ag)o(e>)0
+1473 y(</ns1:Person>)0 1564 y(</savePerson>)0 1656 y(</SOAP-ENV:Body>)0
+1747 y(</SOAP-ENV:Envelope>)0 1930 y(_________________________________)
+39 b(Wed)45 b(Oct)f(11)g(13:10:05)g(2006)g(RESPONSE:)0
+2021 y(Server:)g(Apache/2.0.53-dev)e(\(Unix\))h(mod_ruby/1.2.4)g
+(Ruby/1.8.2\(2004-12-25\))0 2112 y(mod_python/3.1.4)f(Python/2.4.1)0
+2204 y(Transfer-Encoding:)g(chunked)0 2295 y(Content-Type:)h(text/xml)0
+2478 y(<SOAP-ENV:Envelope)f(xmlns:SOAP-ENC="http://schemas.xmlsoap)o
+(.org/s)o(oap/en)o(coding)o(/")0 2569 y
+(xmlns:SOAP-ENV="http://schemas.xmlsoap.)o(org/so)o(ap/env)o(elope/)o
+(")0 2660 y(xmlns:ZSI="http://www.zolera.com/schema)o(s/ZSI/)o(")0
+2752 y(xmlns:xsd="http://www.w3.org/2001/XMLSc)o(hema")0
+2843 y(xmlns:xsi="http://www.w3.org/2001/XMLSc)o(hema-i)o(nstanc)o(e">)
+0 2934 y(<SOAP-ENV:Header></SOAP-ENV:Header>)0 3026 y(<SOAP-ENV:Body>)0
+3117 y(<savePersonResponse></savePersonRespons)o(e>)0
+3208 y(</SOAP-ENV:Body>)0 3300 y(</SOAP-ENV:Envelope>)p
+0 5549 3901 4 v 0 5649 a Fi(D)-6 b(.2.)52 b(code)3430
+b(63)p eop end
+%%Page: 64 70
+TeXDict begin 64 69 bop 0 83 a Ff(D)-7 b(.2.4)100 b(client:)34
+b(in)n(v)n(ok)n(e)28 b(getP)-5 b(erson)30 b(3)e(diff)m(erent)h(w)o(a)m
+(ys)0 286 y Fi(scr)q(ipt)0 580 y Fe(import)44 b(sys)0
+671 y(import)g(MyComplexTypes)0 763 y(from)g(ZSI.client)f(import)h
+(NamedParamBinding)e(as)j(NPBinding,)e(Binding)0 854
+y(from)h(ZSI)g(import)g(TC)0 1037 y(kw)g(=)h
+({'url':'http://localhost/test3/pickler.p)o(y',)39 b
+('tracefile':sys.stdout})0 1128 y(b)45 b(=)f(NPBinding\()629
+1141 y(**)719 1128 y(kw\))0 1219 y(rsp)g(=)h
+(b.getPerson\(name='christopher'\))0 1311 y(assert)f(type\(rsp\))f(is)i
+(dict,)f('expecting)f(a)h(dict')0 1402 y(assert)g
+(rsp['Person']['name'])d(==)k('christopher',)d('wrong)i(person')0
+1585 y(b)h(=)f(NPBinding\(typesmodule=MyComplexTypes,)1883
+1598 y(**)1973 1585 y(kw\))0 1676 y(rsp)g(=)h
+(b.getPerson\(name='christopher'\))0 1767 y(assert)f
+(isinstance\(rsp['Person'],)c(MyComplexTypes.Person\),)h(\()179
+1859 y('expecting)i(instance)h(of)g(\045s')h
+(\045MyComplexTypes.Person\))0 2041 y(b)g(=)f
+(Binding\(typesmodule=MyComplexTypes,)1793 2054 y(**)1883
+2041 y(kw\))0 2133 y(class)g(Name\(str\):)179 2224 y(typecode)g(=)g
+(TC.String\("name"\))0 2407 y(rsp)g(=)h
+(b.getPerson\(Name\('christopher'\)\))0 2498 y(assert)f
+(isinstance\(rsp['Person'],)c(MyComplexTypes.Person\),)h(\()179
+2589 y('expecting)i(instance)h(of)g(\045s')h
+(\045MyComplexTypes.Person\))0 3002 y Fi(SO)n(AP)24 b(T)-10
+b(r)o(ace)0 3205 y Fn(All)23 b(responses)f(are)g(e)o(xactly)f(the)i
+(same,)g(for)e(comparison)g(the)h(three)g(requests)g(are)g(presented)f
+(\002rst)i(and)f(only)g(the)g(last)h(response)f(is)0
+3304 y(included.)p 0 5549 3901 4 v 0 5649 a Fi(64)2743
+b(Appendix)24 b(D)-6 b(.)46 b(pic)n(kler)24 b(e)n(xample)p
+eop end
+%%Page: 65 71
+TeXDict begin 65 70 bop 236 174 a Fe(_________________________________)
+40 b(Wed)k(Oct)g(11)h(13:19:00)e(2006)h(REQUEST:)236
+266 y(<SOAP-ENV:Envelope)e(xmlns:SOAP-ENC="http://schemas.xmlsoap.)o
+(org/so)o(ap/enc)o(oding/)o(")236 357 y
+(xmlns:SOAP-ENV="http://schemas.xmlsoap.o)o(rg/soa)o(p/enve)o(lope/")
+236 448 y(xmlns:ZSI="http://www.zolera.com/schemas)o(/ZSI/")236
+540 y(xmlns:xsd="http://www.w3.org/2001/XMLSch)o(ema")236
+631 y(xmlns:xsi="http://www.w3.org/2001/XMLSch)o(ema-in)o(stance)o(")
+236 722 y(SOAP-ENV:encodingStyle="http://schemas.x)o(mlsoap)o(.org/s)o
+(oap/en)o(coding)o(/">)236 814 y(<SOAP-ENV:Header></SOAP-ENV:Header>)
+236 905 y(<SOAP-ENV:Body>)236 996 y(<getPerson>)236 1088
+y(<name)i(id="o6c2a0")f(xsi:type="xsd:string">christopher</name>)236
+1179 y(</getPerson>)236 1270 y(</SOAP-ENV:Body>)236 1362
+y(</SOAP-ENV:Envelope>)236 1557 y(**)371 1544 y(OMIT)h(RESPONSE)998
+1557 y(**)236 1727 y(_________________________________)c(Wed)k(Oct)g
+(11)h(13:19:00)e(2006)h(REQUEST:)236 1818 y(<SOAP-ENV:Envelope)e
+(xmlns:SOAP-ENC="http://schemas.xmlsoap.)o(org/so)o(ap/enc)o(oding/)o
+(")236 1910 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.o)o(rg/soa)o
+(p/enve)o(lope/")236 2001 y(xmlns:ZSI="http://www.zolera.com/schemas)o
+(/ZSI/")236 2092 y(xmlns:xsd="http://www.w3.org/2001/XMLSch)o(ema")236
+2183 y(xmlns:xsi="http://www.w3.org/2001/XMLSch)o(ema-in)o(stance)o(")
+236 2275 y(SOAP-ENV:encodingStyle="http://schemas.x)o(mlsoap)o(.org/s)o
+(oap/en)o(coding)o(/">)236 2366 y(<SOAP-ENV:Header></SOAP-ENV:Header>)
+236 2457 y(<SOAP-ENV:Body>)236 2549 y(<getPerson>)236
+2640 y(<name)i(id="o6c2a0")f(xsi:type="xsd:string">christopher</name>)
+236 2731 y(</getPerson>)236 2823 y(</SOAP-ENV:Body>)236
+2914 y(</SOAP-ENV:Envelope>)236 3110 y(**)371 3097 y(OMIT)h(RESPONSE)
+998 3110 y(**)236 3279 y(_________________________________)c(Wed)k(Oct)
+g(11)h(13:19:00)e(2006)h(REQUEST:)236 3371 y(<SOAP-ENV:Envelope)e
+(xmlns:SOAP-ENC="http://schemas.xmlsoap.)o(org/so)o(ap/enc)o(oding/)o
+(")236 3462 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.o)o(rg/soa)o
+(p/enve)o(lope/")236 3553 y(xmlns:ZSI="http://www.zolera.com/schemas)o
+(/ZSI/")236 3645 y(xmlns:xsd="http://www.w3.org/2001/XMLSch)o(ema")236
+3736 y(xmlns:xsi="http://www.w3.org/2001/XMLSch)o(ema-in)o(stance)o(")
+236 3827 y(SOAP-ENV:encodingStyle="http://schemas.x)o(mlsoap)o(.org/s)o
+(oap/en)o(coding)o(/">)236 3919 y(<SOAP-ENV:Header></SOAP-ENV:Header>)
+236 4010 y(<SOAP-ENV:Body>)236 4101 y(<getPerson>)236
+4193 y(<name)i(xsi:type="xsd:string">christopher</name>)236
+4284 y(</getPerson>)236 4375 y(</SOAP-ENV:Body>)236 4467
+y(</SOAP-ENV:Envelope>)236 4649 y(_________________________________)c
+(Wed)k(Oct)g(11)h(13:19:00)e(2006)h(RESPONSE:)236 4741
+y(Server:)g(Apache/2.0.53-dev)e(\(Unix\))i(mod_ruby/1.2.4)e
+(Ruby/1.8.2\(2004-12-25\))236 4832 y(mod_python/3.1.4)g(Python/2.4.1)
+236 4923 y(Transfer-Encoding:)g(chunked)236 5015 y(Content-Type:)h
+(text/xml)236 5197 y(<SOAP-ENV:Envelope)f
+(xmlns:SOAP-ENC="http://schemas.xmlsoap.)o(org/so)o(ap/enc)o(oding/)o
+(")236 5289 y(xmlns:SOAP-ENV="http://schemas.xmlsoap.o)o(rg/soa)o
+(p/enve)o(lope/")236 5380 y(xmlns:ZSI="http://www.zolera.com/schemas)o
+(/ZSI/")236 5471 y(xmlns:xsd="http://www.w3.org/2001/XMLSch)o(ema")236
+5562 y(xmlns:xsi="http://www.w3.org/2001/XMLSch)o(ema-in)o(stance)o(">)
+236 5654 y(<SOAP-ENV:Header></SOAP-ENV:Header>)236 5745
+y(<SOAP-ENV:Body>)236 5836 y(<getPersonResponse)g
+(xmlns:ns1="urn:MyApp">)236 5928 y(<ns1:Person>)236 6019
+y(<name)i(xsi:type="xsd:string">christopher</name>)236
+6110 y(<age)g(xsi:type="xsd:nonNegativeInteger">26</age)o(>)236
+6202 y(</ns1:Person>)236 6293 y(</getPersonResponse>)236
+6384 y(</SOAP-ENV:Body>)236 6476 y(</SOAP-ENV:Envelope>)p
+0 5549 3901 4 v 0 5649 a Fi(D)-6 b(.2.)52 b(code)3430
+b(65)p eop end
+%%Trailer
+
+userdict /end-hook known{end-hook}if
+%%EOF
diff --git a/doc/zsi.tex b/doc/zsi.tex
new file mode 100644
index 0000000..71f0cca
--- /dev/null
+++ b/doc/zsi.tex
@@ -0,0 +1,110 @@
+% $Header$
+\def\ZSI{\module{ZSI}}
+\documentclass{manual}
+\title{ZSI: The Zolera Soap Infrastructure \\
+ Developer's Guide}
+\author{Rich Salz,\\
+ Christopher Blunck}
+\authoraddress{
+\email{rsalz@datapower.com\\
+blunck@python.org}
+}
+\date{March 19, 2003}% edited date, not printed date
+\input{version}
+
+% tell latex2html to load the verbatim package so we get \verbatiminput
+\ifx\htmlonly\undefined\else\usepackage{verbatim}\fi
+\usepackage{url}
+
+\begin{document}
+
+\maketitle
+
+
+\centerline{\strong{COPYRIGHT}}
+
+Copyright \copyright{} 2001, Zolera Systems, Inc.\\
+All Rights Reserved.
+
+Copyright \copyright{} 2002-2003, Rich Salz.\\
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or
+sell copies of the Software, and to permit persons to whom the Software
+is furnished to do so, provided that the above copyright notice(s) and
+this permission notice appear in all copies of the Software and that
+both the above copyright notice(s) and this permission notice appear in
+supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+\vspace{1.5cm}
+
+\centerline{\strong{Acknowledgments}}
+
+We are grateful to the members of the \code{soapbuilders}
+mailing list (see \url{http://groups.yahoo.com/soapbuilders}),
+Fredrik Lundh for his \code{soaplib} package (see
+\url{http://www.secretlabs.com/downloads/index.htm\#soap}),
+Cayce Ullman and Brian Matthews for their \code{SOAP.py} package
+(see \url{http://sourceforge.net/projects/pywebsvcs}).
+
+We are particularly grateful to Brian Lloyd and the Zope Corporation
+(\url{http://www.zope.com}) for letting us incorporate his ZOPE
+WebServices package and documentation into \ZSI{}.
+\begin{abstract}
+\noindent
+\ZSI{}, the Zolera SOAP Infrastructure, is a Python package that
+provides an implementation of SOAP messaging, as described in
+\citetitle[http://www.w3.org/TR/soap]{The SOAP 1.1 Specification}.
+In particular, \ZSI{} parses and generates SOAP messages, and
+converts between native Python datatypes and SOAP syntax.
+It can also be used to build applications using
+\citetitle[http://www.w3.org/TR/SOAP-attachments]{SOAP Messages with
+Attachments}.
+\ZSI{} is ``transport neutral'', and provides only a simple
+I/O and dispatch framework; a more complete solution is the
+responsibility of the application using \ZSI{}.
+As usage patterns emerge, and common application frameworks are
+more understood, this may change.
+
+\ZSI{} requires Python 2.3 or later and PyXML version 0.8.3 or later.
+
+The \ZSI{} homepage is at \url{http://pywebsvcs.sf.net/}.
+
+\end{abstract}
+
+\tableofcontents{}
+\input{c01-intro}
+\input{c02-samples}
+\input{c03-except}
+\input{c04-utils}
+\input{c05-parse}
+\input{c06-tc}
+\input{c07-writer}
+\input{c08-fault}
+\input{c09-resolve}
+\input{c10-dispatch}
+\input{bibliography}
+\appendix
+\input{app-dispatch}
+\input{app-dispatch-dict}
+\input{app-lowlevel}
+\input{app-pickler}
+\end{document}
diff --git a/doc/zsi.xsd b/doc/zsi.xsd
new file mode 100644
index 0000000..45d2a75
--- /dev/null
+++ b/doc/zsi.xsd
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- XML Schema for ZSI: Zolera Soap Infrastructure
+
+Copyright 2001, Zolera Systems, Inc. All Rights Reserved
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or
+sell copies of the Software, and to permit persons to whom the Software
+is furnished to do so, provided that the above copyright notice(s) and
+this permission notice appear in all copies of the Software and that
+both the above copyright notice(s) and this permission notice appear in
+supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+-->
+
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:tns="http://www.zolera.com/schemas/ZSI/"
+ xmlns:SOAPFAULT="http://schemas.xmlsoap.org/soap/envelope/"
+ targetNamespace="http://www.zolera.com/schemas/ZSI/">
+
+ <import namespace="http://schemas.xmlsoap.org/soap/envelope/"
+ schemaLocation="http://schemas.xmlsoap.org/soap/envelope/"/>
+
+ <!-- Soap doesn't define a fault element to use when we want
+ to fault because of header problems. -->
+ <element name="detail" type="SOAPFAULT:detail"/>
+
+ <!-- A URIFaultDetail element typically reports an unknown
+ mustUnderstand element. -->
+ <element name="URIFaultDetail" type="tns:URIFaultDetail"/>
+ <complexType name="URIFaultDetail">
+ <sequence>
+ <element name="URI" type="anyURI" minOccurs="1"/>
+ <element name="localname" type="NCName" minOccurs="1"/>
+ <any minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ </complexType>
+
+ <!-- An ActorFaultDetail element typically reports an actor
+ attribute was found that cannot be processed. -->
+ <element name="ActorFaultDetail" type="tns:ActorFaultDetail"/>
+ <complexType name="ActorFaultDetail">
+ <sequence>
+ <element name="URI" type="anyURI" minOccurs="1"/>
+ <any minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ </complexType>
+
+ <!-- A ParseFaultDetail or a FaultDetail element are typically
+ used when there was parsing or "business-logic" errors.
+ The TracedFault type is intended to provide a human-readable
+ string that describes the error (in more detail then the
+ SOAP faultstring element, which is becoming codified),
+ and a human-readable "trace" (optional) that shows where
+ within the application that the fault happened. -->
+ <element name="ParseFaultDetail" type="tns:TracedFault"/>
+ <element name="FaultDetail" type="tns:TracedFault"/>
+ <complexType name="TracedFault">
+ <sequence>
+ <element name="string" type="string" minOccurs="1"/>
+ <element name="trace" type="string" minOccurs="0"/>
+ <!-- <any minOccurs="0" maxOccurs="unbounded"/> -->
+ </sequence>
+ </complexType>
+
+ <!-- An element to hold a name and password, for doing basic-auth. -->
+ <complexType name="BasicAuth">
+ <sequence>
+ <element name="Name" type="string" minOccurs="1"/>
+ <element name="Password" type="string" minOccurs="1"/>
+ </sequence>
+ </complexType>
+
+</schema>
diff --git a/interop/.cvsignore b/interop/.cvsignore
new file mode 100644
index 0000000..4dadcfd
--- /dev/null
+++ b/interop/.cvsignore
@@ -0,0 +1,4 @@
+*.pyc
+debuglog
+nohup.out
+accesslog
diff --git a/interop/InteropTest.wsdl b/interop/InteropTest.wsdl
new file mode 100644
index 0000000..264d67f
--- /dev/null
+++ b/interop/InteropTest.wsdl
@@ -0,0 +1,314 @@
+<?xml version="1.0"?>
+<definitions name="InteropTest" targetNamespace="http://soapinterop.org/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://soapinterop.org/" xmlns:s="http://soapinterop.org/xsd" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
+
+ <types>
+ <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://soapinterop.org/xsd">
+ <complexType name="ArrayOfstring">
+ <complexContent>
+ <restriction base="SOAP-ENC:Array">
+ <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="string[]"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ <complexType name="ArrayOfint">
+ <complexContent>
+ <restriction base="SOAP-ENC:Array">
+ <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="int[]"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ <complexType name="ArrayOffloat">
+ <complexContent>
+ <restriction base="SOAP-ENC:Array">
+ <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="float[]"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ <complexType name="ArrayOfSOAPStruct">
+ <complexContent>
+ <restriction base="SOAP-ENC:Array">
+ <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="s:SOAPStruct[]"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ <complexType name="SOAPStruct">
+ <all>
+ <element name="varString" type="string"/>
+ <element name="varInt" type="int"/>
+ <element name="varFloat" type="float"/>
+ </all>
+ </complexType>
+ </schema>
+ </types>
+
+ <message name="echoStringRequest">
+ <part name="inputString" type="xsd:string"/>
+ </message>
+ <message name="echoStringResponse">
+ <part name="return" type="xsd:string"/>
+ </message>
+ <message name="echoStringArrayRequest">
+ <part name="inputStringArray" type="s:ArrayOfstring"/>
+ </message>
+ <message name="echoStringArrayResponse">
+ <part name="return" type="s:ArrayOfstring"/>
+ </message>
+ <message name="echoIntegerRequest">
+ <part name="inputInteger" type="xsd:int"/>
+ </message>
+ <message name="echoIntegerResponse">
+ <part name="return" type="xsd:int"/>
+ </message>
+ <message name="echoIntegerArrayRequest">
+ <part name="inputIntegerArray" type="s:ArrayOfint"/>
+ </message>
+ <message name="echoIntegerArrayResponse">
+ <part name="return" type="s:ArrayOfint"/>
+ </message>
+ <message name="echoFloatRequest">
+ <part name="inputFloat" type="xsd:float"/>
+ </message>
+ <message name="echoFloatResponse">
+ <part name="return" type="xsd:float"/>
+ </message>
+ <message name="echoFloatArrayRequest">
+ <part name="inputFloatArray" type="s:ArrayOffloat"/>
+ </message>
+ <message name="echoFloatArrayResponse">
+ <part name="return" type="s:ArrayOffloat"/>
+ </message>
+ <message name="echoStructRequest">
+ <part name="inputStruct" type="s:SOAPStruct"/>
+ </message>
+ <message name="echoStructResponse">
+ <part name="return" type="s:SOAPStruct"/>
+ </message>
+ <message name="echoStructArrayRequest">
+ <part name="inputStructArray" type="s:ArrayOfSOAPStruct"/>
+ </message>
+ <message name="echoStructArrayResponse">
+ <part name="return" type="s:ArrayOfSOAPStruct"/>
+ </message>
+ <message name="echoVoidRequest"/>
+ <message name="echoVoidResponse"/>
+ <message name="echoBase64Request">
+ <part name="inputBase64" type="xsd:base64Binary"/>
+ </message>
+ <message name="echoBase64Response">
+ <part name="return" type="xsd:base64Binary"/>
+ </message>
+ <message name="echoDateRequest">
+ <part name="inputDate" type="xsd:dateTime"/>
+ </message>
+ <message name="echoDateResponse">
+ <part name="return" type="xsd:dateTime"/>
+ </message>
+ <message name="echoHexBinaryRequest">
+ <part name="inputHexBinary" type="xsd:hexBinary"/>
+ </message>
+ <message name="echoHexBinaryResponse">
+ <part name="return" type="xsd:hexBinary"/>
+ </message>
+ <message name="echoDecimalRequest">
+ <part name="inputDecimal" type="xsd:decimal"/>
+ </message>
+ <message name="echoDecimalResponse">
+ <part name="return" type="xsd:decimal"/>
+ </message>
+ <message name="echoBooleanRequest">
+ <part name="inputBoolean" type="xsd:boolean"/>
+ </message>
+ <message name="echoBooleanResponse">
+ <part name="return" type="xsd:boolean"/>
+ </message>
+
+ <portType name="InteropTestPortType">
+ <operation name="echoString" parameterOrder="inputString">
+ <input message="tns:echoStringRequest"/>
+ <output message="tns:echoStringResponse"/>
+ </operation>
+ <operation name="echoStringArray" parameterOrder="inputStringArray">
+ <input message="tns:echoStringArrayRequest"/>
+ <output message="tns:echoStringArrayResponse"/>
+ </operation>
+ <operation name="echoInteger" parameterOrder="inputInteger">
+ <input message="tns:echoIntegerRequest"/>
+ <output message="tns:echoIntegerResponse"/>
+ </operation>
+ <operation name="echoIntegerArray" parameterOrder="inputIntegerArray">
+ <input message="tns:echoIntegerArrayRequest"/>
+ <output message="tns:echoIntegerArrayResponse"/>
+ </operation>
+ <operation name="echoFloat" parameterOrder="inputFloat">
+ <input message="tns:echoFloatRequest"/>
+ <output message="tns:echoFloatResponse"/>
+ </operation>
+ <operation name="echoFloatArray" parameterOrder="inputFloatArray">
+ <input message="tns:echoFloatArrayRequest"/>
+ <output message="tns:echoFloatArrayResponse"/>
+ </operation>
+ <operation name="echoStruct" parameterOrder="inputStruct">
+ <input message="tns:echoStructRequest"/>
+ <output message="tns:echoStructResponse"/>
+ </operation>
+ <operation name="echoStructArray" parameterOrder="inputStructArray">
+ <input message="tns:echoStructArrayRequest"/>
+ <output message="tns:echoStructArrayResponse"/>
+ </operation>
+ <operation name="echoVoid">
+ <input message="tns:echoVoidRequest"/>
+ <output message="tns:echoVoidResponse"/>
+ </operation>
+ <operation name="echoBase64" parameterOrder="inputBase64">
+ <input message="tns:echoBase64Request"/>
+ <output message="tns:echoBase64Response"/>
+ </operation>
+ <operation name="echoDate" parameterOrder="inputDate">
+ <input message="tns:echoDateRequest"/>
+ <output message="tns:echoDateResponse"/>
+ </operation>
+ <operation name="echoHexBinary" parameterOrder="inputHexBinary">
+ <input message="tns:echoHexBinaryRequest"/>
+ <output message="tns:echoHexBinaryResponse"/>
+ </operation>
+ <operation name="echoDecimal" parameterOrder="inputDecimal">
+ <input message="tns:echoDecimalRequest"/>
+ <output message="tns:echoDecimalResponse"/>
+ </operation>
+ <operation name="echoBoolean" parameterOrder="inputBoolean">
+ <input message="tns:echoBooleanRequest"/>
+ <output message="tns:echoBooleanResponse"/>
+ </operation>
+ </portType>
+
+ <binding name="InteropTestSoapBinding" type="tns:InteropTestPortType">
+ <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="echoString">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoStringArray">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoInteger">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoIntegerArray">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoFloat">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoFloatArray">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoStruct">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoStructArray">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoVoid">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoBase64">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoDate">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoHexBinary">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoDecimal">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoBoolean">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ </binding>
+
+</definitions>
diff --git a/interop/InteropTestB.wsdl b/interop/InteropTestB.wsdl
new file mode 100644
index 0000000..a9b9cf5
--- /dev/null
+++ b/interop/InteropTestB.wsdl
@@ -0,0 +1,173 @@
+<?xml version="1.0"?>
+<definitions name="InteropTestB" targetNamespace="http://soapinterop.org/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://soapinterop.org/" xmlns:s="http://soapinterop.org/xsd" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
+ <types>
+ <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://soapinterop.org/xsd">
+ <complexType name="ArrayOfstring">
+ <complexContent>
+ <restriction base="SOAP-ENC:Array">
+ <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="string[]"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ <complexType name="ArrayOfint">
+ <complexContent>
+ <restriction base="SOAP-ENC:Array">
+ <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="int[]"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ <complexType name="ArrayOffloat">
+ <complexContent>
+ <restriction base="SOAP-ENC:Array">
+ <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="float[]"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ <complexType name="ArrayOfSOAPStruct">
+ <complexContent>
+ <restriction base="SOAP-ENC:Array">
+ <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="s:SOAPStruct[]"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ <complexType name="SOAPStruct">
+ <all>
+ <element name="varString" type="string"/>
+ <element name="varInt" type="int"/>
+ <element name="varFloat" type="float"/>
+ </all>
+ </complexType>
+ <complexType name="SOAPStructStruct">
+ <all>
+ <element name="varString" type="string"/>
+ <element name="varInt" type="int"/>
+ <element name="varFloat" type="float"/>
+ <element name="varStruct" type="s:SOAPStruct"/>
+ </all>
+ </complexType>
+ <complexType name="SOAPArrayStruct">
+ <all>
+ <element name="varString" type="string"/>
+ <element name="varInt" type="int"/>
+ <element name="varFloat" type="float"/>
+ <element name="varArray" type="s:ArrayOfstring"/>
+ </all>
+ </complexType>
+ <complexType name="ArrayOfString2D">
+ <complexContent>
+ <restriction base="SOAP-ENC:Array">
+ <attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="xsd:string[,]"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ </schema>
+ </types>
+
+ <message name="echoStructAsSimpleTypesRequest">
+ <part name="inputStruct" type="s:SOAPStruct"/>
+ </message>
+ <message name="echoStructAsSimpleTypesResponse">
+ <part name="outputString" type="xsd:string"/>
+ <part name="outputInteger" type="xsd:int"/>
+ <part name="outputFloat" type="xsd:float"/>
+ </message>
+ <message name="echoSimpleTypesAsStructRequest">
+ <part name="inputString" type="xsd:string"/>
+ <part name="inputInteger" type="xsd:int"/>
+ <part name="inputFloat" type="xsd:float"/>
+ </message>
+ <message name="echoSimpleTypesAsStructResponse">
+ <part name="return" type="s:SOAPStruct"/>
+ </message>
+ <message name="echo2DStringArrayRequest">
+ <part name="input2DStringArray" type="s:ArrayOfString2D"/>
+ </message>
+ <message name="echo2DStringArrayResponse">
+ <part name="return" type="s:ArrayOfString2D"/>
+ </message>
+ <message name="echoNestedStructRequest">
+ <part name="inputStruct" type="s:SOAPStructStruct"/>
+ </message>
+ <message name="echoNestedStructResponse">
+ <part name="return" type="s:SOAPStructStruct"/>
+ </message>
+ <message name="echoNestedArrayRequest">
+ <part name="inputStruct" type="s:SOAPArrayStruct"/>
+ </message>
+ <message name="echoNestedArrayResponse">
+ <part name="return" type="s:SOAPArrayStruct"/>
+ </message>
+
+ <portType name="InteropTestPortTypeB">
+ <operation name="echoStructAsSimpleTypes" parameterOrder="inputStruct outputString outputInteger outputFloat">
+ <input message="tns:echoStructAsSimpleTypesRequest"/>
+ <output message="tns:echoStructAsSimpleTypesResponse"/>
+ </operation>
+ <operation name="echoSimpleTypesAsStruct" parameterOrder="inputString inputInteger inputFloat">
+ <input message="tns:echoSimpleTypesAsStructRequest"/>
+ <output message="tns:echoSimpleTypesAsStructResponse"/>
+ </operation>
+ <operation name="echo2DStringArray" parameterOrder="input2DStringArray">
+ <input message="tns:echo2DStringArrayRequest"/>
+ <output message="tns:echo2DStringArrayResponse"/>
+ </operation>
+ <operation name="echoNestedStruct" parameterOrder="inputStruct">
+ <input message="tns:echoNestedStructRequest"/>
+ <output message="tns:echoNestedStructResponse"/>
+ </operation>
+ <operation name="echoNestedArray" parameterOrder="inputStruct">
+ <input message="tns:echoNestedArrayRequest"/>
+ <output message="tns:echoNestedArrayResponse"/>
+ </operation>
+ </portType>
+
+ <binding name="InteropTestSoapBindingB" type="tns:InteropTestPortTypeB">
+ <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="echoStructAsSimpleTypes">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoSimpleTypesAsStruct">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echo2DStringArray">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoNestedStruct">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ <operation name="echoNestedArray">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ </binding>
+
+</definitions>
diff --git a/interop/README b/interop/README
new file mode 100644
index 0000000..3dc085e
--- /dev/null
+++ b/interop/README
@@ -0,0 +1,29 @@
+
+This directory includes a server that implements "Round 2" interoperability
+testing. More information is available from:
+ The soapbuilders mailing list, a forum for people creating SOAP
+ implementations. See http://yahoogroups.com/group/soapbuilders
+
+ An "invitation to join the testing" message can be found at
+ http://groups.yahoo.com/group/soapbuilders/message/5885
+
+The client.py program will send pre-formatted test packets to a server.
+I have not done generic client test, for a couple of reasons:
+ 1. Too hard to keep track of all the endpoints; and
+ 2. I'm not good at writing corner-case tests that stretch the limits
+ 3. The server already exercises all the code -- it parses and
+ serializes There are several other clients out there already
+
+There are some other tests not currently implemented (in probable order
+of increasing difficulty):
+
+a. Polymorph service (get a struct, return the parts, etc.). See WSDL at
+ http://www.whitemesa.net/wsdl/polymorph.wsdl
+
+b. Document/literal encoding. See WSDL at
+ http://www.whitemesa.net/wsdl/interopdoc.wsdl
+
+c. WS-Routing (not yet implemented in ZSI :). See WSDL at
+ http://www.whitemesa.net/wsdl/interop_base_end.wsdl
+
+
diff --git a/interop/client.py b/interop/client.py
new file mode 100755
index 0000000..7dda6fe
--- /dev/null
+++ b/interop/client.py
@@ -0,0 +1,102 @@
+#! /usr/bin/env python
+import getopt, socket, sys
+from cpackets import testlist
+
+try:
+ (opts, args) = getopt.getopt(sys.argv[1:],
+ 'h:lp:qst:w',
+ ( 'host=', 'list', 'port=',
+ 'quit', 'statusonly', 'test=', 'wsdl', 'help'))
+except getopt.GetoptError, e:
+ print >>sys.stderr, sys.argv[0] + ': ' + str(e)
+ sys.exit(1)
+if args:
+ print sys.argv[0] + ': Usage error; try --help.'
+ sys.exit(1)
+
+hostname, portnum, tests, quitting, getwsdl, verbose = \
+ 'localhost', 1122, [0,1], 0, 0, 1
+for opt, val in opts:
+ if opt in [ '--help' ]:
+ print '''Options include:
+ --host HOST (-h HOST) Name of server host
+ --port PORT (-p PORT) Port server is listening on
+ --quit (-q) Send server a QUIT command
+ --testnum 1,2,3 (-t ...) Run comma-separated tests; use * or all for all
+ --list (-l) List tests (brief description)
+ --statusonly (-s) Do not output reply packets; just status code
+ --wsdl (-w) Get the WSDL file
+Default is -h%s -p%d -t%s''' % \
+ (hostname, portnum, ','.join([str(x) for x in tests]))
+ sys.exit(0)
+ if opt in [ '-h', '--host' ]:
+ hostname = val
+ elif opt in [ '-p', '--port' ]:
+ portnum = int(val)
+ elif opt in [ '-s', '--statusonly' ]:
+ verbose = 0
+ elif opt in [ '-q', '--quit' ]:
+ quitting = 1
+ elif opt in [ '-t', '--testnum' ]:
+ if val in [ '*', 'all' ]:
+ tests = range(len(testlist))
+ else:
+ tests = [ int(t) for t in val.split(',') ]
+ elif opt in [ '-l', '--list' ]:
+ for i in range(len(testlist)):
+ print i, testlist[i][0]
+ sys.exit(0)
+ elif opt in [ '-w', '--wsdl' ]:
+ getwsdl = 1
+
+if quitting:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ try:
+ s.connect((hostname, portnum))
+ except socket.error, e:
+ if e.args[1] == 'Connection refused': sys.exit(0)
+ raise
+ f = s.makefile('r+')
+ f.write('QUIT / HTTP/1.0\r\n')
+ f.flush()
+ sys.exit(0)
+
+if getwsdl:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((hostname, portnum))
+ f = s.makefile('r+')
+ f.write('GET /wsdl HTTP/1.0\r\n\r\n')
+ f.flush()
+ status = f.readline()
+ print status,
+ while 1:
+ l = f.readline()
+ if l == '': break
+ print l,
+ sys.exit(0)
+
+for T in tests:
+ descr, IN, header = testlist[T]
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((hostname, portnum))
+ f = s.makefile('r+')
+
+ print '-' * 60, '\n\n\n', T, descr
+ f.write('POST / HTTP/1.0\r\n')
+ f.write('SOAPAction: "http://soapinterop.org/"\r\n')
+ if header == None:
+ f.write('Content-type: text/xml; charset="utf-8"\r\n')
+ f.write('Content-Length: %d\r\n\r\n' % len(IN))
+ else:
+ f.write(header)
+ f.write(IN)
+ f.flush()
+
+ status = f.readline()
+ print status,
+ while 1:
+ l = f.readline()
+ if l == '': break
+ if verbose: print l,
+
+ f.close()
diff --git a/interop/cpackets.py b/interop/cpackets.py
new file mode 100755
index 0000000..30dd335
--- /dev/null
+++ b/interop/cpackets.py
@@ -0,0 +1,205 @@
+#! /usr/bin/env python
+
+pocketsoap_struct_test = '''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:ps42='http://soapinterop.org/xsd'
+xmlns:xsd='http://www.w3.org/1999/XMLSchema'
+xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance'>
+<SOAP-ENV:Header>
+<ww:echoMeStringRequest xmlns:ww="http://soapinterop.org/echoheader/">
+Please release &lt;me</ww:echoMeStringRequest>
+</SOAP-ENV:Header>
+<SOAP-ENV:Body>
+ <m:echoStruct xmlns:m='http://soapinterop.org/'>
+ <inputStruct xsi:type='ps42:SOAPStruct'>
+ <varInt xsi:type='xsd:int'>1073741824</varInt>
+
+ <varFloat xsi:type='xsd:float'>-42.24</varFloat>
+
+ <varString xsi:type='xsd:string'>pocketSOAP
+ rocks!&lt;g&gt;</varString>
+ </inputStruct>
+ </m:echoStruct>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+phalanx_b64_test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:m='http://soapinterop.org/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
+ <SOAP-ENV:Body>
+ <m:echoBase64>
+ <inputBase64 xsi:type='xsd:base64Binary'>Ty4rY6==</inputBase64>
+ </m:echoBase64>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+hexbin_test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:m='http://soapinterop.org/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
+ <SOAP-ENV:Body>
+ <m:echoHexBinary>
+ <inputHexBinary xsi:type='xsd:hexBinary'>656174206d792073686f72747321</inputHexBinary>
+ </m:echoHexBinary>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+phalanx_badhref_test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns1='http://soapinterop.org/xsd' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:ns='http://soapinterop.org/' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
+ <SOAP-ENV:Body>
+ <ns:echoStructArray>
+ <inputStructArray xsi:type='SOAP-ENC:Array' SOAP-ENC:arrayType='ns1:SOAPStruct[3]'>
+ <item xsi:type='ns1:SOAPStruct' href='#2'>invalid value</item>
+ <item xsi:type='ns1:SOAPStruct'>
+ <varFloat xsi:type='xsd:float'>21.02</varFloat>
+ <varString xsi:type='xsd:string'>c</varString>
+ <varInt xsi:type='xsd:int'>3</varInt>
+ </item>
+ <item xsi:type='ns1:SOAPStruct' href='#1'/>
+ </inputStructArray>
+ </ns:echoStructArray>
+ <mrA xsi:type='ns1:SOAPStruct' id='1'>
+ <varInt xsi:type='xsd:int'>-33</varInt>
+ <varFloat xsi:type='xsd:float'>33.33</varFloat>
+ <varString xsi:type='xsd:string'>test 1</varString>
+ </mrA>
+ <mrB xsi:type='ns1:SOAPStruct' id='2'>
+ <varFloat xsi:type='xsd:float'>11.11</varFloat>
+ <varString xsi:type='xsd:string'>test 2</varString>
+ <varInt xsi:type='xsd:int'>-11</varInt>
+ </mrB>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+someones_b64_test = '''<S:Envelope
+ S:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
+ xmlns:S='http://schemas.xmlsoap.org/soap/envelope/'
+ xmlns:E='http://schemas.xmlsoap.org/soap/encoding/'
+ xmlns:a='http://soapinterop.org/'
+ xmlns:b='http://www.w3.org/2001/XMLSchema-instance'>
+ <S:Body>
+ <a:echoBase64><inputBase64
+ b:type='E:base64'>AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T1</inputBase64>
+ </a:echoBase64>
+ </S:Body></S:Envelope>'''
+
+phalanx_void_test = '''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:ps42='http://soapinterop.org/xsd'
+xmlns:xsd='http://www.w3.org/1999/XMLSchema'
+xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance'>
+<SOAP-ENV:Header>
+<ww:echoMeStringRequest SOAP-ENV:mustUnderstand="1" xmlns:ww="http://soapinterop.org/echoheader/">
+Please release me</ww:echoMeStringRequest>
+<ww:echoMeStructRequest xmlns:ww="http://soapinterop.org/echoheader/">
+ <varInt>111</varInt>
+ <varFloat>-42.24</varFloat>
+ <varString>Header text string.</varString>
+ </ww:echoMeStructRequest>
+</SOAP-ENV:Header>
+<SOAP-ENV:Body>
+ <m:echoVoid xmlns:m='http://soapinterop.org/'/>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+multipart_test = '''
+Ignore this
+--sep
+Content-Type: text/xml
+
+<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+xmlns:ps42='http://soapinterop.org/xsd'
+xmlns:xsd='http://www.w3.org/1999/XMLSchema'
+xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance'>
+<SOAP-ENV:Body>
+ <m:echoStruct xmlns:m='http://soapinterop.org/'>
+ <inputStruct xsi:type='ps42:SOAPStruct'>
+ <varInt xsi:type='xsd:int'>1073741824</varInt>
+
+ <varFloat xsi:type='xsd:float'>-42.24</varFloat>
+
+ <varString xsi:type='xsd:string' href="cid:123@456"/>
+ </inputStruct>
+ </m:echoStruct>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+--sep
+Content-ID: otherwise
+
+yehll
+
+--sep
+Content-ID: 123@456
+
+this is a very long string
+it is separated over several lines.
+hwlleasdfasdf
+asdfad
+--sep--
+'''
+phalanx_badstructtype_test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns1='http://soapinterop.org/xsd' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:m='http://soapinterop.org/' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
+ <SOAP-ENV:Body>
+ <m:echoStruct>
+ <inputStruct xsi:type='ns1:roastbeef'>
+ <varString xsi:type='xsd:string'>easy test</varString>
+ <varInt xsi:type='xsd:int'>11</varInt>
+ <varFloat xsi:type='xsd:float'>22.33</varFloat>
+ </inputStruct>
+ </m:echoStruct>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+phalanx_int_href_test = '''<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:m='http://soapinterop.org/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
+ <SOAP-ENV:Body>
+ <m:echoInteger>
+ <inputInteger href='#a1'/>
+ </m:echoInteger>
+ <multiref xsi:type='xsd:int' id='a1'>13</multiref>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+'''
+
+wm_simple_as_struct_test = '''<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope
+SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<SOAP-ENV:Body>
+<m:echoSimpleTypesAsStruct xmlns:m="http://soapinterop.org/">
+<inputString>White Mesa Test</inputString>
+<inputInteger>42</inputInteger>
+<inputFloat>0.0999</inputFloat>
+</m:echoSimpleTypesAsStruct>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+'''
+
+apache_float_test = '''<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SOAP-ENV:Body>
+ <ns1:echoFloat xmlns:ns1="http://soapinterop.org/">
+ <inputFloat xsi:type="xsd:float">3.7</inputFloat>
+ </ns1:echoFloat>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+testlist = (
+ ( 'struct test', pocketsoap_struct_test, None),
+ ( 'base64 test', phalanx_b64_test, None),
+ ( 'hexBinary', hexbin_test, None),
+ ( 'big base64 test', someones_b64_test, None),
+ ( 'echovoid', phalanx_void_test, None),
+ ( 'simple2struct', wm_simple_as_struct_test, None),
+ ( 'multipart', multipart_test,
+ 'Content-type: multipart/related; boundary="sep"\r\n' ),
+ ( 'int href test', phalanx_int_href_test, None),
+ ( 'apache float', apache_float_test, None),
+ ( 'bad href test', phalanx_badhref_test, None),
+ ( 'bad type attr on struct', phalanx_badstructtype_test, None),
+)
diff --git a/interop/echoHeaderBindings.wsdl b/interop/echoHeaderBindings.wsdl
new file mode 100644
index 0000000..5d70702
--- /dev/null
+++ b/interop/echoHeaderBindings.wsdl
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<definitions name="InteropTest" targetNamespace="http://soapinterop.org/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://soapinterop.org/" xmlns:s="http://soapinterop.org/xsd" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
+
+ <message name="echoHeaderString_Request">
+ <part name="echoMeStringRequest" type="xsd:string"/>
+ </message>
+ <message name="echoHeaderString_Response">
+ <part name="echoMeStringResponse" type="xsd:string"/>
+ </message>
+ <message name="echoHeaderStruct_Request">
+ <part name="echoMeStructRequest" type="s:SOAPStruct"/>
+ </message>
+ <message name="echoHeaderStruct_Response">
+ <part name="echoMeStructResponse" type="s:SOAPStruct"/>
+ </message>
+
+ <binding name="InteropEchoHeaderStringBinding" type="tns:InteropTestPortType">
+ <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="echoVoid">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ <soap:header use="encoded" message="tns:echoHeaderString_Request" part="echoMeStringRequest" namespace="http://soapinterop.org/echoheader/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ <soap:header use="encoded" message="tns:echoHeaderString_Response" part="echoMeStringResponse" namespace="http://soapinterop.org/echoheader/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ </binding>
+
+ <binding name="InteropEchoHeaderStructBinding" type="tns:InteropTestPortType">
+ <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="echoVoid">
+ <soap:operation soapAction="http://soapinterop.org/"/>
+ <input>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ <soap:header use="encoded" message="tns:echoHeaderStruct_Request" part="echoMeStructRequest" namespace="http://soapinterop.org/echoheader/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </input>
+ <output>
+ <soap:body use="encoded" namespace="http://soapinterop.org/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ <soap:header use="encoded" message="tns:echoHeaderStruct_Response" part="echoMeStructResponse" namespace="http://soapinterop.org/echoheader/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+ </output>
+ </operation>
+ </binding>
+
+</definitions>
diff --git a/interop/sclasses.py b/interop/sclasses.py
new file mode 100644
index 0000000..b748af8
--- /dev/null
+++ b/interop/sclasses.py
@@ -0,0 +1,233 @@
+#! /usr/bin/env python
+
+WSDL_DEFINITION = '''<?xml version="1.0"?>
+
+<definitions name="InteropTest"
+ targetNamespace="http://soapinterop.org/"
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:tns="http://soapinterop.org/">
+
+ <import
+ location="http://www.whitemesa.com/interop/InteropTest.wsdl"
+ namespace="http://soapinterop.org/xsd"/>
+ <import
+ location="http://www.whitemesa.com/interop/InteropTest.wsdl"
+ namespace="http://soapinterop.org/"/>
+ <import
+ location="http://www.whitemesa.com/interop/InteropTestB.wsdl"
+ namespace="http://soapinterop.org/"/>
+ <import
+ location="http://www.whitemesa.com/interop/echoHeaderBindings.wsdl"
+ namespace="http://soapinterop.org/"/>
+ <import
+ location="http://www.whitemesa.com/interop/InteropTestMap.wsdl"
+ namespace="http://soapinterop.org/"/>
+<!-- DOCSTYLE; soon.
+ <import
+ location="http://www.whitemesa.com/interop/interopdoc.wsdl"
+ namespace="http://soapinterop.org/"/>
+-->
+
+ <service name="interop">
+ <port name="TestSoap" binding="tns:InteropTestSoapBinding">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+ <port name="TestSoapB" binding="tns:InteropTestSoapBindingB">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+ <port name="EchoHeaderString" binding="tns:InteropEchoHeaderStringBinding">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+ <port name="EchoHeaderStruct" binding="tns:InteropEchoHeaderStructBinding">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+ <port name="TestSoapMap" binding="tns:InteropTestSoapBindingMap">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+<!-- DOCSTYLE; soon.
+ <port name="TestDoc" binding="tns:doc_test_binding">
+ <soap:address location=">>>URL<<<"/>
+ </port>
+-->
+ </service>
+
+</definitions>
+'''
+
+from ZSI import *
+from ZSI import _copyright, _seqtypes
+import types
+
+class SOAPStruct:
+ def __init__(self, name):
+ pass
+ def __str__(self):
+ return str(self.__dict__)
+
+class TC_SOAPStruct(TC.Struct):
+ def __init__(self, pname=None, **kw):
+ TC.Struct.__init__(self, SOAPStruct, [
+ TC.String('varString', strip=0, inline=1),
+ TC.Iint('varInt'),
+ TC.FPfloat('varFloat', format='%.18g'),
+ ], pname, **kw)
+
+class TC_SOAPStructStruct(TC.Struct):
+ def __init__(self, pname=None, **kw):
+ TC.Struct.__init__(self, SOAPStruct, [
+ TC.String('varString', strip=0),
+ TC.Iint('varInt'),
+ TC.FPfloat('varFloat', format='%.18g'),
+ TC_SOAPStruct('varStruct'),
+ ], pname, **kw)
+
+class TC_SOAPArrayStruct(TC.Struct):
+ def __init__(self, pname=None, **kw):
+ TC.Struct.__init__(self, SOAPStruct, [
+ TC.String('varString', strip=0),
+ TC.Iint('varInt'),
+ TC.FPfloat('varFloat', format='%.18g'),
+ TC.Array('xsd:string', TC.String(string=0), 'varArray'),
+ ], pname, **kw)
+
+class TC_ArrayOfstring(TC.Array):
+ def __init__(self, pname=None, **kw):
+ TC.Array.__init__(self, 'xsd:string', TC.String(string=0), pname, **kw)
+
+class TC_ArrayOfint(TC.Array):
+ def __init__(self, pname=None, **kw):
+ TC.Array.__init__(self, 'xsd:int', TC.Iint(), pname, **kw)
+
+class TC_ArrayOffloat(TC.Array):
+ def __init__(self, pname=None, **kw):
+ TC.Array.__init__(self, 'xsd:float', TC.FPfloat(format='%.18g'),
+ pname, **kw)
+
+class TC_ArrayOfSOAPStruct(TC.Array):
+ def __init__(self, pname=None, **kw):
+ TC.Array.__init__(self, 'Za:SOAPStruct', TC_SOAPStruct(), pname, **kw)
+
+#class TC_ArrayOfstring2D(TC.Array):
+# def __init__(self, pname=None, **kw):
+# TC.Array.__init__(self, 'xsd:string', TC.String(string=0), pname, **kw)
+
+class RPCParameters:
+ def __init__(self, name):
+ pass
+ def __str__(self):
+ t = str(self.__dict__)
+ if hasattr(self, 'inputStruct'):
+ t += '\ninputStruct\n'
+ t += str(self.inputStruct)
+ if hasattr(self, 'inputStructArray'):
+ t += '\ninputStructArray\n'
+ t += str(self.inputStructArray)
+ return t
+ def frominput(self, arg):
+ self.v = s = SOAPStruct(None)
+ self.v.varString = arg.inputString
+ self.v.varInt = arg.inputInteger
+ self.v.varFloat = arg.inputFloat
+ return self
+
+class Operation:
+ dispatch = {}
+ SOAPAction = '''"http://soapinterop.org/"'''
+ ns = "http://soapinterop.org/"
+ hdr_ns = "http://soapinterop.org/echoheader/"
+
+ def __init__(self, name, tcin, tcout, **kw):
+ self.name = name
+ if type(tcin) not in _seqtypes: tcin = tcin,
+ self.TCin = TC.Struct(RPCParameters, tuple(tcin), name)
+ if type(tcout) not in _seqtypes: tcout = tcout,
+ self.TCout = TC.Struct(RPCParameters, tuple(tcout), name + 'Response')
+ self.convert = kw.get('convert', None)
+ self.headers = kw.get('headers', [])
+ self.nsdict = kw.get('nsdict', {})
+ Operation.dispatch[name] = self
+
+Operation("echoString",
+ TC.String('inputString', strip=0),
+ TC.String('inputString', oname='return', strip=0)
+)
+Operation("echoStringArray",
+ TC_ArrayOfstring('inputStringArray'),
+ TC_ArrayOfstring('inputStringArray', oname='return')
+)
+Operation("echoInteger",
+ TC.Iint('inputInteger'),
+ TC.Iint('inputInteger', oname='return'),
+)
+Operation("echoIntegerArray",
+ TC_ArrayOfint('inputIntegerArray'),
+ TC_ArrayOfint('inputIntegerArray', oname='return'),
+)
+Operation("echoFloat",
+ TC.FPfloat('inputFloat', format='%.18g'),
+ TC.FPfloat('inputFloat', format='%.18g', oname='return'),
+)
+Operation("echoFloatArray",
+ TC_ArrayOffloat('inputFloatArray'),
+ TC_ArrayOffloat('inputFloatArray', oname='return'),
+)
+Operation("echoStruct",
+ TC_SOAPStruct('inputStruct'),
+ TC_SOAPStruct('inputStruct', oname='return'),
+)
+Operation("echoStructArray",
+ TC_ArrayOfSOAPStruct('inputStructArray'),
+ TC_ArrayOfSOAPStruct('inputStructArray', oname='return'),
+ nsdict={'Za': 'http://soapinterop.org/xsd'}
+)
+Operation("echoVoid",
+ [],
+ [],
+ headers=( ( Operation.hdr_ns, 'echoMeStringRequest' ),
+ ( Operation.hdr_ns, 'echoMeStructRequest' ) )
+)
+Operation("echoBase64",
+ TC.Base64String('inputBase64'),
+ TC.Base64String('inputBase64', oname='return'),
+)
+Operation("echoDate",
+ TC.gDateTime('inputDate'),
+ TC.gDateTime('inputDate', oname='return'),
+)
+Operation("echoHexBinary",
+ TC.HexBinaryString('inputHexBinary'),
+ TC.HexBinaryString('inputHexBinary', oname='return'),
+)
+Operation("echoDecimal",
+ TC.Decimal('inputDecimal'),
+ TC.Decimal('inputDecimal', oname='return'),
+)
+Operation("echoBoolean",
+ TC.Boolean('inputBoolean'),
+ TC.Boolean('inputBoolean', oname='return'),
+)
+Operation("echoStructAsSimpleTypes",
+ TC_SOAPStruct('inputStruct'),
+ ( TC.String('outputString', strip=0), TC.Iint('outputInteger'),
+ TC.FPfloat('outputFloat', format='%.18g') ),
+ convert=lambda s: (s.v.varString, s.v.varInt, s.v.varFloat),
+)
+Operation("echoSimpleTypesAsStruct",
+ ( TC.String('inputString', strip=0), TC.Iint('inputInteger'),
+ TC.FPfloat('inputFloat') ),
+ TC_SOAPStruct('v', opname='return'),
+ convert=lambda arg: RPCParameters(None).frominput(arg),
+)
+#Operation("echo2DStringArray",
+# TC_ArrayOfstring2D('input2DStringArray'),
+# TC_ArrayOfstring2D('return')
+#),
+Operation("echoNestedStruct",
+ TC_SOAPStructStruct('inputStruct'),
+ TC_SOAPStructStruct('inputStruct', oname='return'),
+)
+Operation("echoNestedArray",
+ TC_SOAPArrayStruct('inputStruct'),
+ TC_SOAPArrayStruct('inputStruct', oname='return'),
+)
diff --git a/interop/server.py b/interop/server.py
new file mode 100755
index 0000000..56317ed
--- /dev/null
+++ b/interop/server.py
@@ -0,0 +1,209 @@
+#! /usr/bin/env python
+from ZSI import *
+from ZSI import _copyright, resolvers, _child_elements, _textprotect
+import sys, time, cStringIO as StringIO
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+
+from sclasses import Operation, WSDL_DEFINITION, TC_SOAPStruct
+
+class InteropRequestHandler(BaseHTTPRequestHandler):
+ server_version = 'ZSI/1.2 OS390/VM5.4 ' + BaseHTTPRequestHandler.server_version
+
+ def send_xml(self, text, code=200):
+ '''Send some XML.'''
+ self.send_response(code)
+ self.send_header('Content-type', 'text/xml; charset="utf-8"')
+ self.send_header('Content-Length', str(len(text)))
+ self.end_headers()
+ self.wfile.write(text)
+ self.trace(text, 'SENT')
+ self.wfile.flush()
+
+ def send_fault(self, f):
+ '''Send a fault.'''
+ self.send_xml(f.AsSOAP(), 500)
+
+ def trace(self, text, what):
+ '''Log a debug/trace message.'''
+ F = self.server.tracefile
+ if not F: return
+ print >>F, '=' * 60, '\n%s %s %s %s:' % \
+ (what, self.client_address, self.path, time.ctime(time.time()))
+ print >>F, text
+ print >>F, '=' * 60, '\n'
+ F.flush()
+
+ def do_QUIT(self):
+ '''Quit.'''
+ self.server.quitting = 1
+ self.log_message('Got QUIT command')
+ sys.stderr.flush()
+ raise SystemExit
+
+ def do_GET(self):
+ '''The GET command. Always returns the WSDL.'''
+ self.send_xml(WSDL_DEFINITION.replace('>>>URL<<<', self.server.url))
+
+ def do_POST(self):
+ '''The POST command.'''
+
+ try:
+ # SOAPAction header.
+ action = self.headers.get('soapaction', None)
+ if not action:
+ self.send_fault(Fault(Fault.Client,
+ 'SOAPAction HTTP header missing.'))
+ return
+ if action != Operation.SOAPAction:
+ self.send_fault(Fault(Fault.Client,
+ 'SOAPAction is "%s" not "%s"' % \
+ (action, Operation.SOAPAction)))
+ return
+
+ # Parse the message.
+ ct = self.headers['content-type']
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, self.rfile)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ cl = int(self.headers['content-length'])
+ IN = self.rfile.read(cl)
+ self.trace(IN, 'RECEIVED')
+ ps = ParsedSoap(IN)
+ except ParseException, e:
+ self.send_fault(FaultFromZSIException(e))
+ return
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ self.send_fault(FaultFromException(e, 1, sys.exc_info()[2]))
+ return
+
+ try:
+ # Actors?
+ a = ps.WhatActorsArePresent()
+ if len(a):
+ self.send_fault(FaultFromActor(a[0]))
+ return
+
+ # Is the operation defined?
+ root = ps.body_root
+ if root.namespaceURI != Operation.ns:
+ self.send_fault(Fault(Fault.Client,
+ 'Incorrect namespace "%s"' % root.namespaceURI))
+ return
+ n = root.localName
+ op = Operation.dispatch.get(n, None)
+ if not op:
+ self.send_fault(Fault(Fault.Client,
+ 'Undefined operation "%s"' % n))
+ return
+
+ # Scan headers. First, see if we understand all headers with
+ # mustUnderstand set. Then, get the ones intended for us (ignoring
+ # others since step 1 insured they're not mustUnderstand).
+ for mu in ps.WhatMustIUnderstand():
+ if mu not in op.headers:
+ uri, localname = mu
+ self.send_fault(FaultFromNotUnderstood(uri, localname))
+ return
+ headers = [ e for e in ps.GetMyHeaderElements()
+ if (e.namespaceURI, e.localName) in op.headers ]
+ nsdict={ 'Z': Operation.ns }
+ if headers:
+ nsdict['E'] = Operation.hdr_ns
+ self.process_headers(headers, ps)
+ else:
+ self.headertext = None
+
+ try:
+ results = op.TCin.parse(ps.body_root, ps)
+ except ParseException, e:
+ self.send_fault(FaultFromZSIException(e))
+ self.trace(str(results), 'PARSED')
+ if op.convert:
+ results = op.convert(results)
+ if op.nsdict: nsdict.update(op.nsdict)
+ reply = StringIO.StringIO()
+ sw = SoapWriter(reply, nsdict=nsdict, header=self.headertext)
+ sw.serialize(results, op.TCout,
+ name = 'Z:' + n + 'Response', inline=1)
+ sw.close()
+ self.send_xml(reply.getvalue())
+ except Exception, e:
+ # Fault while processing; now it's in the body.
+ self.send_fault(FaultFromException(e, 0, sys.exc_info()[2]))
+ return
+
+ def process_headers(self, headers, ps):
+ '''Process headers, set self.headertext to be what to output.
+ '''
+ self.headertext = ''
+ for h in headers:
+ if h.localName == 'echoMeStringRequest':
+ s = TC.String().parse(h, ps)
+ self.headertext += \
+ '<E:echoMeStringResponse>%s</E:echoMeStringResponse>\n' % _textprotect(s)
+ elif h.localName == 'echoMeStructRequest':
+ tc = TC_SOAPStruct('echoMeStructRequest', inline=1)
+ data = tc.parse(h, ps)
+ s = StringIO.StringIO()
+ sw = SoapWriter(s, envelope=0)
+ tc.serialize(sw, data, name='E:echoMeStructResponse')
+ sw.close()
+ self.headertext += s.getvalue()
+ else:
+ raise TypeError('Unhandled header ' + h.nodeName)
+ pass
+
+
+class InteropHTTPServer(HTTPServer):
+ def __init__(self, me, url, **kw):
+ HTTPServer.__init__(self, me, InteropRequestHandler)
+ self.quitting = 0
+ self.tracefile = kw.get('tracefile', None)
+ self.url = url
+ def handle_error(self, req, client_address):
+ if self.quitting: sys.exit(0)
+ HTTPServer.handle_error(self, req, client_address)
+
+
+import getopt
+try:
+ (opts, args) = getopt.getopt(sys.argv[1:], 'l:p:t:u:',
+ ('log=', 'port=', 'tracefile=', 'url=') )
+except getopt.GetoptError, e:
+ print >>sys.stderr, sys.argv[0] + ': ' + str(e)
+ sys.exit(1)
+if args:
+ print >>sys.stderr, sys.argv[0] + ': Usage error.'
+ sys.exit(1)
+
+portnum = 1122
+tracefile = None
+url = None
+for opt, val in opts:
+ if opt in [ '-l', '--logfile' ]:
+ sys.stderr = open(val, 'a')
+ elif opt in [ '-p', '--port' ]:
+ portnum = int(val)
+ elif opt in [ '-t', '--tracefile' ]:
+ if val == '-':
+ tracefile = sys.stdout
+ else:
+ tracefile = open(val, 'a')
+ elif opt in [ '-u', '--url' ]:
+ url = val
+ME = ( '', portnum )
+
+if not url:
+ import socket
+ url = 'http://' + socket.getfqdn()
+ if portnum != 80: url += ':%d' % portnum
+ url += '/interop.wsdl'
+
+try:
+ InteropHTTPServer(ME, url, tracefile=tracefile).serve_forever()
+except SystemExit:
+ pass
+sys.exit(0)
diff --git a/interop/start b/interop/start
new file mode 100755
index 0000000..0d8ab2c
--- /dev/null
+++ b/interop/start
@@ -0,0 +1,16 @@
+#! /bin/sh
+case "$1" in
+ -keep)
+ shift
+ ;;
+ *)
+ : > debuglog
+ : > accesslog
+ ;;
+esac
+./client.py $* -q
+rm -f nohup.out
+sleep 1
+nohup ./server.py -t debuglog -l accesslog $* &
+sleep 1
+cat nohup.out
diff --git a/newver.py b/newver.py
new file mode 100755
index 0000000..8a38030
--- /dev/null
+++ b/newver.py
@@ -0,0 +1,45 @@
+#! /usr/bin/env python
+
+# This script does not use 2.x features so that you can build an RPM
+# on a system that doesn't have 2.x installed (e.g., basic RedHat).
+
+import ConfigParser
+import os
+import sys
+import time
+
+cf = ConfigParser.ConfigParser()
+cf.read('setup.cfg')
+
+major = cf.getint('version', 'major')
+minor = cf.getint('version', 'minor')
+patchlevel = cf.getint('version', 'patchlevel')
+
+if '--incr' in sys.argv[1:]:
+ patchlevel = patchlevel + 1
+ cf.set('version', 'patchlevel', patchlevel)
+ f = open('setup.cfg', 'w')
+ cf.write(f)
+ f.close()
+
+def write_changes(filename, text):
+ """Write text to the file only if the text changed."""
+ oldtext = None
+ if os.path.exists(filename):
+ f = open(filename)
+ oldtext = f.read()
+ f.close()
+ if oldtext != text:
+ f = open(filename, 'w')
+ f.write(text)
+ f.close()
+
+write_changes('ZSI/version.py',
+ '# Auto-generated file; do not edit\n'
+ 'Version = (%d, %d, %d)\n' % (major, minor, patchlevel))
+
+write_changes('doc/version.tex',
+ '% Auto-generated file; do not edit\n'
+ + time.strftime('\\date{%B %d, %Y}\n', time.localtime())
+ + ('\\release{%d.%d.%d}\n' % (major, minor, patchlevel))
+ + ('\\setshortversion{%d.%d}\n' % (major, minor)))
diff --git a/samples/Echo/Echo.wsdl b/samples/Echo/Echo.wsdl
new file mode 100644
index 0000000..d1751c0
--- /dev/null
+++ b/samples/Echo/Echo.wsdl
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<definitions
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:tns="urn:ZSI:examples"
+ targetNamespace="urn:ZSI:examples" >
+ <types>
+ <xsd:schema elementFormDefault="qualified" targetNamespace="urn:ZSI:examples">
+ <xsd:element name="Echo">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element minOccurs="0" maxOccurs="1" name="EchoIn" type="xsd:string" />
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="EchoResponse">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element minOccurs="0" maxOccurs="1" name="EchoResult" type="xsd:string" />
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ </types>
+
+ <message name="EchoRequest">
+ <part name="parameters" element="tns:Echo" />
+ </message>
+ <message name="EchoResponse">
+ <part name="parameters" element="tns:EchoResponse"/>
+ </message>
+
+ <portType name="EchoServer">
+ <operation name="Echo">
+ <input message="tns:EchoRequest"/>
+ <output message="tns:EchoResponse"/>
+ </operation>
+ </portType>
+
+ <binding name="EchoServer" type="tns:EchoServer">
+ <soap:binding style="document"
+ transport="http://schemas.xmlsoap.org/soap/http"/>
+ <operation name="Echo">
+ <soap:operation soapAction="Echo"/>
+ <input>
+ <soap:body use="literal"/>
+ </input>
+ <output>
+ <soap:body use="literal"/>
+ </output>
+ </operation>
+ </binding>
+
+ <service name="EchoServer">
+ <port name="EchoServer" binding="tns:EchoServer">
+ <soap:address location="http://localhost:7000"/>
+ </port>
+ </service>
+
+</definitions>
diff --git a/samples/Echo/EchoClient.py b/samples/Echo/EchoClient.py
new file mode 100644
index 0000000..3d457a4
--- /dev/null
+++ b/samples/Echo/EchoClient.py
@@ -0,0 +1,17 @@
+#
+# Exampe echo client, to show extended code generation in ZSI
+#
+# Import the client proxy object
+from EchoServer_client import EchoServerSOAP
+
+# Instantiate a client proxy object, then call it
+hw = EchoServerSOAP("http://localhost:9999/EchoServer")
+print hw.Echo("Test TIE String")
+
+# Instantiate a client proxy object, then call it
+hw2 = EchoServerSOAP("http://localhost:9999/EchoServIn")
+try:
+ print hw2.Echo("Test INHERIT String")
+except Exception, e:
+ print "Failed to echo (Inherited): ", e
+
diff --git a/samples/Echo/EchoServer.py b/samples/Echo/EchoServer.py
new file mode 100644
index 0000000..d6eb3d0
--- /dev/null
+++ b/samples/Echo/EchoServer.py
@@ -0,0 +1,67 @@
+#
+# How to build an echo server using the extended code generation
+#
+
+# Import the ZSI stuff you'd need no matter what
+from ZSI.ServiceContainer import ServiceContainer
+
+# This is a new method imported to show it's value
+from ZSI.ServiceContainer import GetSOAPContext
+
+# Import the generated Server Object
+import EchoServer_interface
+
+# Create a Server implementation
+
+# Here we use a Tie approach
+class EchoServer:
+ def authorize(self, auth_info, post, action):
+ print "NOT Authorizing TIE Echo"
+ ctx = GetSOAPContext()
+ print dir(ctx)
+ print "Container: ", ctx.connection
+ print "Parsed SOAP: ", ctx.parsedsoap
+ print "Container: ", ctx.container
+ print "HTTP Headers:\n", ctx.httpheaders
+ print "----"
+ print "XML Data:\n", ctx.xmldata
+ return 0
+
+ def Echo(self, input):
+ return input * 3
+
+# This using a derived server instead
+class EchoServIn(EchoServer_interface.EchoServer):
+ def __init__(self, post='', **kw):
+ EchoServer_interface.EchoServer.__init__(self, post, kw=kw)
+
+ def authorize(self, auth_info, post, action):
+ print "Authorizing INHERIT Echo"
+ ctx = GetSOAPContext()
+ print dir(ctx)
+ print "Container: ", ctx.connection
+ print "Parsed SOAP: ", ctx.parsedsoap
+ print "Container: ", ctx.container
+ print "HTTP Headers:\n", ctx.httpheaders
+ print "----"
+ print "XML Data:\n", ctx.xmldata
+ return 1
+
+ def Echo(self, input):
+ return input * 3
+
+# Here we set up the server
+serviceContainer = ServiceContainer(('localhost', 9999))
+
+# Create the TIE version of the server
+hws = EchoServer()
+hwsi = EchoServer_interface.EchoServer(impl=hws,
+ auth_method_name="authorize")
+serviceContainer.setNode(hwsi, url="/EchoServer")
+
+# Create the Inherited version of the server
+hws2 = EchoServIn()
+serviceContainer.setNode(hws2, url="/EchoServIn")
+
+# Run the service container
+serviceContainer.serve_forever()
diff --git a/samples/Echo/README b/samples/Echo/README
new file mode 100644
index 0000000..079bd4a
--- /dev/null
+++ b/samples/Echo/README
@@ -0,0 +1,144 @@
+
+BACKGROUND
+==========
+
+This is a simple example of the new additions I've been adding to
+ZSI. Among these modifications are:
+
+1) Extended code generation facilities for server and client
+2) Authorization callbacks
+3) ServiceContainer enhancements for implementations to use
+
+
+Extended Code Generation for Server and Client
+----------------------------------------------
+
+These extensions are illustrated by the Echo.wsdl, EchoServer.py and
+EchoClient.py that implement an Echo service. The EchoServer.py file
+has two classes defined that implement the Echo functionality, the
+difference in the two is that one inherits from the generated
+EchoServer_interface class (generated by wsdl2dispatch), and one can
+be passed to the EchoServer_interface constructor with the keyword
+argument impl=<obj>.
+
+These two solutions follow the classic TIE and Derive patterns from
+previous systems like CORBA.
+
+The EchoClient.py implements a simple client for each service and
+invokes the method to do the work. The second call is programmed to
+result in an authorization error.
+
+Authorization Callbacks
+-----------------------
+
+This functionality lets you define a method on your implementation
+object that can be invoked to authorize calls to it from the
+underlying SOAP interface.
+
+ServiceContainer Enhancements
+-----------------------------
+
+I've added the ability to get a CallContext via a ServiceContainer
+module level function GetSOAPContext(). This returns a context object
+for the current request. I've make this thread-safe by keeping a
+global dictionary of contexts indexed by thread id. The requesthandler
+object cleanly creates and destroys these for each incoming request.
+
+The SOAPContext contains:
+
+- a reference to the connection object
+- a reference to the Service Container
+- the HTTP headers
+- the raw xml of the request
+- the Parsed SOAP object
+- the SOAP Action
+
+This should provide all the information a developer could need.
+
+BUILDING
+========
+
+You should (with a fresh checkout from cvs that's been installed) be able to run:
+
+>wsdl2py -e -f Echo.wsdl --simple-naming
+>wsdl2dispatch -e -f Echo.wsdl --simple-naming
+
+Then in separate terminal windows:
+
+Terminal 1:
+>python EchoServer.py
+
+Terminal 2:
+>python EchoClient.py
+
+Expected SERVER Output:
+
+C:\xfer\Echo-extended>EchoServer.py
+Authorizing TIE Echo
+['__doc__', '__init__', '__module__', 'connection', 'container', 'httpheaders',
+'parsedsoap', 'soapaction', 'xmldata']
+Container: <socket._socketobject object at 0x00C03030>
+Parsed SOAP: <ZSI.parse.ParsedSoap instance at 0x00C00878>
+Container: <ZSI.ServiceContainer.ServiceContainer instance at 0x00BEB710>
+HTTP Headers:
+Host: localhost:9999
+Accept-Encoding: identity
+Content-length: 551
+Content-type: text/xml; charset=utf-8
+SOAPAction: "Echo"
+
+----
+XML Data:
+<?xml version="1.0" encoding="utf-8"?>
+<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" >
+<SOAP-ENV:Body>
+<None xmlns="">
+<in_str id="8fbb88" xsi:type="xsd:string">Test TIE String</in_str>
+</None >
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+localhost - - [02/Feb/2005 21:35:00] "POST /EchoServer HTTP/1.1" 200 -
+NOT Authorizing INHERIT Echo
+['__doc__', '__init__', '__module__', 'connection', 'container', 'httpheaders',
+'parsedsoap', 'soapaction', 'xmldata']
+Container: <socket._socketobject object at 0x00C03030>
+Parsed SOAP: <ZSI.parse.ParsedSoap instance at 0x00C5EEB8>
+Container: <ZSI.ServiceContainer.ServiceContainer instance at 0x00BEB710>
+HTTP Headers:
+Host: localhost:9999
+Accept-Encoding: identity
+Content-length: 555
+Content-type: text/xml; charset=utf-8
+SOAPAction: "Echo"
+
+----
+XML Data:
+<?xml version="1.0" encoding="utf-8"?>
+<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" >
+<SOAP-ENV:Body>
+<None xmlns="">
+<in_str id="8f4cb0" xsi:type="xsd:string">Test INHERIT String</in_str>
+</None >
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+
+localhost - - [02/Feb/2005 21:35:00] "POST /EchoServIn HTTP/1.1" 401 -
+
+Expected CLIENT Output:
+
+C:\xfer\Echo-extended>EchoClient.py
+Test TIE StringTest TIE StringTest TIE String
+Failed to echo (Inherited): Not authorized
diff --git a/samples/Echo/buildms.bat b/samples/Echo/buildms.bat
new file mode 100644
index 0000000..8e2bfd9
--- /dev/null
+++ b/samples/Echo/buildms.bat
@@ -0,0 +1,3 @@
+wsdl Echo.wsdl
+csc /t:library EchoService.cs
+csc Echo.cs /r:EchoService.dll \ No newline at end of file
diff --git a/samples/Echo/echo.cs b/samples/Echo/echo.cs
new file mode 100644
index 0000000..d5bab03
--- /dev/null
+++ b/samples/Echo/echo.cs
@@ -0,0 +1,20 @@
+//
+// To use this:
+// wsdl Echo.wsdl
+// csc /t:library EchoService.cs
+// csc Echo.cs /r:EchoService.dll
+
+using System;
+
+class Echo {
+ public static void Main(string[] args) {
+
+ EchoService server = new EchoService();
+
+ string request = "Test C#";
+
+ server.Echo(ref request);
+
+ Console.WriteLine("Return: " + request);
+ }
+} \ No newline at end of file
diff --git a/samples/README b/samples/README
new file mode 100644
index 0000000..ac36da1
--- /dev/null
+++ b/samples/README
@@ -0,0 +1,3 @@
+Sample code, no copyright; use as you see fit.
+ZSI tips and techniques.
+Self-commented, if at all.
diff --git a/samples/poly.py b/samples/poly.py
new file mode 100755
index 0000000..009cc55
--- /dev/null
+++ b/samples/poly.py
@@ -0,0 +1,121 @@
+#! /usr/bin/env python
+
+from ZSI import *
+
+"""Polymorphic containers using TC.Choice
+
+Based on code and text from Dan Gunter <dkgunter@lbl.gov>.
+
+The TC.Choice typecode can be used to create a polymorphic type for
+a specified set of object types. Here's how:
+ 1. Define all your data classes (D1, D2, ...). If they derive from
+ a common base class (Base), some feel you get "cleaner" code.
+ 2. Create a typecode class (TC_D1, TC_D2, ...) for each data class.
+ 3. Create a "base" typecode that uses TC.Choice to do the actual
+ parsing and serializing. Two versions are shown, below.
+Then you can instantiate, e.g., an Array that can handle multiple
+datatypes with:
+ TC.Array("base-class-name", TC_Base(), "MyArray")
+"""
+
+class Base: pass
+class D1(Base): pass
+class D2(Base): pass
+
+class TC_D1(TC.TypeCode): pass
+class TC_D2(TC.TypeCode): pass
+D1.typecode = TC_D1()
+D2.typecode = TC_D2()
+
+# A simple version of TC_Base that is "hardwired" with the types of
+# objects it can handle. We defer setting the choice attribute because
+# with nested containers you could get too much recursion.
+class TC_Base(TC.TypeCode):
+ def parse(self, elt, ps):
+ return self.choice.parse(elt, ps)[1]
+
+ def serialize(self, sw, pyobj, **kw):
+ if not isinstance(pyobj, Base):
+ raise TypeError(str(pyobj.__class__) + " not in type hierarchy")
+ if isinstance(pyobj, D1):
+ self.choice.serialize(sw, ('D1', pyobj), **kw)
+ elif isinstance(pyobj, D2):
+ self.choice.serialize(sw, ('D2', pyobj), **kw)
+ else:
+ raise TypeError(str(pyobj.__class__) + " unknown type")
+ return
+
+ def __getattr__(self, attr):
+ if attr == 'choice':
+ choice = TC.Choice((D1.typecode, D2.typecode), 'Item')
+ self.__dict__['choice'] = choice
+ return choice
+ raise AttributeError(attr)
+
+## Another version that takes a dictionary that maps element names to
+## the python class.
+
+class TC_Polymorphic(TC.TypeCode):
+ def __init__(self, name2class, pname=None, **kw):
+ TC.TypeCode.__init__(self, pname, **kw)
+ self.name2class = name2class
+
+ def parse(self, elt, ps):
+ return self.choice.parse(elt, ps)[1]
+
+ def serialize(self, sw, pyobj, **kw):
+ self.choice.serialize(sw,
+ (self.class2name[pyobj.__class__], pyobj), **kw)
+
+ def __getattr__(self, attr):
+ if attr == 'choice':
+ choice = TC.Choice(
+ [getattr(v, 'typecode') for k,v in self.name2class.items()],
+ 'Item')
+ self.__dict__['choice'] = choice
+ return choice
+ if attr == 'class2name':
+ class2name = {}
+ for k,v in self.name2class.items(): class2name[v] = k
+ self.__dict__['class2name'] = class2name
+ return class2name
+ raise AttributeError(attr)
+
+class P1: pass
+class P2: pass
+P1.typecode = TC.String('s')
+P2.typecode = TC.Integer('i')
+myTC = TC.Array("Base", TC_Polymorphic({'i': P2, 's': P1}))
+
+test = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body xmlns='test-uri'>
+ <array SOAP-ENC:arrayType="Base">
+ <i>34</i>
+ <s>hello</s>
+ <s>34</s>
+ <i>12</i>
+ <s>goodbye</s>
+ </array>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+ps = ParsedSoap(test)
+a = myTC.parse(ps.body_root, ps)
+print a
+
+if 0:
+ # XXX. Does not work. :(
+ b = [ P1(), P1(), P2(), P1() ]
+ b[0].s = 'string'
+ b[1].s = '34'
+ b[2].i = 34
+ b[3].s = 'adios'
+
+ import sys
+ sw = SoapWriter(sys.stdout)
+ myTC.serialize(sw, b)
diff --git a/scripts/wsdl2dispatch b/scripts/wsdl2dispatch
new file mode 100644
index 0000000..c04085a
--- /dev/null
+++ b/scripts/wsdl2dispatch
@@ -0,0 +1,9 @@
+#!/bin/env python
+############################################################################
+# Joshua Boverhof<JRBoverhof@lbl.gov>, LBNL
+# Monte Goode <MMGoode@lbl.gov>, LBNL
+# See Copyright for copyright notice!
+###########################################################################
+
+from ZSI.generate.commands import wsdl2dispatch
+wsdl2dispatch()
diff --git a/scripts/wsdl2py b/scripts/wsdl2py
new file mode 100644
index 0000000..659f866
--- /dev/null
+++ b/scripts/wsdl2py
@@ -0,0 +1,9 @@
+#!/bin/env python
+############################################################################
+# Joshua Boverhof<JRBoverhof@lbl.gov>, LBNL
+# Monte Goode <MMGoode@lbl.gov>, LBNL
+# See Copyright for copyright notice!
+###########################################################################
+
+from ZSI.generate.commands import wsdl2py
+wsdl2py()
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..01bb76c
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,16 @@
+[bdist_rpm]
+release = 1
+requires = PyXML
+doc_files = README CHANGES doc/zsi.css doc/zsi.html
+
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
+[version]
+major = 2
+minor = 0
+patchlevel = 0
+candidate = 3
+
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..37f95a6
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,68 @@
+#! /usr/bin/env python
+# $Header$
+import sys
+try:
+ from setuptools import setup
+ hasSetuptools = True
+except ImportError:
+ from distutils.core import setup
+ hasSetuptools = False
+
+_url = "http://pywebsvcs.sf.net/"
+
+import ConfigParser
+cf = ConfigParser.ConfigParser()
+cf.read('setup.cfg')
+major = cf.getint('version', 'major')
+minor = cf.getint('version', 'minor')
+patchlevel = cf.getint('version', 'patchlevel')
+candidate = cf.getint('version', 'candidate')
+
+_version = "%d.%d" % ( major, minor )
+if patchlevel:
+ _version += '.%d' % patchlevel
+if candidate:
+ _version += '_rc%d' % candidate
+
+try:
+ open('ZSI/version.py', 'r').close()
+except:
+ print 'ZSI/version.py not found; run "make"'
+ sys.exit(1)
+
+_packages = [ "ZSI", "ZSI.generate", "ZSI.wstools"]
+if sys.version_info[0:2] >= (2, 4):
+ _packages.append("ZSI.twisted")
+
+
+# setuptools specific logic
+additional_params = {}
+if hasSetuptools:
+ additional_params['entry_points'] = {
+ 'console_scripts': [
+ 'wsdl2py = ZSI.generate.commands:wsdl2py',
+ 'wsdl2dispatch = ZSI.generate.commands:wsdl2dispatch',
+ ],
+ }
+ additional_params['install_requires'] = [ "PyXML >= 0.8.3", ]
+ additional_params['setup_requires'] = [ "setuptools >= 0.6c3", ]
+ additional_params['dependency_links'] = [
+ "http://sourceforge.net/project/showfiles.php?group_id=6473&package_id=6541&release_id=286213",
+ ]
+else:
+ additional_params['scripts'] = ["scripts/wsdl2py", "scripts/wsdl2dispatch"]
+
+setup(
+ name="ZSI",
+ version=_version,
+ license="Python",
+ packages=_packages,
+ description="Zolera SOAP Infrastructure",
+ author="Rich Salz, et al",
+ author_email="rsalz@datapower.com",
+ maintainer="Rich Salz, et al",
+ maintainer_email="pywebsvcs-talk@lists.sf.net",
+ url=_url,
+ long_description="For additional information, please see " + _url,
+ **additional_params
+)
diff --git a/test/.cvsignore b/test/.cvsignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/test/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/test/README b/test/README
new file mode 100644
index 0000000..6d72f6f
--- /dev/null
+++ b/test/README
@@ -0,0 +1,10 @@
+These tests are in the process of being incorporated in pyunit. Not
+all errors are currently being caught by pyunit, so it is still
+necessary to check the output of the code by hand.
+
+Two top level modules have been provided to run the tests. Use
+"test_zsi.py" to run all of the local tests, or "test_zsi_net.py" to
+run all of the zsi tests including those that require network access.
+
+Additional tests are located in the wsdl2py/ directory; see the README
+there for more information on those.
diff --git a/test/cgicli.py b/test/cgicli.py
new file mode 100755
index 0000000..95f80b0
--- /dev/null
+++ b/test/cgicli.py
@@ -0,0 +1,75 @@
+#! /usr/bin/env python
+import getopt, socket, sys
+
+try:
+ (opts, args) = getopt.getopt(sys.argv[1:],
+ 'h:p:s',
+ ( 'host=', 'port=',
+ 'statusonly', 'help'))
+except getopt.GetoptError, e:
+ print >>sys.stderr, sys.argv[0] + ': ' + str(e)
+ sys.exit(1)
+if args:
+ print sys.argv[0] + ': Usage error; try --help.'
+ sys.exit(1)
+
+hostname, portnum, verbose = 'localhost', 80, 1
+for opt, val in opts:
+ if opt in [ '--help' ]:
+ print '''Options include:
+ --host HOST (-h HOST) Name of server host
+ --port PORT (-p PORT) Port server is listening on
+ --statusonly (-s) Do not output reply packets; just status code
+Default is -h%s -p%d -t%s''' % \
+ (hostname, portnum, ','.join([str(x) for x in tests]))
+ sys.exit(0)
+ if opt in [ '-h', '--host' ]:
+ hostname = val
+ elif opt in [ '-p', '--port' ]:
+ portnum = int(val)
+ elif opt in [ '-s', '--statusonly' ]:
+ verbose = 0
+
+
+IN = '''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
+<SOAP-ENV:Body>
+ <hello/>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+'''
+
+IN = '''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
+<SOAP-ENV:Body>
+ <echo>
+ <SOAP-ENC:int>1</SOAP-ENC:int>
+ <SOAP-ENC:int>2</SOAP-ENC:int>
+ </echo>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+'''
+
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.connect((hostname, portnum))
+f = s.makefile('r+')
+
+f.write('POST /cgi-bin/x HTTP/1.0\r\n')
+f.write('Content-type: text/xml; charset="utf-8"\r\n')
+f.write('Content-Length: %d\r\n\r\n' % len(IN))
+f.write(IN)
+f.flush()
+
+status = f.readline()
+print status,
+while 1:
+ l = f.readline()
+ if l == '': break
+ if verbose: print l,
+
+f.close()
diff --git a/test/test_TCtimes.py b/test/test_TCtimes.py
new file mode 100644
index 0000000..9e38a55
--- /dev/null
+++ b/test/test_TCtimes.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+import unittest, sys, tests_good, tests_bad, time
+from ZSI import *
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
+
+class TestCase(unittest.TestCase):
+ '''Examples from "Definitive XML Schema, Priscilla Walmsley, p237-246
+ '''
+ def check_dateTime_local_offset(self):
+ # UTC with local timezone offset
+ #
+ typecode = TC.gDateTime()
+ off_hour = time.altzone/60/60
+ off_min = time.altzone%60
+ stamp_offset = '1968-04-02T13:20:00+%02d:%02d' %(off_hour,off_min)
+ data = typecode.text_to_data(stamp_offset, None, None)
+ stamp = typecode.get_formatted_content(data)
+
+ correct = "1968-04-01T22:20:00Z"
+ self.failUnless(stamp == correct,
+ 'dateTime with local offset(%s), expecting "%s" got "%s"' %(
+ stamp_offset, correct, stamp))
+
+ def check_valid_dateTime(self):
+ typecode = TC.gDateTime()
+ for i in ('1968-04-02T13:20:00', '1968-04-02T13:20:15.5',
+ '1968-04-02T13:20:00-05:00', '1968-04-02T13:20:00Z'):
+ data = typecode.text_to_data(i, None, None)
+ text = typecode.get_formatted_content(data)
+
+ def check_parse_microseconds(self):
+ good = (1968, 4, 2, 13, 20, 15, 511, 0, 0)
+ typecode = TC.gDateTime()
+ data = typecode.text_to_data('1968-04-02T13:20:15.511', None, None)
+ self.failUnless(data == good,
+ 'did not parse something %s, not equal %s' %(data,good))
+
+ def check_serialize_microseconds(self):
+ dateTime = '1968-04-02T13:20:15.511Z'
+ typecode = TC.gDateTime()
+ text = typecode.get_formatted_content((1968, 4, 2, 13, 20, 15, 511, 0, 0))
+ self.failUnless(text == dateTime,
+ 'did not serialze correctly %s, not equal %s' %(text, dateTime))
+
+ def check_serialize_microseconds_1000(self):
+ bad = (1968, 4, 2, 13, 20, 15, 1000, 0)
+ typecode = TC.gDateTime()
+ self.failUnlessRaises(ValueError, typecode.get_formatted_content, bad)
+
+ def check_serialize_microseconds_lessZero(self):
+ '''ignore negative microseconds
+ '''
+ bad = (1968, 4, 2, 13, 20, 15, -1, 0)
+ typecode = TC.gDateTime()
+ text = typecode.get_formatted_content(bad)
+ typecode.get_formatted_content(bad)
+
+ def check_parse_microseconds2(self):
+ good = (1968, 4, 2, 13, 20, 15, 500, 0, 0)
+ typecode = TC.gDateTime()
+ data = typecode.text_to_data('1968-04-02T13:20:15.5Z', None,None)
+ self.failUnless(data == good,
+ 'did not serialze correctly %s, not equal %s' %(data, good))
+
+ #text = typecode.get_formatted_content((1968, 4, 2, 13, 20, 15, 5, 0, 500))
+ #self.failUnless(text == dateTime,
+ # 'did not serialze correctly %s, not equal %s' %(text, dateTime))
+
+ def check_invalid_dateTime(self):
+ typecode = TC.gDateTime()
+
+ def check_valid_time(self):
+ typecode = TC.gTime()
+ for i in ('13:20:00', '13:20:30.5555', '13:20:00Z'):
+ data = typecode.text_to_data(i, None, None)
+ text = typecode.get_formatted_content(data)
+
+ def broke_valid_time(self):
+ typecode = TC.gTime()
+ data = typecode.text_to_data('13:20:00-05:00', None, None)
+
+ def check_invalid_time(self):
+ typecode = TC.gTime()
+ for i in ('5:20:00', '13:20.5:00',):
+ self.failUnlessRaises(Exception, typecode.text_to_data, i, None, None),
+
+ def broke_invalid_time_no_seconds(self):
+ typecode = TC.gTime()
+ i = '13:20:'
+ self.failUnlessRaises(Exception, typecode.text_to_data, i, None, None)
+
+ def broke_invalid_time_bad_timeofday(self):
+ typecode = TC.gTime()
+ i = '13:65:00'
+ self.failUnlessRaises(Exception, typecode.text_to_data, i, None, None)
+
+ def check_valid_date(self):
+ typecode = TC.gDate()
+ for i in ('1968-04-02', '-0045-01-01', '11968-04-02', '1968-04-02+05:00', '1968-04-02Z'):
+ data = typecode.text_to_data(i, None, None)
+ text = typecode.get_formatted_content(data)
+
+ def check_invalid_date(self):
+ typecode = TC.gDate()
+ for i in ('68-04-02', '1968-4-2', '1968/04/02', '04-02-1968',):
+ self.failUnlessRaises(Exception, typecode.text_to_data, i, None, None),
+
+ def broke_invalid_date_april31(self):
+ # No checks for valid date April 30 days
+ typecode = TC.gDate()
+ self.failUnlessRaises(Exception, typecode.text_to_data, '1968-04-31', None, None),
+
+#
+# Creates permutation of test options: "check", "check_any", etc
+#
+_SEP = '_'
+for t in [i[0].split(_SEP) for i in filter(lambda i: callable(i[1]), TestCase.__dict__.items())]:
+ test = ''
+ for f in t:
+ test += f
+ if globals().has_key(test): test += _SEP; continue
+ def _closure():
+ name = test
+ def _makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestCase, name))
+ return suite
+ return _makeTestSuite
+
+ globals()[test] = _closure()
+ test += _SEP
+
+
+makeTestSuite = check
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+if __name__ == "__main__" : main()
+
+
diff --git a/test/test_t1.py b/test/test_t1.py
new file mode 100644
index 0000000..2cc3936
--- /dev/null
+++ b/test/test_t1.py
@@ -0,0 +1,229 @@
+#!/usr/bin/env python
+import unittest, sys, tests_good, tests_bad, time
+from ZSI import *
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
+class t1TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t1 test case"
+
+ def setUp(self):
+ self.goodTests = []
+ self.badTests = []
+ for key,val in tests_good.__dict__.items():
+ try:
+ if key[0:4] == "test" and int(key[4:]) > 0:
+ self.goodTests.append((key,val))
+ except:
+ pass
+ for key,val in tests_bad.__dict__.items():
+ try:
+ if key[0:4] == "test" and int(key[4:]) > 0:
+ self.badTests.append((key,val))
+ except:
+ pass
+ self.goodTests.sort(lambda a,b: cmp(a[0], b[0]))
+ self.badTests.sort(lambda a,b: cmp(a[0], b[0]))
+
+ def checkt1(self):
+ for key,val in self.badTests:
+ print "\n", "." * 60, key
+ self.failUnlessRaises(ParseException, ParsedSoap, val)
+ for key,val in self.goodTests:
+ print "\n", "." * 60, key
+ ps = ParsedSoap(val)
+
+ ps = ParsedSoap(datatest)
+ elts = ps.data_elements
+
+ self.failUnlessEqual(TC.Integer(None, nillable=True).parse(elts[10], ps),
+ None)
+ self.failUnlessEqual(TC.Ibyte(None, nillable=True).parse(elts[10], ps),
+ None)
+ B = [ TC.Integer('Price'), TC.Integer('p2'), TC.String(unique=1) ]
+ self.failUnlessEqual(TC.Integer(('test-uri', 'Price')).parse(elts[0], ps),
+ 34)
+ self.failUnlessEqual(B[0].parse(elts[0], ps), 34)
+ self.failUnlessEqual(B[1].parse(elts[1], ps), 44)
+ self.failUnlessEqual(B[2].parse(elts[2], ps), u"This is the name")
+ self.failUnlessEqual(TC.HexBinaryString().parse(elts[9], ps), "? A")
+ self.failUnlessEqual(TC.String('Name').parse(elts[2], ps),
+ u"This is the name")
+ self.failUnlessEqual(TC.Any('Price').parse(elts[0], ps), 34)
+ self.failUnlessEqual(TC.Any('n3').parse(elts[4], ps),
+ u"The value of n3")
+ TC.XML('n2').parse(elts[3], ps)
+ nodelist = TC.XML('a2').parse(elts[7], ps)
+ self.failUnlessEqual(TC.String('n3').parse(elts[4], ps),
+ u"The value of n3")
+ self.failUnlessEqual(TC.Base64String('n64').parse(elts[5], ps),
+ u"hello")
+ self.failUnlessEqual(TC.String('n64').parse(elts[5], ps),
+ u"a GVsbG8=")
+ enum = TC.Enumeration(['Red', 'Blue', 'Green'], 'color')
+ self.failUnlessEqual(enum.parse(elts[6], ps), u'Red')
+ self.failUnlessEqual(TC.IEnumeration([44,46,47]).parse(elts[1],ps),
+ 44)
+ S = TC.Struct(None, [TC.String('t'), TC.Integer('i')], inorder=0)
+ pyobj = S.parse(elts[8], ps)
+ S2 = TC.Struct(myclass, [TC.IunsignedShort('i'), TC.String('q:z',
+ minOccurs=0), TC.String('t')], 'a2', typed=0)
+ pyobj2 = S2.parse(elts[8], ps)
+ self.failUnlessEqual(TC.URI().parse(elts[12], ps),
+ u'"http://foo.com/~salz"')
+ self.failUnlessEqual(pyobj["i"], pyobj2.i)
+ self.failUnlessEqual(pyobj["t"], pyobj2.t)
+
+ tcary = TC.Array('SOAP-ENC:int', TC.Integer())
+ nsa = tcary.parse(elts[14],ps)
+ self.failUnlessEqual(nsa, [None, None, None, 12, 13, 14, 15, 16, 17])
+ tcary.sparse = 1
+ sa = tcary.parse(elts[14],ps)
+ self.failUnlessEqual(sa,
+ [(3, 12), (4, 13), (5, 14), (6, 15), (7, 16), (8, 17)])
+
+ """
+ mychoice = TC.Choice([
+ TC.String('n3'),
+ TC.URI('uri'),
+ TC.Integer('Price'),
+ ])
+
+ b = mychoice.parse(elts[0], ps)
+ self.failUnlessEqual(b[0], 'Price')
+ self.failUnlessEqual(b[1], 34)
+ b = mychoice.parse(elts[12], ps)
+ self.failUnlessEqual(b[0], 'uri')
+ self.failUnlessEqual(b[1], u'"http://foo.com/~salz"')
+ b = mychoice.parse(elts[4], ps)
+ self.failUnlessEqual(b[0], 'n3')
+ self.failUnlessEqual(b[1], u'The value of n3')
+ """
+
+ self.failUnlessEqual(TC.Array(('test-uri','x'), TC.Any()).parse(elts[15], ps),
+ [u'The value of n3', u'rich salz', 13])
+ self.failUnlessEqual(TC.Struct(None,(TC.FPfloat('a'), TC.Decimal('b'),
+ TC.FPdouble('c'))).parse(elts[13],ps),
+ {'a': 6.9000000000000004, 'c':
+ TC._make_inf(), 'b': 0.0})
+ nsdict = ps.GetElementNSdict(ps.header)
+ nsdict[''] = "http://www.zolera.com/ns/"
+ nsdict['q'] = 'q-namespace-uri'
+ sio = StringIO.StringIO()
+ z = SoapWriter(sio, header=ps.header_elements, nsdict=nsdict)
+ z.serialize(pyobj2, S2)
+ S2.inline = 1
+ S2.typed = 0
+ tc = TC.gDateTime('dt')
+ z.serialize(pyobj2, S2)
+ z.serialize(pyobj, S)
+ #z.serialize(('n3', '******this is the value of a union'), mychoice)
+ z.serialize('uri:some/place/special', TC.XML('foo', nsdict=nsdict))
+ tcary.sparse = False
+ z.serialize(nsa, tcary, childnames='tt')
+ tcary.sparse = True
+ z.serialize(sa, tcary, name='MYSPARSEARRAY')
+ z.serialize(time.time(), tc)
+ z.serialize(time.time(), TC.gTime('monthday'))
+ z.serialize('$$$$$foo<', TC.String(textprotect=0))
+ self.failUnlessEqual(TC.Any().parse(elts[11], ps),
+ {'urt-i': 12, 'urt-t': u'rich salz'})
+
+ try:
+ a = bar()
+ except Exception, e:
+ f = FaultFromException(e, 0, sys.exc_info()[2])
+ print f.AsSOAP()
+ print
+ print
+ print FaultFromNotUnderstood('myuri', 'dalocalname', actor='cher').AsSOAP()
+ print
+ print
+ print FaultFromActor('actor:i:dont:understand').AsSOAP()
+
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t1TestCase, "check"))
+ return suite
+
+## exceptions
+def foo():
+ '''dummy'''
+ return 3 / 0
+
+def bar():
+ return foo() + 2
+
+class zParseException: pass
+
+class myclass:
+ def __init__(self, name=None):
+ self.name = name or id(self)
+ self.z = 'z value'
+ def __str__(self):
+ return 'myclass-%s-(%d,"%s")' % (self.name, self.i, self.t) + \
+ str(self.z)
+
+datatest = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header xmlns:t="http://www.zolera.com/ns/" xmlns:q='"'>
+ <t:sometag SOAP-ENV:mustUnderstand="1">you must grok sometag</t:sometag>
+ </SOAP-ENV:Header>
+ <SOAP-ENV:Body xmlns='test-uri'>
+ <root SOAP-ENC:root='1'/>
+ <Price xsi:type='xsd:integer'>34</Price> <!-- 0 -->
+ <SOAP-ENC:byte>44</SOAP-ENC:byte> <!-- 1 -->
+ <Name>This is the name</Name> <!-- 2 -->
+ <n2><xmldoc><![CDATA[<greeting>Hello</greeting>]]></xmldoc></n2> <!-- 3 -->
+ <n3 href='#zzz' xsi:type='SOAP-ENC:string'/> <!-- 4 -->
+ <n64>a GVsbG8=</n64> <!-- 5 -->
+ <SOAP-ENC:string>Red</SOAP-ENC:string> <!-- 6 -->
+ <a2 href='#tri2'/> <!-- 7 -->
+ <a2><i>12</i><t>rich salz</t></a2> <!-- 8 -->
+ <xsd:hexBinary>3F2041</xsd:hexBinary> <!-- 9 -->
+ <nullint xsi:nil='1'/> <!-- 10 -->
+ <Anytest><urt-i xsi:type='SOAP-ENC:byte'>12</urt-i>
+ <urt-t id="urtid"
+ xsi:type="xsd:string">rich salz</urt-t></Anytest> <!-- 11 -->
+ <uri>"http://foo.com/%7Esalz"</uri> <!-- 12 -->
+ <floattest> <!-- 13 -->
+ <a>6.9</a> <b>-0</b> <c>INF</c>
+ </floattest>
+ <atest SOAP-ENC:offset='[3]' SOAP-ENC:arrayType="x"> <!-- 14 -->
+ <i>12</i>
+ <SOAP-ENC:integer id='n13'>13</SOAP-ENC:integer>
+ <i>14</i>
+ <i>15</i>
+ <i>16</i>
+ <i>17</i>
+ </atest>
+ <sarray SOAP-ENC:arrayType="struct"> <!-- 15 -->
+ <i href="#zzz" xsi:type='xsd:string'/>
+ <i href="#urtid"/>
+ <thing href="#n13"/>
+ </sarray>
+ <xpath>//sarray</xpath> <!-- 16 -->
+ <z xmlns='myns' xsi:type='SOAP-ENC:string' id='zzz'>The value of n3</z>
+ <zz xmlns='myns2' id='tri2'>
+ <inner xmlns='myns2' >
+ <f1>content</f1>
+ <sec xmlns='myns2' >ond</sec >
+ </inner>
+ </zz>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+
+if __name__ == "__main__" : main()
+
+
diff --git a/test/test_t2.py b/test/test_t2.py
new file mode 100644
index 0000000..75c2731
--- /dev/null
+++ b/test/test_t2.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+import unittest, sys
+from ZSI import *
+
+
+class t2TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t2 test case"
+
+ def checkt2(self):
+ try:
+ ps = ParsedSoap(IN)
+ except ParseException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
+ except Exception, e:
+ # Faulted while processing; assume it's in the
+ # header.
+ print >>OUT, FaultFromException(e, 1).AsSOAP()
+ self.fail()
+ # We are not prepared to handle any actors or mustUnderstand elements.
+ # Arbitrary fault back with the first one found.
+ a = ps.WhatActorsArePresent()
+ if len(a):
+ print >>OUT, FaultFromActor(a[0]).AsSOAP()
+ self.fail()
+ mu = ps.WhatMustIUnderstand()
+ if len(mu):
+ uri, localname = mu[0]
+ print >>OUT, FaultFromNotUnderstood(uri, localname).AsSOAP()
+ self.fail()
+
+
+ try:
+ player = ps.Parse(Player)
+ except EvaluateException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
+
+ try:
+ import operator
+ total = reduce(operator.add, player.Scores, 0)
+ result = Average(foo(total, len(player.Scores)))
+ sw = SoapWriter().serialize(result)
+ print >>OUT, str(sw)
+ except Exception, e:
+ print >>OUT, FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP()
+ self.fail()
+
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t2TestCase, "check"))
+ return suite
+
+
+class Player:
+ '''Input class.'''
+ def __init__(self, name=None):
+ pass
+Player.typecode = TC.Struct(Player, [ TC.String('Name', optional=1),
+ TC.Array('xsd:integer', TC.Integer(),
+ 'Scores'), ], 'GetAverage')
+class Average:
+ '''Output class.'''
+ def __init__(self, average):
+ self.average = average
+Average.typecode = TC.Struct(Average, [ TC.Integer('average'),
+ ], 'GetAverageResponse', inline=1)
+
+def bar(total, len):
+ return total / len
+
+def foo(total, len):
+ return bar(total, len)
+
+OUT = sys.stdout
+IN='''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/">
+ <SOAP-ENV:Header>
+ <trans SOAP-ENV:mustUnderstand="0"/>
+ </SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <GetAverage>
+ <Scores SOAP-ENC:arrayType="xsd:integer">
+ <i>84</i>
+ <xxi>101</xxi>
+ <foi>200</foi>
+ <izzz>4</izzz>
+ </Scores>
+ <Name>John Doe</Name>
+ </GetAverage>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+
+if __name__ == "__main__" : main()
+
+
diff --git a/test/test_t3.py b/test/test_t3.py
new file mode 100644
index 0000000..837b52b
--- /dev/null
+++ b/test/test_t3.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+import unittest
+from ZSI import *
+from ZSI.wstools.logging import setBasicLoggerDEBUG
+setBasicLoggerDEBUG()
+
+class t3TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t3 test case"
+
+ def checkt3(self):
+ a = None
+ try:
+ 3 / 0
+ except Exception, e:
+ a = e
+ f = FaultFromException(a, 0)
+ text = f.AsSOAP()
+ i = 0
+ for l in text.split('\n'):
+ print i, l
+ i += 1
+ ps = ParsedSoap(text)
+ if ps.IsAFault():
+ f = FaultFromFaultMessage(ps)
+ print f.AsSOAP()
+ self.failUnless(f.AsSOAP().find(str(a)) > 0)
+ print '--'*20
+
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t3TestCase, "check"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+
+if __name__ == "__main__" : main()
+
+
diff --git a/test/test_t4.py b/test/test_t4.py
new file mode 100644
index 0000000..e2f7a3c
--- /dev/null
+++ b/test/test_t4.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+import unittest, sys
+from ZSI import *
+from ZSI import resolvers
+from xml.dom.ext import PrettyPrint
+
+OUT = sys.stdout
+IN='''<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/">
+ <SOAP-ENV:Body>
+ <hreftest>
+ <xmltest href="http://www-itg.lbl.gov/~kjackson/zsi.xsd"/>
+ <stringtest href="http://www.microsoft.com"/>
+ </hreftest>
+ </SOAP-ENV:Body>
+ </SOAP-ENV:Envelope>
+ '''
+
+class t4TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t4 test case"
+
+ def checkt4(self):
+ try:
+ r = resolvers.NetworkResolver(['http:'])
+ ps = ParsedSoap(IN, resolver=r.Resolve)
+ except ParseException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ print >>OUT, FaultFromException(e, 1, sys.exc_info()[2]).AsSOAP()
+ self.fail()
+ print 'resolving'
+ typecode = TC.Struct(None, [ TC.XML('xmltest'),
+ TC.String('stringtest', resolver=r.Opaque), ])
+ try:
+ dict = ps.Parse(typecode)
+ except EvaluateException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
+ except Exception, e:
+ # Faulted while processing; now it's the body
+ print >>OUT, FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP()
+ self.fail()
+ PrettyPrint(dict['xmltest'])
+ print '**', dict['stringtest'], '**'
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t4TestCase, "check"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+
+if __name__ == "__main__" : main()
+
+
diff --git a/test/test_t5.py b/test/test_t5.py
new file mode 100644
index 0000000..863e02f
--- /dev/null
+++ b/test/test_t5.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+import unittest, multifile, mimetools
+from ZSI import *
+from ZSI import resolvers
+from xml.dom import Node
+from xml.dom.ext.reader import PyExpat
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
+class t5TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t5 test case"
+
+ def checkt5(self):
+ istr = StringIO.StringIO(intext)
+ m = mimetools.Message(istr)
+ if m.gettype()[0:10] == "multipart/":
+ cid = resolvers.MIMEResolver(m['content-type'], istr)
+ xml = cid.GetSOAPPart()
+ print 'xml=', xml.getvalue()
+ for h,b in cid.parts:
+ print h, b.read()
+ dom = PyExpat.Reader().fromStream(xml)
+ print dom
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t5TestCase, "check"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+intext = '''Content-Type: multipart/mixed; boundary="sep"
+Subject: testing
+
+skip this
+
+--sep
+Content-type: text/xml
+
+<foo xmlns='www.zolera.com'>hello world</foo>
+--sep
+Content-Type: text/plain
+content-id: <part111@example.zolera.com>
+
+this is some plain text
+--sep
+content-type: application/skipme
+
+do not see this
+okay?
+--sep
+Content-Type: text/xml
+Content-ID: <part2@example.zolera.com>
+
+<xml>spoken</xml>
+--sep--
+'''
+
+if __name__ == "__main__" : main()
+
+
diff --git a/test/test_t6.py b/test/test_t6.py
new file mode 100644
index 0000000..b5ff9f6
--- /dev/null
+++ b/test/test_t6.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+import unittest, sys, multifile, mimetools, base64
+from xml.dom import Node
+from xml.dom.ext.reader import PyExpat
+from ZSI import *
+from ZSI import resolvers
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
+class t6TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t6 test case"
+
+ def checkt6(self):
+ try:
+ istr = StringIO.StringIO(intext)
+ m = mimetools.Message(istr)
+ cid = resolvers.MIMEResolver(m['content-type'], istr)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ except ParseException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
+ except Exception, e:
+ # Faulted while processing; assume it's in the header.
+ print >>OUT, FaultFromException(e, 1, sys.exc_info()[2]).AsSOAP()
+ self.fail()
+
+ try:
+ dict = ps.Parse(typecode)
+ except Exception, e:
+ # Faulted while processing; now it's the body
+ print >>OUT, FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP()
+ self.fail()
+
+ self.failUnlessEqual(dict['stringtest'], strExtTest,
+ "Failed to extract stringtest correctly")
+ print base64.encodestring(cid['partii@zolera.com'].read())
+ v = dict['b64']
+ print type(v), 'is type(v)'
+ self.failUnlessEqual(cid['partii@zolera.com'].getvalue(), v,
+ "mismatch")
+ print base64.encodestring(v)
+ from ZSI.wstools.c14n import Canonicalize
+ z = dict['xmltest']
+ print type(z), z
+ print Canonicalize(z)
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t6TestCase, "check"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+OUT = sys.stdout
+typecode = TC.Struct(None, [
+ TC.String('b64'),
+ TC.String('stringtest'),
+ TC.XML('xmltest'),
+ ])
+
+intext='''Return-Path: <rsalz@zolera.com>
+Received: from zolera.com (os390.zolera.com [10.0.1.9])
+ by zolera.com (8.11.0/8.11.0) with ESMTP id f57I2sf00832
+ for <rsalz@zolera.com>; Thu, 7 Jun 2001 14:02:54 -0400
+Sender: rsalz@zolera.com
+Message-ID: <3B1FC1D1.FF6B21B4@zolera.com>
+Date: Thu, 07 Jun 2001 14:02:57 -0400
+From: Rich Salz <rsalz@zolera.com>
+X-Mailer: Mozilla 4.72 [en] (X11; U; Linux 2.2.14-5.0 i686)
+X-Accept-Language: en
+MIME-Version: 1.0
+To: rsalz@zolera.com
+Subject: mime with attachments
+Content-Type: multipart/mixed;
+ boundary="------------68E4BAC5B266315E42428C64"
+Status: R
+
+This is a multi-part message in MIME format.
+--------------68E4BAC5B266315E42428C64
+Content-Type: text/plain; charset=us-ascii
+Content-Transfer-Encoding: 7bit
+
+<SOAP-ENV:Envelope
+ xmlns="http://www.example.com/schemas/TEST"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:ZSI="http://www.zolera.com/schemas/ZSI/">
+<SOAP-ENV:Body>
+<hreftest>
+ <stringtest href="cid:part1@zolera.com"/>
+ <b64 href="cid:partii@zolera.com"/>
+ <xmltest href="cid:12@zolera.com"/>
+</hreftest>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+--------------68E4BAC5B266315E42428C64
+Content-Type: text/plain; charset=us-ascii;
+ name="abs.txt"
+Content-Transfer-Encoding: 7bit
+Content-ID: <part1@zolera.com>
+Content-Disposition: inline;
+ filename="abs.txt"
+
+
+Digitial Signatures in a Web Services World
+
+An influential Forrestor report created the term inverted security: it's
+not about who you keep out, it's about who you let in. Customer portals,
+without a costly PKI deployment or application integration issues.
+
+--------------68E4BAC5B266315E42428C64
+Content-Type: application/pdf;
+ name="gmpharma.pdf"
+Content-Transfer-Encoding: base64
+Content-ID: <partii@zolera.com>
+Content-Disposition: inline;
+ filename="gmpharma.pdf"
+
+JVBERi0xLjINJeLjz9MNCjQzIDAgb2JqDTw8IA0vTGluZWFyaXplZCAxIA0vTyA0NSANL0gg
+WyAxMTQ0IDM5NiBdIA0vTCA2NjkwMiANL0UgMTAyODIgDS9OIDkgDS9UIDY1OTI0IA0+PiAN
+RB3nwVOQH9JpmFv6Ri2Zq7mlddSS2B5WcZwvAP+gy9QtuYlfqj1rsi9WqJOszzHXmXZ8fXxK
+XBBztIpgbkRrd+SGtY4QXo0fX0VN86uKXwtrkd7h1qiq2FUtXl6uNfnCoyX1Dve1O3RPRyhG
+sKn6fLMb+uSSIHPQkClRBwu5gechz/1PBUBSB34jXbPdMTIb+/wRP+pauSAhLBzFELDOgk5b
+PaIPAnIudFovQTc7Df2Ws9Atz4Bua+oINphIOojogG5LP3Tb3oNu8bsmuK+wFXEdbfgFIx+G
+gKULYx5A2WnaDXB5JeoRQg90S0HcX2dCPmRCqDXB/aX34KujsPwJ/UpRdxXPeAftDkQS6hag
+bh/yTOiUyqBz9CzxnyMYQGDO0jrUZ47kkWfmYvVg
+--------------68E4BAC5B266315E42428C64
+Content-ID: <12@zolera.com>
+
+<foo xmlns="example.com" xmlns:Z="zolera">
+ this is a foo
+ <b xmlns:Z="zolera">redundnant ns decl</b>
+ <b Z:x="this was first" Z:a="2nd-orig">b test</b>
+</foo>
+
+--------------68E4BAC5B266315E42428C64--
+'''
+
+strExtTest = '''
+Digitial Signatures in a Web Services World
+
+An influential Forrestor report created the term inverted security: it's
+not about who you keep out, it's about who you let in. Customer portals,
+without a costly PKI deployment or application integration issues.
+
+'''
+
+if __name__ == "__main__" : main()
+
+
diff --git a/test/test_t7.py b/test/test_t7.py
new file mode 100644
index 0000000..0c71405
--- /dev/null
+++ b/test/test_t7.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+import unittest, sys
+from ZSI import *
+
+
+class t7TestCase(unittest.TestCase):
+ "Test case wrapper for old ZSI t7 test case"
+
+ def checkt7(self):
+ ps = ParsedSoap(text)
+
+ tcdict = TC.Apache.Map('c-gensym1')
+ tclist = TC.Apache.Map('c-gensym1', aslist=1)
+
+ d = tcdict.parse(ps.body_root, ps)
+ self.assertEqual(d, { u'a':123, '\x00\x01':456 })
+ print 'as dictionary\n', d
+
+ l = tclist.parse(ps.body_root, ps)
+ self.assertEqual(l, [('\x00\x01', 456), (u'a', 123)])
+ print '\n', '=' * 30
+ print 'as list\n', l
+
+ print '\n', '=' * 30
+ sw = SoapWriter()
+ sw.serialize(d, tcdict)
+ print >>sys.stdout, sw
+
+ print '\n', '=' * 30
+ sw = SoapWriter()
+ sw.serialize(l, tclist)
+ print >>sys.stdout, sw
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(t7TestCase, "check"))
+ return suite
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+text = '''
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/1999/XMLSchema"
+ xmlns:xmlsoap="http://xml.apache.org/xml-soap">
+<SOAP-ENV:Body>
+<c-gensym1 xsi:type="xmlsoap:Map">
+ <item>
+ <key xsi:type="SOAP-ENC:base64">AAE=</key>
+ <value xsi:type="xsd:int">456</value>
+ </item>
+ <item>
+ <key xsi:type="xsd:string">a</key>
+ <value xsi:type="xsd:int">123</value>
+ </item>
+</c-gensym1>
+</SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+'''
+
+
+if __name__ == "__main__" : main()
+
+
diff --git a/test/test_t8.py b/test/test_t8.py
new file mode 100644
index 0000000..bc5b278
--- /dev/null
+++ b/test/test_t8.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python
+import unittest, sys, types, time
+from ZSI import TC, SoapWriter, ParsedSoap, EvaluateException
+from ZSI.wstools.Namespaces import SCHEMA, SOAP
+
+NSDICT = {'tns':'xmlns:tns="urn:a"',
+ 'xsi':'xmlns:xsi="%s"' %SCHEMA.XSI3,
+ 'xsd':'xmlns:xsd="%s"' %SCHEMA.XSD3,
+ 'soap':'xmlns:SOAP-ENC="%s"' %SOAP.ENC,
+}
+
+class AnyTestCase(unittest.TestCase):
+ "Test Any serialize and parse"
+
+ def check_empty_array(self):
+ """Empty Array returned as list()
+ """
+ data = []
+ s = str(SoapWriter().serialize(data,TC.Any(aslist=True)))
+ p = ParsedSoap(s).Parse(TC.Any())
+ self.failUnless(data==p, 'expecting "%s", got "%s"' %(data,p))
+
+ def check_empty_struct(self):
+ """Empty Struct is None, maybe dict() makes more sense, but this
+ is fairly hard to determine if not typed (which is the norm).
+ """
+ data = {}
+ s = str(SoapWriter().serialize(data,TC.Any()))
+ p = ParsedSoap(s).Parse(TC.Any())
+ self.failUnless(p==None, 'expecting "%s", got "%s"' %(None,p))
+
+ def check_parse_empty_all(self):
+ # None
+ skip = [TC.FPEnumeration, TC.Enumeration, TC.IEnumeration, TC.List, TC.Integer]
+ for typeclass in filter(lambda c: type(c) in [types.ClassType,type] and not issubclass(c, TC.String) and issubclass(c, TC.SimpleType), TC.__dict__.values()):
+ if typeclass in skip: continue
+ tc = typeclass()
+ sw = SoapWriter()
+ sw.serialize(None, typecode=tc, typed=True)
+ soap = str(sw)
+ ps = ParsedSoap(soap)
+ parsed = ps.Parse(TC.Any())
+ self.assertEqual(None, parsed)
+
+ def check_parse_empty_string(self):
+ # Empty String
+ typecodes = TC.Any.parsemap.values()
+ for tc in filter(lambda c: isinstance(c, TC.String), TC.Any.parsemap.values()):
+ sw = SoapWriter()
+ sw.serialize("", typecode=tc, typed=True)
+ soap = str(sw)
+ ps = ParsedSoap(soap)
+ parsed = ps.Parse(TC.Any())
+ self.assertEqual("", parsed)
+
+ def check_builtins(self):
+ myInt,myLong,myStr,myDate,myFloat = 123,2147483648,\
+ u"hello", time.gmtime(), 1.0001
+ orig = [myInt,myLong,myStr,myDate,myFloat]
+
+ sw = SoapWriter()
+ sw.serialize(orig, typecode=TC.Any(pname="builtins", aslist=True))
+
+ ps = ParsedSoap(str(sw))
+ parsed = ps.Parse(TC.Any())
+ self.assertEqual(len(orig), len(parsed))
+
+ self.assertEqual(myInt, parsed[0])
+ self.assertEqual(myLong, parsed[1])
+ self.assertEqual(myStr, parsed[2])
+ self.assertEqual(myDate[0:6], parsed[3][0:6])
+ self.assertEqual(myFloat, parsed[4])
+
+ self.assertEqual(type(myInt), type(parsed[0]))
+ self.assertEqual(type(myLong), type(parsed[1]))
+ self.assertEqual(type(myStr), type(parsed[2]))
+ self.assertEqual(tuple, type(parsed[3]))
+ self.assertEqual(type(myFloat), type(parsed[4]))
+
+ def check_any_nill(self):
+ result = ['23', {'a' : None, 'b': 5}]
+ soap = str(SoapWriter().serialize(result, TC.Any(pname="NilRequest", nillable=True, aslist=True)))
+
+ ps = ParsedSoap(soap)
+ tc = TC.Any(nillable=True)
+ pyobj = ps.Parse(tc)
+
+ def check_any_compound(self):
+ # from zsi developer's guide
+ xml = """
+<tns:foo %(tns)s %(xsi)s %(soap)s>
+ <tns:i xsi:type="SOAP-ENC:integer">12</tns:i>
+ <tns:name xsi:type="SOAP-ENC:string">Hello world</tns:name>
+</tns:foo>""" %NSDICT
+
+ ps = ParsedSoap(xml, envelope=False)
+ self.failUnless(ps.Parse(TC.Any()) == {'i': 12, 'name': 'Hello world'})
+ self.failUnless(ps.Parse(TC.Any(aslist=True)) == [12, 'Hello world'])
+
+ def check_any_typed_soap_integer(self):
+ # from zsi developer's guide
+ value = 12
+ d = dict(value=value)
+ d.update(NSDICT)
+ xml = """<tns:i xsi:type="SOAP-ENC:integer" %(xsi)s %(soap)s %(tns)s>%(value)d</tns:i>""" %d
+ ps = ParsedSoap(xml, envelope=False)
+ self.failUnless(ps.Parse(TC.Any()) == value)
+
+ def check_any_typed_xsd_int(self):
+ # from zsi developer's guide
+ value = 12
+ d = dict(value=value)
+ d.update(NSDICT)
+ xml = """<tns:i xsi:type="xsd:int" %(xsi)s %(soap)s %(tns)s %(xsd)s>%(value)d</tns:i>""" %d
+ ps = ParsedSoap(xml, envelope=False)
+ self.failUnless(ps.Parse(TC.Any()) == value)
+
+ def check_any_typed_nonNegativeInteger(self):
+ # from zsi developer's guide
+ value = 12
+ d = dict(value=value)
+ d.update(NSDICT)
+ xml = """<tns:i xsi:type="xsd:nonNegativeInteger" %(xsi)s %(soap)s %(tns)s %(xsd)s>%(value)d</tns:i>""" %d
+ ps = ParsedSoap(xml, envelope=False)
+ self.failUnless(ps.Parse(TC.Any()) == value)
+
+ def check_any_untyped_int(self):
+ # from zsi developer's guide
+ d = dict(value=12)
+ d.update(NSDICT)
+ xml = """<tns:i %(tns)s>12</tns:i>""" %NSDICT
+ ps = ParsedSoap(xml, envelope=False)
+ self.failUnlessRaises(EvaluateException, ps.Parse, TC.Any())
+
+
+#
+# Creates permutation of test options: "check", "check_any", etc
+#
+_SEP = '_'
+for t in [i[0].split(_SEP) for i in filter(lambda i: callable(i[1]), AnyTestCase.__dict__.items())]:
+ test = ''
+ for f in t:
+ test += f
+ if globals().has_key(test): test += _SEP; continue
+ def _closure():
+ name = test
+ def _makeTestSuite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(AnyTestCase, name))
+ return suite
+ return _makeTestSuite
+
+ globals()[test] = _closure()
+ test += _SEP
+
+makeTestSuite = check
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+if __name__ == "__main__" : main()
+
+
diff --git a/test/test_t9.py b/test/test_t9.py
new file mode 100644
index 0000000..d2b6c84
--- /dev/null
+++ b/test/test_t9.py
@@ -0,0 +1,363 @@
+#!/usr/bin/env python
+import unittest, sys, sha, base64
+from ZSI import _get_element_nsuri_name
+from ZSI.parse import ParsedSoap
+from ZSI.wstools.c14n import Canonicalize
+from ZSI.wstools.Namespaces import WSA200403, SOAP
+from cStringIO import StringIO
+
+
+class CanonicalizeFromTestCase(unittest.TestCase):
+ "c14n tests, this has nothing to do with ws-addressing."
+
+ def setUp(self):
+ self.ps = ParsedSoap(XML_INST1)
+ self.el = filter(lambda el: _get_element_nsuri_name(el) == (WSA200403.ADDRESS, "From"),
+ self.ps.header_elements)[0]
+
+ def tearDown(self):
+ del self.ps
+ del self.el
+
+ def check_c14n(self):
+ """http://www.w3.org/TR/xml-c14n
+ """
+ s = StringIO()
+ Canonicalize(self.el, s, unsuppressedPrefixes=None)
+ cxml = s.getvalue()
+ d1 = base64.encodestring(sha.sha(C14N_INC1).digest()).strip()
+ d2 = base64.encodestring(sha.sha(cxml).digest()).strip()
+ self.assertEqual(d1, d2)
+ self.assertEqual(d1, C14N_INC1_DIGEST)
+
+ def check_c14n_exc(self):
+ """http://www.w3.org/TR/xml-exc-c14n/
+ """
+ s = StringIO()
+ Canonicalize(self.el, s, unsuppressedPrefixes=[])
+ cxml = s.getvalue()
+ d1 = base64.encodestring(sha.sha(C14N_EXCL1).digest()).strip()
+ d2 = base64.encodestring(sha.sha(cxml).digest()).strip()
+ self.assertEqual(d1, C14N_EXCL1_DIGEST)
+ self.assertEqual(d1, d2)
+
+ def check_c14n_exc2_unsuppressed(self):
+ """http://www.w3.org/TR/xml-exc-c14n/
+ The method of canonicalization described in this specification receives
+ an InclusiveNamespaces PrefixList parameter, which lists namespace prefixes
+ that are handled in the manner described by the Canonical XML Recommendation
+ """
+ s = StringIO()
+ Canonicalize(self.el, s, unsuppressedPrefixes=['xsi', 'xsd'])
+ cxml = s.getvalue()
+ d1 = base64.encodestring(sha.sha(C14N_EXCL2).digest()).strip()
+ d2 = base64.encodestring(sha.sha(cxml).digest()).strip()
+ self.assertEqual(d1, C14N_EXCL2_DIGEST)
+ self.assertEqual(d1, d2)
+
+ def check_c14n_exc3(self):
+ """http://www.w3.org/TR/xml-exc-c14n/
+ tests if a namespace defined in a parent node to the top node
+ to be canonicalized is added when discovered that this namespace
+ is used.
+ """
+ self.ps = ParsedSoap(XML_INST2)
+ self.el = self.ps.body
+
+ s = StringIO()
+ Canonicalize(self.el, s, unsuppressedPrefixes=[])
+ cxml = s.getvalue()
+ print cxml
+ d1 = base64.encodestring(sha.sha(C14N_EXCL3).digest()).strip()
+ d2 = base64.encodestring(sha.sha(cxml).digest()).strip()
+ self.assertEqual(d1, C14N_EXCL3_DIGEST)
+ self.assertEqual(d1, d2)
+
+ def xcheck_c14n_exc4(self):
+ RCVDIGEST = "jhTbi7gWlY9oLqsRr+EZ0bokRFA="
+ CALDIGEST = "IkMyI4zCDlK41qE7sZxvkFHJioU="
+
+ d1 = base64.encodestring(sha.sha(WRONG).digest()).strip()
+ d2 = base64.encodestring(sha.sha(CORRECT).digest()).strip()
+
+ ps = ParsedSoap(XML_INST4)
+ el = filter(lambda el: _get_element_nsuri_name(el) == (WSA200403.ADDRESS, "MessageID"),
+ ps.header_elements)[0]
+
+ s = StringIO()
+ Canonicalize(el, s, unsuppressedPrefixes=[])
+ cxml = s.getvalue()
+ print "-- "*20
+ print cxml
+ print "-- "*20
+ d3 = base64.encodestring(sha.sha(cxml).digest()).strip()
+
+ self.assertEqual(d1, RCVDIGEST)
+ self.assertEqual(d2, CALDIGEST)
+ self.assertEqual(d3, CALDIGEST)
+
+
+def makeTestSuite():
+ suite = unittest.TestSuite()
+ #suite.addTest(unittest.makeSuite(CanonicalizeFromTestCase, "check"))
+ suite.addTest(unittest.makeSuite(CanonicalizeFromTestCase, "xcheck"))
+ return suite
+
+
+C14N_EXCL1_DIGEST = "xSOXT+dlQwo5uT9PbK08of6W9PM="
+C14N_EXCL1 = """<wsa:From xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" ns3:Id="id-7680063" soapenv:mustUnderstand="0"><wsa:Address>http://bosshog.lbl.gov:9999/wsrf/services/SecureCounterService</wsa:Address><wsa:ReferenceProperties><ns1:CounterKey xmlns:ns1="http://counter.com" ns3:Id="10112">10577413</ns1:CounterKey></wsa:ReferenceProperties></wsa:From>"""
+
+C14N_INC1_DIGEST = "qdU4f7/+BeHV/JlVGIPM90fNeV8="
+C14N_INC1 = """<wsa:From xmlns:ns1="http://counter.com" xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ns3:Id="id-7680063" soapenv:mustUnderstand="0"><wsa:Address>http://bosshog.lbl.gov:9999/wsrf/services/SecureCounterService</wsa:Address><wsa:ReferenceProperties><ns1:CounterKey ns3:Id="10112">10577413</ns1:CounterKey></wsa:ReferenceProperties></wsa:From>"""
+
+C14N_EXCL2_DIGEST = "+IEqF6DRo36Bh93A06S7C4Cmcuo="
+C14N_EXCL2 = """<wsa:From xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ns3:Id="id-7680063" soapenv:mustUnderstand="0"><wsa:Address>http://bosshog.lbl.gov:9999/wsrf/services/SecureCounterService</wsa:Address><wsa:ReferenceProperties><ns1:CounterKey xmlns:ns1="http://counter.com" ns3:Id="10112">10577413</ns1:CounterKey></wsa:ReferenceProperties></wsa:From>"""
+
+
+C14N_EXCL3_DIGEST = "VJvTr+Mx3TeWsQY6iwGbhAJ9/eA="
+C14N_EXCL3 = """<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-28219008"><RequestSecurityTokenResponse xmlns="http://schemas.xmlsoap.org/ws/2004/04/trust"><wsa:EndpointReference xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"><wsa:Address>http://131.243.2.147:8888/wsrf/services/DelegationService</wsa:Address><wsa:ReferenceProperties><ns1:DelegationKey xmlns:ns1="http://www.globus.org/08/2004/delegationService">8adaa710-ba01-11da-bc99-cbed73daa755</ns1:DelegationKey></wsa:ReferenceProperties></wsa:EndpointReference></RequestSecurityTokenResponse></soapenv:Body>"""
+
+
+XML_INST1 = """<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Header>
+ <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:SignedInfo>
+ <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="soapenv wsa xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:CanonicalizationMethod>
+ <ds:SignatureMethod Algorithm="http://www.globus.org/2002/04/xmlenc#gssapi-sign"/>
+ <ds:Reference URI="#id-8409752">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="wsa xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>m9pihAqIBdcdk7ytDvccj89eWi8=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-11434871">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>ofD+Ket5kzR2u/5jWKbFTMtmigk=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-19645447">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>SoQ7RlJa3r94weDWBuWAg/BvydQ=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-5428820">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>z6sCEkkRJrCuY/C0S5b+46WfyMs=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-7680063">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>+IEqF6DRo36Bh93A06S7C4Cmcuo=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-28476580">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xsd xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform>
+ </ds:Transforms>
+ <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>NFltkKAJpmMkPbJQj5MW1qVceto=</ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue>AAAAAAAAAAMAAAvZTrXlZjRSO7tP12tId+lehprEKgk=</ds:SignatureValue>
+ <ds:KeyInfo>
+ <wsse:SecurityTokenReference><wsse:Reference URI="#SecurityContextToken-32970611" ValueType="http://www.globus.org/ws/2004/09/security/sc#GSSAPI_CONTEXT_TOKEN"/></wsse:SecurityTokenReference>
+ </ds:KeyInfo>
+ </ds:Signature><wsc:SecurityContextToken wsu:Id="SecurityContextToken-32970611" xmlns:wsc="http://schemas.xmlsoap.org/ws/2004/04/sc" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsc:Identifier>3b1ef410-ab3d-11da-9436-88b687faed94</wsc:Identifier></wsc:SecurityContextToken></wsse:Security><wsa:MessageID wsu:Id="id-11434871" soapenv:mustUnderstand="0" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">uuid:3d592ca0-ab3d-11da-9436-88b687faed94</wsa:MessageID><wsa:To wsu:Id="id-19645447" soapenv:mustUnderstand="0" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:To><wsa:Action wsu:Id="id-5428820" soapenv:mustUnderstand="0" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">http://counter.com/CounterPortType/addResponse</wsa:Action><wsa:From ns3:Id="id-7680063" soapenv:mustUnderstand="0" xmlns:ns1="http://counter.com" xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsa:Address>http://bosshog.lbl.gov:9999/wsrf/services/SecureCounterService</wsa:Address><wsa:ReferenceProperties><ns1:CounterKey ns3:Id="10112">10577413</ns1:CounterKey></wsa:ReferenceProperties></wsa:From><wsa:RelatesTo RelationshipType="wsa:Reply" wsu:Id="id-28476580" soapenv:mustUnderstand="0" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">uuid:1141449047.05</wsa:RelatesTo></soapenv:Header><soapenv:Body wsu:Id="id-8409752" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><addResponse xmlns="http://counter.com">13</addResponse></soapenv:Body></soapenv:Envelope>"""
+
+
+XML_INST2 = """<?xml version="1.0" encoding="UTF-8"?>
+ <soapenv:Envelope
+xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Header>
+ <wsse:Security soapenv:mustUnderstand="1"
+xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><wsse:BinarySecurityToken
+EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
+ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"
+wsu:Id="CertId-1851922"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">MIIGbDCCA6owggKSoAMCAQICAh07MA0GCSqGSIb3DQEBBQUAMGkxEzARBgoJkiaJk/IsZAEZFgNv
+
+cmcxGDAWBgoJkiaJk/IsZAEZFghET0VHcmlkczEgMB4GA1UECxMXQ2VydGlmaWNhdGUgQXV0aG9y
+
+aXRpZXMxFjAUBgNVBAMTDURPRUdyaWRzIENBIDEwHhcNMDUxMjIxMjExNzUzWhcNMDYxMjIxMjEx
+
+NzUzWjBfMRMwEQYKCZImiZPyLGQBGRYDb3JnMRgwFgYKCZImiZPyLGQBGRYIZG9lZ3JpZHMxDzAN
+
+BgNVBAsTBlBlb3BsZTEdMBsGA1UEAxMUTWF0dCBSb2RyaWd1ZXogODkzMzAwggEiMA0GCSqGSIb3
+
+DQEBAQUAA4IBDwAwggEKAoIBAQCec6hEiQcu1lIa2pS/KxgmXbkfKLKrOm6AxPrfkht6Ja91+rdY
+
+TLQ4a21S792hglezFbylzLkDmCzYp43fH1xh0LlLea+YzUB7LoUnG29qv73CylSYqDnJWAU+sHhw
+
+fr3Hqpp6GxbxPqXJXcICs1lKbwinsgZQxMsml25O6ZF0x772b1kyiL4IsKwaS9/BQQCWCDA6vcMX
+
+4cKx67EYtDqopRfMUf9Ne3MAOpsfp17U/yeznDemjuxL5Q+zI1Qbq3Kx1kpFcLXKlSNz258EPF/u
+
+/9sOLME3EVp/9n+MjvgHJsTXvlMahF6Ci1UF+clZgMLjEhDHaLghiaagt7t8tqVnAgMBAAGjZjBk
+
+MBEGCWCGSAGG+EIBAQQEAwIF4DAOBgNVHQ8BAf8EBAMCBPAwHwYDVR0jBBgwFoAUyhkdEo5upDhd
+
+QtQxDgjb2Y0XDV0wHgYDVR0RBBcwFYETTUtSb2RyaWd1ZXpAbGJsLmdvdjANBgkqhkiG9w0BAQUF
+
+AAOCAQEAgRZkSHe4Gn9djOBlkn+5iGL5fiWb9LbZDeomS9OzfFePAP9G/8ihl+RLBZXgSdLXZm9v
+
+d6Ep+yVD4YHs0cZzaFlPnPxv6h6yWva+nEsTKkbm70yJrv1nsWP1k+nuBY6U6OQsa6um6Z1OCU6H
+
+u6uPSlyuedV93Vf77THU/1nv6Awf9pFhKolQVlmtQ4zfS9M4WNlNIAZYGgldaFjHVYQYee07Mb4S
+
+Y5EIGUQ6XiabX5C1xbynxniNTL5p4beW/dPZ6w7znHxHpJScoqELAVg2WbQhlcKQaKZPOO1fHy0/
+
+VM907Q1v541/FAhO1+5sTEYf1JPhsNYvNXMw+Z9ukb1PSzCCArowggGioAMCAQICBFSZyLUwDQYJ
+
+KoZIhvcNAQEEBQAwXzETMBEGCgmSJomT8ixkARkWA29yZzEYMBYGCgmSJomT8ixkARkWCGRvZWdy
+
+aWRzMQ8wDQYDVQQLEwZQZW9wbGUxHTAbBgNVBAMTFE1hdHQgUm9kcmlndWV6IDg5MzMwMB4XDTA2
+
+MDMyMjE4NTQwOFoXDTA2MDMyMzA2NTkwOFowdDETMBEGCgmSJomT8ixkARkWA29yZzEYMBYGCgmS
+
+JomT8ixkARkWCGRvZWdyaWRzMQ8wDQYDVQQLEwZQZW9wbGUxHTAbBgNVBAMTFE1hdHQgUm9kcmln
+
+dWV6IDg5MzMwMRMwEQYDVQQDEwoxNDE5MzY0NTMzMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMpt
+
+7hUlms1nmxRfeSlJQP7omyLujGCKkTTU0CAG2r40eKOqHNtCeFYCXT5/oCMrgB7YyEmxuUz57bJP
+
+sGPyHnsCAwEAAaMxMC8wDgYDVR0PAQH/BAQDAgSwMB0GCCsGAQUFBwEOAQH/BA4wDDAKBggrBgEF
+
+BQcVATANBgkqhkiG9w0BAQQFAAOCAQEALEPirNkcuhZB4/nouueISh/x+tD3GAgvAEERsVdJyWrF
+
+EceT9v0xN2FI00sk2U5yi5wCOhyCZfwN79/dVo0CGB8OqpG5rJ4GnhJ/eea8h98ZVqR0oRWb7IcG
+
+FhqU1ja930dCZGpoaBKjy39HHgzQTFuvwXjaWyoV6C7sAE1Aw3PSafMGaHxjJoK386KpolVxZbrq
+
+DpeKZoxPZKBC7+hyv4vO7KG6s9G/tmIkTroMKEtHHz7NhZHkv+h1aO8g8p57j9uZ8EvdUWUcnwiS
+
+EWXM9AMmho4Z5rex2cdE/s3d+Wa7IFhYoo61VW6v4amSHQH/o4Vdt0pN4hh+/9y32lp89g==</wsse:BinarySecurityToken><ds:Signature
+xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:SignedInfo>
+ <ds:CanonicalizationMethod
+Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ <ds:SignatureMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+ <ds:Reference URI="#id-25484440">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>xqoPUGjk97yY+StAheOFmeaHgbw=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-28219008">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>VJvTr+Mx3TeWsQY6iwGbhAJ9/eA=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-18539969">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>W1PrEK32GMCbF6FTEmlYiYwqAeQ=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-14816181">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>lWhqYlKqBnB4LwkRWyXMwHy18hc=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-8120088">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>2Zjgz4McHaxMLfpBbqelAqWvRsU=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-8450175">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>knsi7QmfOvjrn5mWClmsbCpZ32A=</ds:DigestValue>
+ </ds:Reference>
+ <ds:Reference URI="#id-19744521">
+ <ds:Transforms>
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+ </ds:Transforms>
+ <ds:DigestMethod
+Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <ds:DigestValue>/yBXQ0yxPqpgSGYym/DA08k0dXM=</ds:DigestValue>
+ </ds:Reference>
+ </ds:SignedInfo>
+ <ds:SignatureValue>
+
+LuGNfoVBzUIoF0AU0lzJkH9kAOi+PQVG8hMrCIEjWh1lifSG/bquhu/qZVq78x3UR+tGK411hWuQ
+ nGle1GvY0A==
+ </ds:SignatureValue>
+ <ds:KeyInfo Id="KeyId-17834932">
+ <wsse:SecurityTokenReference wsu:Id="STRId-9973812"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsse:Reference
+URI="#CertId-1851922"
+ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"/></wsse:SecurityTokenReference>
+ </ds:KeyInfo>
+ </ds:Signature>
+ <wsu:Timestamp wsu:Id="id-25484440"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsu:Created>2006-03-23T00:11:14Z</wsu:Created><wsu:Expires>2006-03-23T00:16:14Z</wsu:Expires></wsu:Timestamp></wsse:Security><wsa:MessageID
+wsu:Id="id-8120088" soapenv:mustUnderstand="0"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">uuid:8aec8160-ba01-11da-bc99-cbed73daa755</wsa:MessageID><wsa:To
+wsu:Id="id-18539969" soapenv:mustUnderstand="0"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:To><wsa:Action
+wsu:Id="id-19744521" soapenv:mustUnderstand="0"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">http://www.globus.org/08/2004/delegationService/DelegationFactoryPortType/RequestSecurityTokenResponse</wsa:Action><wsa:From
+wsu:Id="id-14816181" soapenv:mustUnderstand="0"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsa:Address>http://bosshog.lbl.gov:8888/wsrf/services/DelegationFactoryService</wsa:Address></wsa:From><wsa:RelatesTo
+RelationshipType="wsa:Reply" wsu:Id="id-8450175"
+soapenv:mustUnderstand="0"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">uuid:1143072675.25</wsa:RelatesTo></soapenv:Header><soapenv:Body
+wsu:Id="id-28219008"
+xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><RequestSecurityTokenResponse
+xmlns="http://schemas.xmlsoap.org/ws/2004/04/trust"><wsa:EndpointReference
+xmlns:ns1="http://www.globus.org/08/2004/delegationService"><wsa:Address>http://131.243.2.147:8888/wsrf/services/DelegationService</wsa:Address><wsa:ReferenceProperties><ns1:DelegationKey>8adaa710-ba01-11da-bc99-cbed73daa755</ns1:DelegationKey></wsa:ReferenceProperties></wsa:EndpointReference></RequestSecurityTokenResponse></soapenv:Body></soapenv:Envelope>"""
+
+
+
+CORRECT = """<ns1:MessageID xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10102">uuid:1143760705.98</ns1:MessageID>"""
+
+WRONG = """<ns1:MessageID xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" ns2:Id="10102">uuid:1143760705.98</ns1:MessageID>"""
+
+XML_INST4 = """<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/"><SOAP-ENV:Header xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/03/addressing"><ns1:MessageID xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10102">uuid:1143760705.98</ns1:MessageID><ns1:Action xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10103">http://counter.com/CounterPortType/createCounterRequest</ns1:Action><ns1:To xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10104">http://131.243.2.159:9080/wsrf/services/SecureCounterService</ns1:To><ns1:From xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10105"><ns1:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</ns1:Address></ns1:From><ns2:Security xmlns:ns3="http://www.w3.org/2000/09/xmldsig#" xmlns:ns4="http://schemas.xmlsoap.org/ws/2004/04/sc"><ns3:Signature><ns3:SignedInfo xsi:type="ns3:SignedInfoType"><ns3:CanonicalizationMethod xsi:type="ns3:CanonicalizationMethodType" Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ns3:CanonicalizationMethod><ns3:SignatureMethod xsi:type="ns3:SignatureMethodType" Algorithm="http://www.globus.org/2002/04/xmlenc#gssapi-sign"></ns3:SignatureMethod><ns3:Reference xsi:type="ns3:ReferenceType" URI="#10102"><ns3:DigestMethod xsi:type="ns3:DigestMethodType" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns3:DigestMethod><ns3:DigestValue xsi:type="ns3:DigestValueType">
+ IkMyI4zCDlK41qE7sZxvkFHJioU=
+ </ns3:DigestValue></ns3:Reference><ns3:Reference xsi:type="ns3:ReferenceType" URI="#10103"><ns3:DigestMethod xsi:type="ns3:DigestMethodType" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns3:DigestMethod><ns3:DigestValue xsi:type="ns3:DigestValueType">
+ DyEF6Pa7w3SSEVJ98LIoX2LW85k=
+ </ns3:DigestValue></ns3:Reference><ns3:Reference xsi:type="ns3:ReferenceType" URI="#10104"><ns3:DigestMethod xsi:type="ns3:DigestMethodType" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns3:DigestMethod><ns3:DigestValue xsi:type="ns3:DigestValueType">
+ p/2PhmYP+/1UPcpwsRcdlvLmOAg=
+ </ns3:DigestValue></ns3:Reference><ns3:Reference xsi:type="ns3:ReferenceType" URI="#10105"><ns3:DigestMethod xsi:type="ns3:DigestMethodType" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns3:DigestMethod><ns3:DigestValue xsi:type="ns3:DigestValueType">
+ KFLeYjf5ohGUIoPoZV/oew9SuUM=
+ </ns3:DigestValue></ns3:Reference><ns3:Reference xsi:type="ns3:ReferenceType" URI="#10106"><ns3:DigestMethod xsi:type="ns3:DigestMethodType" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns3:DigestMethod><ns3:DigestValue xsi:type="ns3:DigestValueType">
+ 7Gg0SC1wltHVAwiOfdgZsGM9W5g=
+ </ns3:DigestValue></ns3:Reference></ns3:SignedInfo><ns3:SignatureValue xsi:type="ns3:SignatureValueType">
+ AAAAAAAAAAEAAAdrBxzrHLZG4NglRglL9F3rKQu0658=
+ </ns3:SignatureValue><ns3:KeyInfo xsi:type="ns3:KeyInfoType"><ns2:SecurityTokenReference><ns2:Reference URI="#CertId-10107" ValueType="http://www.globus.org/ws/2004/09/security/sc#GSSAPI_CONTEXT_TOKEN"></ns2:Reference></ns2:SecurityTokenReference></ns3:KeyInfo></ns3:Signature><ns4:SecurityContextToken xmlns:ns5="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns5:Id="CertId-10107"><ns4:Identifier xsi:type="xsd:anyURI">1000</ns4:Identifier></ns4:SecurityContextToken></ns2:Security></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="http://counter.com"><ns1:createCounter xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns2:Id="10106"></ns1:createCounter></SOAP-ENV:Body></SOAP-ENV:Envelope>"""
+
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+
+if __name__ == '__main__':
+ main()
+
diff --git a/test/test_zsi.py b/test/test_zsi.py
new file mode 100644
index 0000000..c15725c
--- /dev/null
+++ b/test/test_zsi.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+import unittest
+import test_t1
+import test_t2
+import test_t3
+import test_t5
+import test_t6
+import test_t7
+import test_t8
+import test_t9
+
+def makeTestSuite():
+ suite1 = test_t1.makeTestSuite()
+ suite2 = test_t2.makeTestSuite()
+ suite3 = test_t3.makeTestSuite()
+ suite5 = test_t5.makeTestSuite()
+ suite6 = test_t6.makeTestSuite()
+ suite7 = test_t7.makeTestSuite()
+ suite8 = test_t8.makeTestSuite()
+ suite9 = test_t9.makeTestSuite()
+ t = (suite1, suite2, suite3, suite5, suite6, suite7, suite8, suite9)
+ suite = unittest.TestSuite(t)
+ return suite
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+ suite = unittest.TestSuite()
+
+if __name__ == "__main__" : main()
diff --git a/test/test_zsi_net.py b/test/test_zsi_net.py
new file mode 100644
index 0000000..65aed78
--- /dev/null
+++ b/test/test_zsi_net.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+import unittest
+import test_t1
+import test_t2
+import test_t3
+import test_t4
+import test_t5
+import test_t6
+import test_t7
+import test_t8
+import test_t9
+
+def makeTestSuite():
+ suite1 = test_t1.makeTestSuite()
+ suite2 = test_t2.makeTestSuite()
+ suite3 = test_t3.makeTestSuite()
+ suite4 = test_t4.makeTestSuite()
+ suite5 = test_t5.makeTestSuite()
+ suite6 = test_t6.makeTestSuite()
+ suite7 = test_t7.makeTestSuite()
+ suite8 = test_t8.makeTestSuite()
+ suite9 = test_t9.makeTestSuite()
+ t = (suite1, suite2, suite3, suite4, suite5, suite6, suite7, suite8, suite9)
+ suite = unittest.TestSuite(t)
+ return suite
+def main():
+ unittest.main(defaultTest="makeTestSuite")
+ suite = unittest.TestSuite()
+
+if __name__ == "__main__" : main()
diff --git a/test/tests_bad.py b/test/tests_bad.py
new file mode 100644
index 0000000..4c50c80
--- /dev/null
+++ b/test/tests_bad.py
@@ -0,0 +1,126 @@
+test01 = '''<SOAP-ENV:Envelope foo='bar'
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body>
+ <m:GetLastTradePrice xmlns:m="Some-URI">
+ <symbol>DIS</symbol>
+ </m:GetLastTradePrice>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test02 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+<SOAP-ENV:Header>
+ <t:Transaction xmlns:t="some-URI" SOAP-ENV:mustUnderstand="1">
+ 5
+ </t:Transaction>
+</SOAP-ENV:Header>
+<SOAP-ENV:Body/>
+</SOAP-ENV:Envelope>'''
+
+test03 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+ <SOAP-ENV:Body>
+ <SOAP-ENV:Fault>
+ <faultcode>SOAP-ENV:MustUnderstand</faultcode>
+ <faultstring>SOAP Must Understand Error</faultstring>
+ <?MYPI spenser?>
+ </SOAP-ENV:Fault>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test04 = '''<SOAP-ENV:Envelope fooattr='bar'
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+ <SOAP-ENV:Body>
+ <SOAP-ENV:Fault>
+ <faultcode>SOAP-ENV:Server</faultcode>
+ <faultstring>Server Error</faultstring>
+ <detail>
+ <e:myfaultdetails xmlns:e="Some-URI">
+ <message>
+ My application didn't work
+ </message>
+ <errorcode>
+ 1001
+ </errorcode>
+ </e:myfaultdetails>
+ </detail>
+ </SOAP-ENV:Fault>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test05 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test06 = '''<SOAP-ENV:ChemicalX
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+</SOAP-ENV:ChemicalX>'''
+
+test07 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+</SOAP-ENV:Envelope>'''
+
+test08 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:zBody></SOAP-ENV:zBody>
+</SOAP-ENV:Envelope>'''
+
+test09 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test10 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+</SOAP-ENV:Envelope>'''
+
+test11 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <m:data xmlns:m="data-URI">
+ <symbol>DEF</symbol>
+ </m:data>
+</SOAP-ENV:Envelope>'''
+
+test12 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <m:data xmlns:m="data-URI">
+ <?PIE?>
+ <symbol>DEF</symbol>
+ </m:data>
+</SOAP-ENV:Envelope>'''
+
+test13 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <?xoo?>
+ <SOAP-ENV:Header></SOAP-ENV:Header>
+ <SOAP-ENV:Body></SOAP-ENV:Body>
+ <m:data xmlns:m="data-URI">
+ <symbol>DEF</symbol>
+ </m:data>
+</SOAP-ENV:Envelope>'''
+
diff --git a/test/tests_good.py b/test/tests_good.py
new file mode 100644
index 0000000..eca6112
--- /dev/null
+++ b/test/tests_good.py
@@ -0,0 +1,88 @@
+test01 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <!-- foo foo-pi -->
+ <SOAP-ENV:Body>
+ <m:GetLastTradePriceResponse xmlns:m="Some-URI">
+ <Price>34.5</Price>
+ </m:GetLastTradePriceResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test02 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Header>
+ <t:Transaction
+ actor="foobar"
+ xmlns:t="some-URI"
+ SOAP-ENV:mustUnderstand="1">
+ 5
+ </t:Transaction>
+ </SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <m:GetLastTradePrice xmlns:m="Some-URI">
+ <symbol>DEF</symbol>
+ </m:GetLastTradePrice>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test03 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body>
+ <m:GetLastTradePriceDetailed
+ xmlns:m="Some-URI">
+ <Symbol>DEF</Symbol>
+ <Company>DEF Corp</Company>
+ <Price>34.1</Price>
+ </m:GetLastTradePriceDetailed>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test04 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:xsi='xmlschemainstance'>
+ <SOAP-ENV:Header>
+ <t:Transaction xmlns:t="some-URI" xsi:type="xsd:int" mustUnderstand="1">
+ 5
+ <nested mustUndertand="1"/>
+ </t:Transaction>
+ </SOAP-ENV:Header>
+ <SOAP-ENV:Body>
+ <m:GetLastTradePriceResponse xmlns:m="Some-URI">
+ <m:Price>34.5</m:Price>
+ </m:GetLastTradePriceResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test05 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body>
+ <m:GetLastTradePriceResponse
+ xmlns:m="Some-URI">
+ <PriceAndVolume>
+ <LastTradePrice>
+ 34.5
+ </LastTradePrice>
+ <DayVolume>
+ 10000
+ </DayVolume>
+ </PriceAndVolume>
+ </m:GetLastTradePriceResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
+test06 = '''<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <SOAP-ENV:Body>
+ <foo/>
+ <m:GetLastTradePriceResponse xmlns:m="Some-URI">
+ <Price>34.5</Price>
+ </m:GetLastTradePriceResponse>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>'''
+
diff --git a/test/wsdl2py/.cvsignore b/test/wsdl2py/.cvsignore
new file mode 100644
index 0000000..873196c
--- /dev/null
+++ b/test/wsdl2py/.cvsignore
@@ -0,0 +1 @@
+stubs
diff --git a/test/wsdl2py/README b/test/wsdl2py/README
new file mode 100644
index 0000000..4b05678
--- /dev/null
+++ b/test/wsdl2py/README
@@ -0,0 +1,118 @@
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+Note that since all services are not always available, connection
+timeouts and HTTP errors are not treated as test failures.
+
+Client and Server stubs will be generated for each test, but
+the Server stubs will are runtime tested only if "dispatch" is
+specified and an executable is provided via the "servers"
+section.
+
+
+To run all "working" tests:
+ %./runTests.py
+
+To run only "local" tests:
+ %./runTests.py local
+
+To run only "network" tests:
+ %./runTests.py net
+
+To run only tests against the wsdl2dispatch tool:
+ %./runTests.py dispatch
+
+
+Convention Modules: All test modules should define these targets:
+
+ "all"
+ -- run all tests
+
+ "dispatch"
+ -- run only "test_dispatch*" tests
+
+ "local"
+ -- run only "test_local*" tests
+
+ "net"
+ -- run only "test_net*" tests
+
+
+Convention TestCase: All TestCases should adhere to these conventions.
+
+ methods:
+
+ "test_net*"
+ -- only tests run against a network available endpoint, which
+ is specified in the WSDL location address information item.
+
+ "test_local*"
+ -- only local tests, no networking.
+
+ "test_dispatch*"
+ -- only tests run against a local endpoint, this is
+ configured and forked by the unittest framework. Will
+ override the WSDL location address information item with
+ information from the dispatch configuration section.
+
+ class attributes:
+
+ client_file_name
+ -- if specified, "client_module" attribute will reference module.
+
+ types_file_name
+ -- if specified, "types_module" attribute will reference module.
+
+ server_file_name
+ -- if specified, "server_module" attribute will reference module.
+
+
+Directories:
+
+ "stubs"
+ -- Generated code, regenerated everytime a test is run.
+
+ "servers"
+ -- Server code, this is an implemenation of service and must
+ be an executable. It is also hand coded. Used for
+ "dispatch" tests.
+
+
+Configuration:
+
+ Edit "config.txt", short descriptions are provided inline.
+
+ [configuration]
+ The "configuation" section for global configuration.
+
+ "tracefile" -- show XML of individual messages as serialized/parsed.
+ "debug" -- turn on verbose debugging information.
+ "skip" -- skip generation (wsdl2py and wsdl2dispatch)
+ "twisted" -- use twisted.web.client
+
+ [dispatch]
+ The "configuration" section for dispatch tests.
+
+ "host" -- for local tests the hostname where server will be ran.
+ "port" -- for local tests the port where server will be ran.
+ "path" -- path where service is located
+ "other items" -- executable "ServiceContainer" which is configured
+ with the required service instance.
+
+
+ [rpc_encoded],[rpc_encoded_broke],[doc_encoded],[doc_encoded_broke]
+ [rpc_literal],[rpc_literal_broke],[doc_literal],[doc_literal_broke]
+
+ Sections to categorize all tests...
+ All these sections contain a "test" option, this is a whitespace
+ separated list of all the tests that belong in a particular section.
+
+ [WSDL]
+ URL to WSDL for particular service.
+
+
+
+
+
diff --git a/test/wsdl2py/ServiceTest.py b/test/wsdl2py/ServiceTest.py
new file mode 100644
index 0000000..6ac6c13
--- /dev/null
+++ b/test/wsdl2py/ServiceTest.py
@@ -0,0 +1,418 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+from compiler.ast import Module
+import StringIO, copy, getopt
+import os, sys, unittest, urlparse, signal, time, warnings, subprocess
+from ConfigParser import ConfigParser, NoSectionError, NoOptionError
+from ZSI.wstools.TimeoutSocket import TimeoutError
+
+"""Global Variables:
+ CONFIG_FILE -- configuration file
+ CONFIG_PARSER -- ConfigParser instance
+ DOCUMENT -- test section variable, specifying document style.
+ LITERAL -- test section variable, specifying literal encodings.
+ BROKE -- test section variable, specifying broken test.
+ TESTS -- test section variable, whitespace separated list of modules.
+ SECTION_CONFIGURATION -- configuration section, turn on/off debuggging.
+ TRACEFILE -- file class instance.
+ TOPDIR -- current working directory
+ MODULEDIR -- stubs directory
+ PORT -- port of local container
+ HOST -- address of local container
+ SECTION_SERVERS -- services to be tested, values are paths to executables.
+"""
+CONFIG_FILE = 'config.txt'
+CONFIG_PARSER = ConfigParser()
+DOCUMENT = 'document'
+LITERAL = 'literal'
+BROKE = 'broke'
+TESTS = 'tests'
+SECTION_CONFIGURATION = 'configuration'
+SECTION_DISPATCH = 'dispatch'
+TRACEFILE = sys.stdout
+TOPDIR = os.getcwd()
+MODULEDIR = TOPDIR + '/stubs'
+SECTION_SERVERS = 'servers'
+
+CONFIG_PARSER.read(CONFIG_FILE)
+
+DEBUG = CONFIG_PARSER.getboolean(SECTION_CONFIGURATION, 'debug')
+SKIP = CONFIG_PARSER.getboolean(SECTION_CONFIGURATION, 'skip')
+TWISTED = CONFIG_PARSER.getboolean(SECTION_CONFIGURATION, 'twisted')
+LAZY = CONFIG_PARSER.getboolean(SECTION_CONFIGURATION, 'lazy')
+OUTPUT = CONFIG_PARSER.get(SECTION_CONFIGURATION, 'output') or sys.stdout
+
+if DEBUG:
+ from ZSI.wstools.logging import setBasicLoggerDEBUG
+ setBasicLoggerDEBUG()
+
+sys.path.append('%s/%s' %(os.getcwd(), 'stubs'))
+ENVIRON = copy.copy(os.environ)
+ENVIRON['PYTHONPATH'] = ENVIRON.get('PYTHONPATH', '') + ':' + MODULEDIR
+
+
+def _SimpleMain():
+ """Gets tests to run from configuration file.
+ """
+ unittest.TestProgram(defaultTest="all")
+main = _SimpleMain
+
+
+def _TwistedMain():
+ """Gets tests to run from configuration file.
+ """
+ from twisted.internet import reactor
+ reactor.callWhenRunning(_TwistedTestProgram, defaultTest="all")
+ reactor.run(installSignalHandlers=0)
+if TWISTED: main = _TwistedMain
+
+
+def _LaunchContainer(cmd):
+ '''
+ Parameters:
+ cmd -- executable, sets up a ServiceContainer or ?
+ '''
+ host = CONFIG_PARSER.get(SECTION_DISPATCH, 'host')
+ port = CONFIG_PARSER.get(SECTION_DISPATCH, 'port')
+ process = subprocess.Popen([cmd, port], env=ENVIRON)
+ time.sleep(1)
+ return process
+
+
+class _TwistedTestProgram(unittest.TestProgram):
+
+ def runTests(self):
+ from twisted.internet import reactor
+ if self.testRunner is None:
+ self.testRunner = unittest.TextTestRunner(verbosity=self.verbosity)
+
+ result = self.testRunner.run(self.test)
+ reactor.stop()
+ return result.wasSuccessful()
+
+
+
+class ConfigException(Exception):
+ """Exception thrown when configuration settings arent correct.
+ """
+ pass
+
+class TestException(Exception):
+ """Exception thrown when test case isn't correctly set up.
+ """
+ pass
+
+
+class ServiceTestCase(unittest.TestCase):
+ """Conventions for method names:
+ test_net*
+ -- network tests
+
+ test_local*
+ -- local tests
+
+ test_dispatch*
+ -- tests that use the a spawned local container
+
+ class attributes: Edit/Override these in the inheriting class as needed
+ out -- file descriptor to write output to
+ name -- configuration item, must be set in class.
+ url_section -- configuration section, maps a test module
+ name to an URL.
+ client_file_name --
+ types_file_name --
+ server_file_name --
+ """
+ out = OUTPUT
+ name = None
+ url_section = 'WSDL'
+ client_file_name = None
+ types_file_name = None
+ server_file_name = None
+
+ def __init__(self, methodName):
+ """
+ parameters:
+ methodName --
+ instance variables:
+ client_module
+ types_module
+ server_module
+ processID
+ done
+
+ """
+ self.methodName = methodName
+ self.url = None
+ self.wsdl2py_args = []
+ self.wsdl2dispatch_args = []
+ self.portkwargs = {}
+ self.client_module = self.types_module = self.server_module = None
+ self.done = False
+
+ if TWISTED:
+ self.wsdl2py_args.append('--twisted')
+
+ if LAZY:
+ self.wsdl2py_args.append('--lazy')
+
+ unittest.TestCase.__init__(self, methodName)
+
+ write = lambda self, arg: self.out.write(arg)
+
+ if sys.version_info[:2] >= (2,5):
+ _exc_info = unittest.TestCase._exc_info
+ else:
+ _exc_info = unittest.TestCase._TestCase__exc_info
+
+ def __call__(self, *args, **kwds):
+ self.run(*args, **kwds)
+
+ def run(self, result=None):
+ if result is None: result = self.defaultTestResult()
+ result.startTest(self)
+ testMethod = getattr(self, self.methodName)
+ try:
+ try:
+ self.setUp()
+ except KeyboardInterrupt:
+ raise
+ except:
+ result.addError(self, self._exc_info())
+ return
+
+ ok = False
+ try:
+ t1 = time.time()
+ pyobj = testMethod()
+ t2 = time.time()
+ ok = True
+ except self.failureException:
+ result.addFailure(self, self._exc_info())
+ except KeyboardInterrupt:
+ raise
+ except:
+ result.addError(self, self._exc_info())
+
+ try:
+ self.tearDown()
+ except KeyboardInterrupt:
+ raise
+ except:
+ result.addError(self, self._exc_info())
+ ok = False
+ if ok:
+ result.addSuccess(self)
+ print>>self
+ print>>self, "|"+"-"*60
+ print>>self, "| TestCase: %s" %self.methodName
+ print>>self, "|"+"-"*20
+ print>>self, "| run time: %s ms" %((t2-t1)*1000)
+ print>>self, "| return : %s" %pyobj
+ print>>self, "|"+"-"*60
+
+ finally:
+ result.stopTest(self)
+
+
+
+
+ def getPortKWArgs(self):
+ kw = {}
+ if CONFIG_PARSER.getboolean(SECTION_CONFIGURATION, 'tracefile'):
+ kw['tracefile'] = TRACEFILE
+
+ kw.update(self.portkwargs)
+ return kw
+
+ def _setUpDispatch(self):
+ """Set this test up as a dispatch test.
+ url --
+ """
+ host = CONFIG_PARSER.get(SECTION_DISPATCH, 'host')
+ port = CONFIG_PARSER.get(SECTION_DISPATCH, 'port')
+ path = CONFIG_PARSER.get(SECTION_DISPATCH, 'path')
+
+ scheme = 'http'
+ netloc = '%s:%s' %(host, port)
+ params = query = fragment = None
+
+ self.portkwargs['url'] = \
+ urlparse.urlunparse((scheme,netloc,path,params,query,fragment))
+
+ _wsdl = {}
+ def _generate(self):
+ """call the wsdl2py and wsdl2dispatch scripts and
+ automatically add the "-f" or "-u" argument. Other args
+ can be appended via the "wsdl2py_args" and "wsdl2dispatch_args"
+ instance attributes.
+ """
+ url = self.url
+ if SKIP:
+ ServiceTestCase._wsdl[url] = True
+ return
+
+ args = []
+ ServiceTestCase._wsdl[url] = False
+ if os.path.isfile(url):
+ args += ['-f', os.path.abspath(url)]
+ else:
+ args += ['-u', url]
+
+ try:
+ os.mkdir(MODULEDIR)
+ except OSError, ex:
+ pass
+
+ os.chdir(MODULEDIR)
+ if MODULEDIR not in sys.path:
+ sys.path.append(MODULEDIR)
+
+ try:
+ # Client Stubs
+ wsdl2py = ['wsdl2py'] + args + self.wsdl2py_args
+ try:
+ exit = subprocess.call(wsdl2py)
+ except OSError, ex:
+ warnings.warn("TODO: Not sure what is going on here?")
+ exit = -1
+
+ #TODO: returncode WINDOWS?
+ self.failUnless(os.WIFEXITED(exit),
+ '"%s" exited with signal#: %d' %(wsdl2py, exit))
+ self.failUnless(exit == 0,
+ '"%s" exited with exit status: %d' %(wsdl2py, exit))
+
+ # Service Stubs
+ if '-x' not in self.wsdl2py_args:
+ wsdl2dispatch = (['wsdl2dispatch'] + args +
+ self.wsdl2dispatch_args)
+ try:
+ exit = subprocess.call(wsdl2dispatch)
+ except OSError, ex:
+ warnings.warn("TODO: Not sure what is going on here?")
+
+ #TODO: returncode WINDOWS?
+ self.failUnless(os.WIFEXITED(exit),
+ '"%s" exited with signal#: %d' %(wsdl2dispatch, exit))
+ self.failUnless(exit == 0,
+ '"%s" exited with exit status: %d' %(wsdl2dispatch, exit))
+
+ ServiceTestCase._wsdl[url] = True
+
+ finally:
+ os.chdir(TOPDIR)
+
+ _process = None
+ _lastToDispatch = None
+ def setUp(self):
+ """Generate types and services modules once, then make them
+ available thru the *_module attributes if the *_file_name
+ attributes were specified.
+ """
+ section = self.url_section
+ name = self.name
+ if not section or not name:
+ raise TestException, 'section(%s) or name(%s) not defined' %(
+ section, name)
+
+ if not CONFIG_PARSER.has_section(section):
+ raise TestException,\
+ 'No such section(%s) in configuration file(%s)' %(
+ self.url_section, CONFIG_FILE)
+
+ self.url = CONFIG_PARSER.get(section, name)
+
+ status = ServiceTestCase._wsdl.get(self.url)
+ if status is False:
+ self.fail('generation failed for "%s"' %self.url)
+
+ if status is None:
+ self._generate()
+
+ # Check for files
+ tfn = self.types_file_name
+ cfn = self.client_file_name
+ sfn = self.server_file_name
+
+ files = filter(lambda f: f is not None, [cfn, tfn,sfn])
+ if None is cfn is tfn is sfn:
+ return
+
+ for n,m in map(lambda i: (i,__import__(i.split('.py')[0])), files):
+ if tfn is not None and tfn == n:
+ self.types_module = m
+ elif cfn is not None and cfn == n:
+ self.client_module = m
+ elif sfn is not None and sfn == n:
+ self.server_module = m
+ else:
+ self.fail('Unexpected module %s' %n)
+
+ # DISPATCH PORTION OF SETUP
+ if not self.methodName.startswith('test_dispatch'):
+ return
+
+ self._setUpDispatch()
+ if ServiceTestCase._process is not None:
+ return
+
+ try:
+ expath = CONFIG_PARSER.get(SECTION_DISPATCH, name)
+ except (NoSectionError, NoOptionError), ex:
+ self.fail('section dispatch has no item "%s"' %name)
+
+ if ServiceTestCase._lastToDispatch == expath:
+ return
+
+ if ServiceTestCase._lastToDispatch is not None:
+ ServiceTestCase.CleanUp()
+
+ ServiceTestCase._lastToDispatch = expath
+ ServiceTestCase._process = _LaunchContainer(TOPDIR + '/' + expath)
+
+ def CleanUp(cls):
+ """call this when dispatch server is no longer needed,
+ maybe another needs to be started. Assumption that
+ a single "Suite" uses the same server, once all the
+ tests are run in that suite do a cleanup.
+ """
+ if cls._process is None:
+ return
+ os.kill(cls._process.pid, signal.SIGKILL)
+ cls._process = None
+ CleanUp = classmethod(CleanUp)
+
+
+class ServiceTestSuite(unittest.TestSuite):
+ """A test suite is a composite test consisting of a number of TestCases.
+
+ For use, create an instance of TestSuite, then add test case instances.
+ When all tests have been added, the suite can be passed to a test
+ runner, such as TextTestRunner. It will run the individual test cases
+ in the order in which they were added, aggregating the results. When
+ subclassing, do not forget to call the base class constructor.
+ """
+ def __init__(self, tests=()):
+ unittest.TestSuite.__init__(self, tests)
+
+ def __call__(self, result):
+ # for python2.4
+ return self.run(result)
+
+ def addTest(self, test):
+ unittest.TestSuite.addTest(self, test)
+
+ def run(self, result):
+ for test in self._tests:
+ if result.shouldStop:
+ break
+ test(result)
+
+ ServiceTestCase.CleanUp()
+ return result
+
+
diff --git a/test/wsdl2py/config.txt b/test/wsdl2py/config.txt
new file mode 100644
index 0000000..2ca2c3e
--- /dev/null
+++ b/test/wsdl2py/config.txt
@@ -0,0 +1,157 @@
+############################################################################
+# Joshua R. Bovernof, LBNL
+# See Copyright for copyright notice!
+#
+# CONFIGURATION SECTIONS
+# [configuration] - global configuration of service tests.
+# tracefile -- trace soap messages
+# debug -- turn on debugging
+# skip -- skip generation steps, code generated don't generate again
+# twisted -- Use twisted web client
+# lazy -- use lazy typecode evaluation
+# output -- file name where output will be stored, if unspecified test output goes to stdout
+#
+# [dispatch] -- configuration for test_dispatch* tests
+# host -- address of local container
+# port -- port of local container
+# path -- path of service
+# twisted -- Use twisted web resource
+#
+##########################################################################
+[configuration]
+tracefile = False
+debug = False
+skip = False
+twisted = False
+lazy = False
+output =
+
+[dispatch]
+host = localhost
+port = 9080
+path = test
+twisted = False
+test_WhiteMesa = servers/WhiteMesa.py
+test_Echo = servers/EchoServer.py
+
+
+##########################################################################
+# URL SECTIONS: All SOAP-1.1
+# document -- document style True/False
+# literal -- literal encoding True/False
+# broke -- broke True/False
+# tests -- whitespace separated list of test modules, referenced in
+# section WSDL.
+#
+# [rpc_encoded] -- these are rpc/encoded services that WORK.
+# [rpc_encoded_broke] -- these are rpc/encoded services that DONT WORK.
+#
+# [doc_encoded] -- these are document/encoded services that WORK.
+# [doc_encoded_broke] -- these are document/encoded services that DONT WORK.
+#
+# [rpc_literal] -- these are rpc/literal services that WORK.
+# [rpc_literal_broke] -- these are rpc/literal services that DONT WORK.
+# [rpc_literal_broke_interop] -- these are broke BP-1.0 compliant rpc/literal services.
+#
+# [doc_literal] -- these are doc/literal services that WORK.
+# [doc_literal_broke] -- these are doc/literal services that DONT WORK.
+# [doc_literal_broke_interop] -- these are broke BP-1.0 compliant doc/literal services.
+#
+##########################################################################
+
+
+########################## RPC/ENCODED ###################################
+#
+# Broken:
+#
+#
+##########################################################################
+[rpc_encoded]
+document = False
+literal = False
+broke = False
+tests = test_XMethodsQuery test_ThreatService
+
+[rpc_encoded_broke]
+document = False
+literal = False
+broke = True
+tests =
+
+
+########################## RPC/LITERAL ###################################
+#
+# Broken:
+#
+#
+##########################################################################
+[rpc_literal]
+document = False
+literal = True
+broke = False
+tests = test_WhiteMesa
+
+[rpc_literal_broke]
+document = False
+literal = True
+broke = True
+tests =
+
+[rpc_literal_broke_interop]
+document = False
+literal = True
+broke = True
+tests =
+
+########################## DOCUMENT/LITERAL ##############################
+#
+# Broken:
+# test_TerraService -- wsdl2python Not supporting content <ZSI.wstools.XMLSchema.List>
+#
+#
+##########################################################################
+[doc_literal]
+document = True
+literal = True
+broke = False
+tests = test_MapPoint test_OpcDaGateway test_Sabre test_Echo test_AWSECommerceService
+
+[doc_literal_broke]
+document = True
+literal = True
+broke = True
+tests =
+
+[doc_literal_broke_interop]
+document = True
+literal = True
+broke = True
+tests =
+
+
+##########################################################################
+# SECTION [WSDL] - test name(equivalent to module name) and URL.
+#
+##########################################################################
+[WSDL]
+test_AWSECommerceService = http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl
+test_AmazonS3 = http://s3.amazonaws.com/doc/2006-03-01/AmazonS3.wsdl
+test_TemperatureService = http://www.xmethods.net/sd/2001/TemperatureService.wsdl
+test_WorldTimeService = http://ws.digiposs.com/WorldTime.jws?wsdl
+test_BFGraphLib = http://bfo.co.uk/products/graph/docs/bfograph-2.1.wsdl
+
+test_XMethodsQuery = http://www.xmethods.net/wsdl/query.wsdl
+test_ZipCodeResolver = http://webservices.eraserver.net/zipcoderesolver/zipcoderesolver.asmx?WSDL
+test_WhiteMesa = http://www.whitemesa.net/wsdl/test-rpc-lit.wsdl
+test_OpcDaGateway = http://tswinc.us/XMLDADemo/ts_sim/OpcDaGateway.asmx?WSDL
+test_MapPoint = http://staging.mappoint.net/standard-30/mappoint.wsdl
+test_Sabre = http://webservices.sabre.com/wsdl/sabreXML1.0.00/res/SessionCreateRQ.wsdl
+
+test_Echo = ../../samples/Echo/Echo.wsdl
+test_Choice = test_Choice.xsd
+test_Attributes = test_Attributes.xsd
+test_DerivedTypes = test_DerivedTypes.xsd
+
+test_ThreatService = http://www.boyzoid.com/threat.cfc?wsdl
+test_TerraService = http://terraservice.net/TerraService.asmx?WSDL
+test_InfoBil = http://javatest2.infodata.se/webservices/services/Infobil?wsdl
diff --git a/test/wsdl2py/runTests.py b/test/wsdl2py/runTests.py
new file mode 100755
index 0000000..6cb68af
--- /dev/null
+++ b/test/wsdl2py/runTests.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See Copyright for copyright notice!
+###########################################################################
+import unittest, warnings
+from ServiceTest import main, CONFIG_PARSER, DOCUMENT, LITERAL, BROKE, TESTS
+
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ return _dispatchTestSuite(broke=False)
+
+def local():
+ """Run all local tests"""
+ return _localTestSuite(broke=False)
+
+def net():
+ """Run all network tests"""
+ return _netTestSuite(broke=False)
+
+def all():
+ """Run all tests"""
+ return _allTestSuite(broke=False)
+
+
+# Specialized binding targets
+def docLitTestSuite():
+ """Run all doc/lit network tests"""
+ return _netTestSuite(broke=False, document=True, literal=True)
+
+def rpcLitTestSuite():
+ """Run all rpc/lit network tests"""
+ return _netTestSuite(broke=False, document=False, literal=True)
+
+def rpcEncTestSuite():
+ """Run all rpc/enc network tests"""
+ return _netTestSuite(broke=False, document=False, literal=False)
+
+
+# Low level functions
+def _allTestSuite(document=None, literal=None, broke=None):
+ return _makeTestSuite('all', document, literal, broke)
+
+def _netTestSuite(document=None, literal=None, broke=None):
+ return _makeTestSuite('net', document, literal, broke)
+
+def _localTestSuite(document=None, literal=None, broke=None):
+ return _makeTestSuite('local', document, literal, broke)
+
+def _dispatchTestSuite(document=None, literal=None, broke=None):
+ return _makeTestSuite('dispatch', document, literal, broke)
+
+
+def _makeTestSuite(test, document=None, literal=None, broke=None):
+ """Return a test suite containing all test cases that satisfy
+ the parameters. None means don't check.
+
+ Parameters:
+ test -- "net" run network tests, "local" run local tests,
+ "dispatch" run dispatch tests, "all" run all tests.
+ document -- None, True, False
+ literal -- None, True, False
+ broke -- None, True, False
+ """
+ assert test in ['net', 'local', 'dispatch', 'all'],(
+ 'test must be net, local, dispatch, or all')
+
+ cp = CONFIG_PARSER
+ testSections = []
+ sections = [\
+ 'rpc_encoded' , 'rpc_encoded_broke',
+ 'rpc_literal', 'rpc_literal_broke', 'rpc_literal_broke_interop',
+ 'doc_literal', 'doc_literal_broke', 'doc_literal_broke_interop',
+ ]
+ boo = cp.getboolean
+ for s,d,l,b in map(\
+ lambda sec: \
+ (sec, (None,boo(sec,DOCUMENT)), (None,boo(sec,LITERAL)), (None,boo(sec,BROKE))), sections):
+ if document in d and literal in l and broke in b:
+ testSections.append(s)
+
+ suite = unittest.TestSuite()
+ for section in testSections:
+ moduleList = cp.get(section, TESTS).split()
+ for module in map(__import__, moduleList):
+ def _warn_empty():
+ warnings.warn('"%s" has no test "%s"' %(module, test))
+ return unittest.TestSuite()
+
+ s = getattr(module, test, _warn_empty)()
+ suite.addTest(s)
+ return suite
+
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/test/wsdl2py/servers/EchoServer.py b/test/wsdl2py/servers/EchoServer.py
new file mode 100755
index 0000000..75eb971
--- /dev/null
+++ b/test/wsdl2py/servers/EchoServer.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys
+from ZSI.ServiceContainer import AsServer
+from EchoServer_services_server import EchoServer
+
+"""
+EchoServer example service
+
+WSDL: ../../samples/Echo/Echo.wsdl
+
+"""
+
+
+class Service(EchoServer):
+ def soap_Echo(self, ps):
+ response = EchoServer.soap_Echo(self, ps)
+ response.EchoResult = self.request.EchoIn
+ return response
+
+
+if __name__ == "__main__" :
+ port = int(sys.argv[1])
+ AsServer(port, (Service('test'),))
diff --git a/test/wsdl2py/servers/WhiteMesa.py b/test/wsdl2py/servers/WhiteMesa.py
new file mode 100755
index 0000000..73858d9
--- /dev/null
+++ b/test/wsdl2py/servers/WhiteMesa.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys
+from ZSI.ServiceContainer import AsServer
+from RPC_Literal_TestDefinitions_services_server import WhiteMesaSoapRpcLitTestSvc as WhiteMesa
+"""
+WhiteMesa web service for rpc/literal tests.
+
+WSDL: http://www.whitemesa.net/wsdl/test-rpc-lit.wsdl
+
+"""
+
+class Service(WhiteMesa):
+ def soap_echoStruct(self, ps):
+ response = WhiteMesa.soap_echoStruct(self, ps)
+ return response
+ def soap_echoStructArray(self, ps):
+ response = WhiteMesa.soap_echoStructArray(self, ps)
+ return response
+ def soap_echoStructAsSimpleTypes(self, ps):
+ response = WhiteMesa.soap_echoStructAsSimpleTypes(self, ps)
+ return response
+ def soap_echoSimpleTypesAsStruct(self, ps):
+ response = WhiteMesa.soap_echoSimpleTypesAsStruct(self, ps)
+ return response
+ def soap_echoNestedStruct(self, ps):
+ response = WhiteMesa.soap_echoNestedStruct(self, ps)
+ return response
+ def soap_echoNestedArray(self, ps):
+ response = WhiteMesa.soap_echoNestedArray(self, ps)
+ return response
+ def soap_echoStringArray(self, ps):
+ response = WhiteMesa.soap_echoStringArray(self, ps)
+ return response
+ def soap_echoIntegerArray(self, ps):
+ response = WhiteMesa.soap_echoIntegerArray(self, ps)
+ return response
+ def soap_echoBoolean(self, ps):
+ response = WhiteMesa.soap_echoBoolean(self, ps)
+ response._return = self.request._inputBoolean
+ return response
+ def soap_echoString(self, ps):
+ response = WhiteMesa.soap_echoString(self, ps)
+ return response
+
+if __name__ == "__main__" :
+ port = int(sys.argv[1])
+ AsServer(port, (Service('test'),))
+
+
diff --git a/test/wsdl2py/test_AWSECommerceService.py b/test/wsdl2py/test_AWSECommerceService.py
new file mode 100755
index 0000000..b5708ec
--- /dev/null
+++ b/test/wsdl2py/test_AWSECommerceService.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite, TestException
+from ZSI.schema import ElementDeclaration, GED
+from ZSI import ParsedSoap
+
+"""
+Unittest for contacting the Amazon ECommerce Service
+
+WSDL:
+
+"""
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_'))
+ return suite
+
+
+class AmazonTestCase(ServiceTestCase):
+ """Test case for Amazon ECommerce Web service
+ """
+ name = "test_AWSECommerceService"
+ client_file_name = "AWSECommerceService_services.py"
+ types_file_name = "AWSECommerceService_services_types.py"
+ server_file_name = "AWSECommerceService_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+ self.wsdl2py_args.append('--lazy')
+
+ def test_local_bug_1525567(self):
+ element = GED("http://webservices.amazon.com/AWSECommerceService/2006-09-18", 'Items')
+ # Make sure this is a GED
+ self.failUnless(isinstance(element, ElementDeclaration), '"%s" not a GED' %element)
+
+ def test_local_parse_ItemSearch(self):
+ msg = self.client_module.ItemSearchResponseMsg()
+ ps = ParsedSoap(ItemSearchResponseMsg)
+ response = ps.Parse(msg.typecode)
+ response.OperationRequest.Arguments
+ for i in response.OperationRequest.Arguments.Argument:
+ i.get_attribute_Name()
+ i.get_attribute_Value()
+
+ for i in response.OperationRequest.HTTPHeaders.Header or []:
+ i.get_attribute_Name()
+ i.get_attribute_Value()
+
+ response.OperationRequest.RequestId
+ response.OperationRequest.RequestProcessingTime
+ for its in response.Items:
+ self.failUnless(its.TotalResults == 55, '')
+ self.failUnless(its.TotalPages == 6, '')
+ for it in its.Item:
+ it.ASIN;
+ it.Accessories;
+ #it.AlternateVersions;
+ it.BrowseNodes
+ #it.Collections;
+ it.CustomerReviews ;it.DetailPageURL
+ it.EditorialReviews; it.Errors; it.ImageSets; it.ItemAttributes
+ it.LargeImage; it.ListmaniaLists; it.MediumImage; it.MerchantItemAttributes
+ it.OfferSummary; it.Offers;
+ #it.ParentASIN;
+ it.SalesRank; it.SearchInside
+ it.SimilarProducts; it.SmallImage; it.Subjects; it.Tracks;
+
+
+ def test_net_ItemSearch(self):
+ loc = self.client_module.AWSECommerceServiceLocator()
+ port = loc.getAWSECommerceServicePortType(**self.getPortKWArgs())
+
+ msg = self.client_module.ItemSearchRequestMsg()
+ msg.SubscriptionId = '0HP1WHME000749APYWR2'
+ request = msg.new_Request()
+ msg.Request = [request]
+
+ # request
+ request.ItemPage = 1
+ request.SearchIndex = "Books"
+ request.Keywords = 'Tamerlane'
+ request.ResponseGroup = ['Medium',]
+
+ response = port.ItemSearch(msg)
+
+ response.OperationRequest
+ self.failUnless(response.OperationRequest.Errors is None, 'ecommerce site reported errors')
+
+ response.OperationRequest.Arguments
+ for i in response.OperationRequest.Arguments.Argument:
+ i.get_attribute_Name()
+ i.get_attribute_Value()
+
+ for i in response.OperationRequest.HTTPHeaders.Header or []:
+ i.get_attribute_Name()
+ i.get_attribute_Value()
+
+ response.OperationRequest.RequestId
+ response.OperationRequest.RequestProcessingTime
+ for its in response.Items:
+ for it in its.Item:
+ it.ASIN;
+ it.Accessories;
+ #it.AlternateVersions;
+ it.BrowseNodes
+ #it.Collections;
+ it.CustomerReviews ;it.DetailPageURL
+ it.EditorialReviews; it.Errors; it.ImageSets; it.ItemAttributes
+ it.LargeImage; it.ListmaniaLists; it.MediumImage; it.MerchantItemAttributes
+ it.OfferSummary; it.Offers;
+ #it.ParentASIN;
+ it.SalesRank; it.SearchInside
+ it.SimilarProducts; it.SmallImage; it.Subjects; it.Tracks;
+ it.VariationSummary; it.Variations
+
+
+ItemSearchResponseMsg="""<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SOAP-ENV:Body><ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2006-09-18"><OperationRequest><HTTPHeaders><Header Name="UserAgent"></Header></HTTPHeaders><RequestId>1C167XDF9BX253MEYAF2</RequestId><Arguments><Argument Name="Service" Value="AWSECommerceService"></Argument></Arguments><RequestProcessingTime>0.987805128097534</RequestProcessingTime></OperationRequest><Items><Request><IsValid>True</IsValid><ItemSearchRequest><ItemPage>1</ItemPage><Keywords>Tamerlane</Keywords><ResponseGroup>Medium</ResponseGroup><SearchIndex>Books</SearchIndex></ItemSearchRequest></Request><TotalResults>55</TotalResults><TotalPages>6</TotalPages><Item><ASIN>030681465X</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=030681465X%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/030681465X%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>135340</SalesRank><SmallImage><URL>http://images.amazon.com/images/P/030681465X.01._SCTHUMBZZZ_V66860320_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">50</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/030681465X.01._SCMZZZZZZZ_V66860320_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">108</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/030681465X.01._SCLZZZZZZZ_V66860320_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">337</Width></LargeImage><ImageSets><ImageSet Category="primary"><SmallImage><URL>http://images.amazon.com/images/P/030681465X.01._SCTHUMBZZZ_V66860320_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">50</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/030681465X.01._SCMZZZZZZZ_V66860320_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">108</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/030681465X.01._SCLZZZZZZZ_V66860320_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">337</Width></LargeImage></ImageSet></ImageSets><ItemAttributes><Author>Justin Marozzi</Author><Binding>Hardcover</Binding><DeweyDecimalNumber>920</DeweyDecimalNumber><EAN>9780306814655</EAN><Edition>New Ed</Edition><ISBN>030681465X</ISBN><Label>Da Capo Press</Label><ListPrice><Amount>2695</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$26.95</FormattedPrice></ListPrice><Manufacturer>Da Capo Press</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>368</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">150</Height><Length Units="hundredths-inches">904</Length><Weight Units="hundredths-pounds">174</Weight><Width Units="hundredths-inches">640</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>2006-02-22</PublicationDate><Publisher>Da Capo Press</Publisher><Studio>Da Capo Press</Studio><Title>Tamerlane: Sword of Islam, Conqueror of the World</Title></ItemAttributes><OfferSummary><LowestNewPrice><Amount>831</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$8.31</FormattedPrice></LowestNewPrice><LowestUsedPrice><Amount>832</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$8.32</FormattedPrice></LowestUsedPrice><TotalNew>43</TotalNew><TotalUsed>26</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary><EditorialReviews><EditorialReview><Source>Book Description</Source><Content>A powerful account of the life of Tamerlane the Great (1336-1405), the last great Mongol conqueror of Central Asia, ruler of a vast empire, and one of history's most brutal tyrants &lt;P&gt; Tamerlane, aka Temur-the Mongol successor to Genghis Khan-ranks with Alexander the Great as one of the world's great conquerors, yet the details of his life are scarcely known in the West. Born in obscurity and poverty, he rose to become a fierce tribal leader, and with that his dominion and power grew with astonishing speed. He blazed through Asia, razing cities to the ground. He tortured conquered inhabitants without mercy, sometimes ordering them buried alive, at other times decapitating them. Over the ruins of conquered Baghdad, Tamerlane had his soldiers erect a pyramid of 90,000 enemy heads. As he and his armies swept through Central Asia, sacking, and then rebuilding cities, Tamerlane gradually imposed an iron rule and a refined culture over a vast territory-from the steppes of Asia to the Syrian coastline. &lt;P&gt; Justin Marozzi traveled in the footsteps of this fearsome emperor of Samarkand (modern-day Uzbekistan) to write this book, which is part history, part travelogue. He carefully follows the path of this infamous and enigmatic conqueror, recounting the history and the story of this cruel, cultivated, and indomitable warrior.</Content></EditorialReview></EditorialReviews></Item><Item><ASIN>1853141046</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=1853141046%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/1853141046%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>366445</SalesRank><ItemAttributes><Author>David Nicolle</Author><Author>Richard Hook</Author><Binding>Hardcover</Binding><DeweyDecimalNumber>950.20922</DeweyDecimalNumber><EAN>9781853141041</EAN><ISBN>1853141046</ISBN><Label>Firebird</Label><ListPrice><Amount>2495</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$24.95</FormattedPrice></ListPrice><Manufacturer>Firebird</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>208</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">1000</Height><Length Units="hundredths-inches">75</Length><Weight Units="hundredths-pounds">160</Weight><Width Units="hundredths-inches">775</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>1990-09</PublicationDate><Publisher>Firebird</Publisher><Studio>Firebird</Studio><Title>The Mongol Warlords: Ghengis Khan, Kublai Khan, Hulegu, Tamerlane (Heroes &amp; Warriors)</Title></ItemAttributes><OfferSummary><LowestUsedPrice><Amount>1095</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$10.95</FormattedPrice></LowestUsedPrice><TotalNew>0</TotalNew><TotalUsed>3</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary></Item><Item><ASIN>0521633842</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0521633842%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/0521633842%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>429712</SalesRank><SmallImage><URL>http://images.amazon.com/images/P/0521633842.01._SCTHUMBZZZ_V1114821525_.jpg</URL><Height Units="pixels">60</Height><Width Units="pixels">39</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/0521633842.01._SCMZZZZZZZ_V1114821525_.jpg</URL><Height Units="pixels">140</Height><Width Units="pixels">90</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/0521633842.01._SCLZZZZZZZ_V1114821525_.jpg</URL><Height Units="pixels">475</Height><Width Units="pixels">306</Width></LargeImage><ImageSets><ImageSet Category="primary"><SmallImage><URL>http://images.amazon.com/images/P/0521633842.01._SCTHUMBZZZ_V1114821525_.jpg</URL><Height Units="pixels">60</Height><Width Units="pixels">39</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/0521633842.01._SCMZZZZZZZ_V1114821525_.jpg</URL><Height Units="pixels">140</Height><Width Units="pixels">90</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/0521633842.01._SCLZZZZZZZ_V1114821525_.jpg</URL><Height Units="pixels">475</Height><Width Units="pixels">306</Width></LargeImage></ImageSet></ImageSets><ItemAttributes><Author>Beatrice Forbes Manz</Author><Binding>Paperback</Binding><DeweyDecimalNumber>950.2</DeweyDecimalNumber><EAN>9780521633840</EAN><Edition>Reprint</Edition><ISBN>0521633842</ISBN><Label>Cambridge University Press</Label><ListPrice><Amount>2299</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$22.99</FormattedPrice></ListPrice><Manufacturer>Cambridge University Press</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>246</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">67</Height><Length Units="hundredths-inches">850</Length><Weight Units="hundredths-pounds">74</Weight><Width Units="hundredths-inches">562</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>1999-03-28</PublicationDate><Publisher>Cambridge University Press</Publisher><Studio>Cambridge University Press</Studio><Title>The Rise and Rule of Tamerlane (Canto original series)</Title></ItemAttributes><OfferSummary><LowestNewPrice><Amount>1298</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$12.98</FormattedPrice></LowestNewPrice><LowestUsedPrice><Amount>1054</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$10.54</FormattedPrice></LowestUsedPrice><LowestCollectiblePrice><Amount>2299</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$22.99</FormattedPrice></LowestCollectiblePrice><TotalNew>25</TotalNew><TotalUsed>21</TotalUsed><TotalCollectible>1</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary><EditorialReviews><EditorialReview><Source>Book Description</Source><Content>This is the first serious study of Tamerlane, the great nomad conqueror who rose to power in 1370 on the ruins of the Mongol Empire and led his armies on campaigns of unprecedented scope, ranging from Moscow to Delhi. As the last nomad ruler to unite the steppe regions of Eurasia, Tamerlane marks the transition from the era of nomad conquest and rule to the modern ascendency of the settled world.</Content></EditorialReview></EditorialReviews></Item><Item><ASIN>1885221770</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=1885221770%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/1885221770%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>474520</SalesRank><SmallImage><URL>http://images.amazon.com/images/P/1885221770.01._SCTHUMBZZZ_V1056534986_.jpg</URL><Height Units="pixels">60</Height><Width Units="pixels">40</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/1885221770.01._SCMZZZZZZZ_V1056534986_.jpg</URL><Height Units="pixels">140</Height><Width Units="pixels">93</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/1885221770.01._SCLZZZZZZZ_V1056534986_.jpg</URL><Height Units="pixels">475</Height><Width Units="pixels">317</Width></LargeImage><ImageSets><ImageSet Category="primary"><SmallImage><URL>http://images.amazon.com/images/P/1885221770.01._SCTHUMBZZZ_V1056534986_.jpg</URL><Height Units="pixels">60</Height><Width Units="pixels">40</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/1885221770.01._SCMZZZZZZZ_V1056534986_.jpg</URL><Height Units="pixels">140</Height><Width Units="pixels">93</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/1885221770.01._SCLZZZZZZZ_V1056534986_.jpg</URL><Height Units="pixels">475</Height><Width Units="pixels">317</Width></LargeImage></ImageSet></ImageSets><ItemAttributes><Author>Roy Stier</Author><Binding>Paperback</Binding><EAN>9781885221773</EAN><ISBN>1885221770</ISBN><Label>Bookpartners</Label><ListPrice><Amount>1695</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$16.95</FormattedPrice></ListPrice><Manufacturer>Bookpartners</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>304</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">79</Height><Length Units="hundredths-inches">900</Length><Weight Units="hundredths-pounds">92</Weight><Width Units="hundredths-inches">606</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>1998-09</PublicationDate><Publisher>Bookpartners</Publisher><Studio>Bookpartners</Studio><Title>Tamerlane: The Ultimate Warrior</Title></ItemAttributes><OfferSummary><LowestUsedPrice><Amount>975</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$9.75</FormattedPrice></LowestUsedPrice><LowestCollectiblePrice><Amount>2295</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$22.95</FormattedPrice></LowestCollectiblePrice><TotalNew>0</TotalNew><TotalUsed>3</TotalUsed><TotalCollectible>1</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary><EditorialReviews><EditorialReview><Source>Book Description</Source><Content>From humble beginnings, Tamerlane, the ancient Turki-Mongol conqueror, rose to become the scourge of his time and changed the course of history. &lt;P&gt;The name Tamerlane runs the gamut of human emotions, evoking in many a revulsion for the devil incarnate, in others, an appreciation for the benefactor of millions. &lt;P&gt;By using accounts from Tamerlane's detractors and his admirers, Roy Stier has captured an amazing story that gives credence to the old adage, "truth is stranger than fiction." &lt;P&gt;Tamerlane, the Ultimate Warrior is presented as a fascinating series of events and captures the reader in the first comprehensive view of this historical figure who dominated Asia and made Europe tremble.</Content></EditorialReview></EditorialReviews></Item><Item><ASIN>0850459494</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0850459494%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/0850459494%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>480359</SalesRank><SmallImage><URL>http://images.amazon.com/images/P/0850459494.01._SCTHUMBZZZ_V1128022797_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">56</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/0850459494.01._SCMZZZZZZZ_V1128022797_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">119</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/0850459494.01._SCLZZZZZZZ_V1128022797_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">372</Width></LargeImage><ImageSets><ImageSet Category="primary"><SmallImage><URL>http://images.amazon.com/images/P/0850459494.01._SCTHUMBZZZ_V1128022797_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">56</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/0850459494.01._SCMZZZZZZZ_V1128022797_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">119</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/0850459494.01._SCLZZZZZZZ_V1128022797_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">372</Width></LargeImage></ImageSet></ImageSets><ItemAttributes><Author>David Nicolle</Author><Binding>Paperback</Binding><Creator Role="Illustrator">Angus Mcbride</Creator><EAN>9780850459494</EAN><ISBN>0850459494</ISBN><Label>Osprey Publishing</Label><ListPrice><Amount>1595</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$15.95</FormattedPrice></ListPrice><Manufacturer>Osprey Publishing</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>48</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">15</Height><Length Units="hundredths-inches">978</Length><Weight Units="hundredths-pounds">36</Weight><Width Units="hundredths-inches">722</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>1990-07-26</PublicationDate><Publisher>Osprey Publishing</Publisher><ReleaseDate>1990-07-26</ReleaseDate><Studio>Osprey Publishing</Studio><Title>The Age of Tamerlane (Men-at-Arms)</Title></ItemAttributes><OfferSummary><LowestNewPrice><Amount>920</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$9.20</FormattedPrice></LowestNewPrice><LowestUsedPrice><Amount>815</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$8.15</FormattedPrice></LowestUsedPrice><TotalNew>12</TotalNew><TotalUsed>9</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary><EditorialReviews><EditorialReview><Source>Book Description</Source><Content>Tamerlane or Timur-i-Lenk ('Timur the Lame') is one of the most extraordinary conquerors in history. In the late 14th century his armies seized huge territories from the borders of Mongolia to Palestine and Anatolia. His passage was marked by massacres that outdid even those of the Mongols for sheer savagery. Timur's career was unequalled since Alexander the Great in terms of constant battlefield success. Only in his youth, while recovering his family estates south of Samarqand, did he face occasional defeat. This title tells the remarkable story of Timur and details the organisation, tactics, arms and armour of his all-conquering army.</Content></EditorialReview></EditorialReviews></Item><Item><ASIN>1851684573</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=1851684573%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/1851684573%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>600493</SalesRank><SmallImage><URL>http://images.amazon.com/images/P/1851684573.01._SCTHUMBZZZ_V63318467_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">49</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/1851684573.01._SCMZZZZZZZ_V63318467_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">104</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/1851684573.01._SCLZZZZZZZ_V63318467_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">324</Width></LargeImage><ImageSets><ImageSet Category="primary"><SmallImage><URL>http://images.amazon.com/images/P/1851684573.01._SCTHUMBZZZ_V63318467_.jpg</URL><Height Units="pixels">75</Height><Width Units="pixels">49</Width></SmallImage><MediumImage><URL>http://images.amazon.com/images/P/1851684573.01._SCMZZZZZZZ_V63318467_.jpg</URL><Height Units="pixels">160</Height><Width Units="pixels">104</Width></MediumImage><LargeImage><URL>http://images.amazon.com/images/P/1851684573.01._SCLZZZZZZZ_V63318467_.jpg</URL><Height Units="pixels">500</Height><Width Units="pixels">324</Width></LargeImage></ImageSet></ImageSets><ItemAttributes><Author>Robert Rand</Author><Binding>Paperback</Binding><DeweyDecimalNumber>320</DeweyDecimalNumber><EAN>9781851684571</EAN><ISBN>1851684573</ISBN><Label>Oneworld Publications</Label><ListPrice><Amount>1495</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$14.95</FormattedPrice></ListPrice><Manufacturer>Oneworld Publications</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>224</NumberOfPages><PackageDimensions><Height Units="hundredths-inches">66</Height><Length Units="hundredths-inches">784</Length><Weight Units="hundredths-pounds">54</Weight><Width Units="hundredths-inches">512</Width></PackageDimensions><ProductGroup>Book</ProductGroup><PublicationDate>2006-09-11</PublicationDate><Publisher>Oneworld Publications</Publisher><Studio>Oneworld Publications</Studio><Title>Tamerlane's Children: Dispatches from Contemporary Uzbekistan</Title></ItemAttributes><OfferSummary><LowestNewPrice><Amount>915</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$9.15</FormattedPrice></LowestNewPrice><LowestUsedPrice><Amount>950</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$9.50</FormattedPrice></LowestUsedPrice><TotalNew>31</TotalNew><TotalUsed>6</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary><EditorialReviews><EditorialReview><Source>Book Description</Source><Content>In the central park of Tashkent, in a place the Uzbeks call the square, a magnificent statue of a mounted warrior dominates the surroundings - Tamerlane - national hero of post-Soviet Uzbekistan. And yet how does this 14th century conqueror reflect one of the world's most diverse and politically intriguing countries?Having spent three years in the region, renowned journalist Robert Rand seeks to answer this question, covering an assortment of fascinating topics, ranging from the effect of 9/11 to the clash of culture in Uzbek pop music. Overflowing with charming anecdotes and loveable personalities, Rand gives the reader a real sense of the country's confused identity and the challenges which it and its people will face in generations to come.</Content></EditorialReview></EditorialReviews></Item><Item><ASIN>B000GKT9AQ</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=B000GKT9AQ%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/B000GKT9AQ%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>619234</SalesRank><ItemAttributes><Author>Harold Lamb</Author><Binding>Hardcover</Binding><Label>Garden City, N.Y., U.S.A.: Doubleday &amp; Company, Inc.</Label><Manufacturer>Garden City, N.Y., U.S.A.: Doubleday &amp; Company, Inc.</Manufacturer><ProductGroup>Book</ProductGroup><ProductTypeName>BOOKS_1973_AND_LATER</ProductTypeName><PublicationDate>1941</PublicationDate><Publisher>Garden City, N.Y., U.S.A.: Doubleday &amp; Company, Inc.</Publisher><Studio>Garden City, N.Y., U.S.A.: Doubleday &amp; Company, Inc.</Studio><Title>Earth Shakers: The: The March of the Barbarians and Tamerlane</Title></ItemAttributes><OfferSummary><LowestUsedPrice><Amount>850</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$8.50</FormattedPrice></LowestUsedPrice><TotalNew>0</TotalNew><TotalUsed>2</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary></Item><Item><ASIN>B00087SKA2</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=B00087SKA2%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/B00087SKA2%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>855185</SalesRank><ItemAttributes><Author>Harold Lamb</Author><Binding>Unknown Binding</Binding><Label>Bantam Books</Label><Manufacturer>Bantam Books</Manufacturer><NumberOfPages>216</NumberOfPages><ProductGroup>Book</ProductGroup><PublicationDate>1955</PublicationDate><Publisher>Bantam Books</Publisher><Studio>Bantam Books</Studio><Title>Tamerlane: Conqueror of the earth</Title></ItemAttributes><OfferSummary><LowestUsedPrice><Amount>650</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$6.50</FormattedPrice></LowestUsedPrice><TotalNew>0</TotalNew><TotalUsed>5</TotalUsed><TotalCollectible>0</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary></Item><Item><ASIN>0689304463</ASIN><DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0689304463%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/0689304463%253FSubscriptionId=0HP1WHME000749APYWR2</DetailPageURL><SalesRank>1028876</SalesRank><ItemAttributes><Author>Barbara Corcoran</Author><Binding>Hardcover</Binding><Creator Role="Illustrator">Charles Robinson</Creator><EAN>9780689304460</EAN><Edition>[1st ed.]</Edition><ISBN>0689304463</ISBN><Label>Macmillan Pub Co</Label><ListPrice><Amount>695</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$6.95</FormattedPrice></ListPrice><Manufacturer>Macmillan Pub Co</Manufacturer><NumberOfItems>1</NumberOfItems><NumberOfPages>152</NumberOfPages><ProductGroup>Book</ProductGroup><PublicationDate>1975-02</PublicationDate><Publisher>Macmillan Pub Co</Publisher><ReadingLevel>Young Adult</ReadingLevel><Studio>Macmillan Pub Co</Studio><Title>Meet Me at Tamerlane's Tomb</Title></ItemAttributes><OfferSummary><LowestUsedPrice><Amount>30</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$0.30</FormattedPrice></LowestUsedPrice><LowestCollectiblePrice><Amount>1000</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$10.00</FormattedPrice></LowestCollectiblePrice><TotalNew>0</TotalNew><TotalUsed>20</TotalUsed><TotalCollectible>2</TotalCollectible><TotalRefurbished>0</TotalRefurbished></OfferSummary></Item></Items></ItemSearchResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>"""
+
+if __name__ == '__main__':
+ main()
diff --git a/test/wsdl2py/test_AmazonS3.py b/test/wsdl2py/test_AmazonS3.py
new file mode 100755
index 0000000..506f949
--- /dev/null
+++ b/test/wsdl2py/test_AmazonS3.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest, time
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite, TestException
+from ZSI.schema import ElementDeclaration, GED
+from ZSI import ParsedSoap
+
+"""
+Unittest for contacting the Amazon ECommerce Service
+
+WSDL:
+
+"""
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AmazonTestCase, 'test_'))
+ return suite
+
+
+class AmazonTestCase(ServiceTestCase):
+ """Test case for AmazonS3 web service
+ """
+ name = "test_AmazonS3"
+ client_file_name = "AmazonS3_services.py"
+ types_file_name = "AmazonS3_services_types.py"
+ server_file_name = "AmazonS3_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+ self.wsdl2py_args.append('--lazy')
+
+ def test_local_import(self):
+ pass
+
+ def test_net_CreateBucket(self):
+ loc = self.client_module.AmazonS3Locator()
+ port = loc.getAmazonS3(**self.getPortKWArgs())
+
+ msg = self.client_module.CreateBucketRequest()
+ #msg.SubscriptionId = '0HP1WHME000749APYWR2'
+ msg.Bucket = "HoneyPot"
+ acl = msg.AccessControlList = msg.new_AccessControlList()
+ grant = acl.new_Grant()
+ acl.Grant = [grant]
+ grant.Grantee = grant.new_Grantee()
+ grant.Permission = grant.new_Permission("YES")
+
+ msg.AWSAccessKeyId = '0HP1WHME000749APYWR2'
+ msg.Timestamp = time.gmtime()
+ msg.Signature = 'whatever'
+
+ rsp = port.CreateBucket(msg)
+
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/wsdl2py/test_Attributes.py b/test/wsdl2py/test_Attributes.py
new file mode 100644
index 0000000..2fd55fa
--- /dev/null
+++ b/test/wsdl2py/test_Attributes.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import os, sys, unittest, time
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import FaultException
+from ZSI.TC import _get_global_element_declaration as GED
+from ZSI.writer import SoapWriter
+from ZSI.parse import ParsedSoap
+
+"""
+Unittest for Bug Report
+[ ]
+
+XSD: test_Attributes.xsd
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AttributeTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AttributeTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AttributeTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(AttributeTestCase, 'test_'))
+ return suite
+
+
+class AttributeTestCase(ServiceTestCase):
+ name = "test_Attributes"
+ types_file_name = "test_Attributes_xsd_services_types.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+ self.wsdl2py_args.append('-x')
+
+ def test_local_attribute1(self):
+ """
+ """
+ myDouble = 4.5
+ myInt = 9
+ myFloat = 3.0001
+ myDecimal = 8.999
+ myGDateTime = time.gmtime()
+ myAnyURI = "urn:whatever"
+ myQName = ("urn:test", "qbert")
+ myString = "whatever"
+ myHexBinary = hex(888)
+
+ pyobj = GED("urn:example", "Test1").pyclass()
+ # Test serialize/parse
+ pyobj.set_attribute_myDecimal(myDecimal)
+ pyobj.set_attribute_myDouble(myDouble)
+ pyobj.set_attribute_myFloat(myFloat)
+ pyobj.set_attribute_myInt(myInt)
+ pyobj.set_attribute_myDateTime(myGDateTime)
+
+ pyobj.set_attribute_myGDay(myGDateTime)
+ pyobj.set_attribute_myGMonth(myGDateTime)
+ pyobj.set_attribute_myGYear(myGDateTime)
+ pyobj.set_attribute_myGYearMonth(myGDateTime)
+ pyobj.set_attribute_myDate(myGDateTime)
+ pyobj.set_attribute_myTime(myGDateTime)
+
+ pyobj.set_attribute_myAnyURI(myAnyURI)
+ pyobj.set_attribute_myString(myString)
+ pyobj.set_attribute_myHexBinary(myHexBinary)
+ pyobj.set_attribute_myDuration(myGDateTime)
+
+ # Problems parsings
+ pyobj.set_attribute_myQName(myQName)
+ pyobj.set_attribute_myGMonthDay(myGDateTime)
+
+
+ #TODO:
+ #pyobj.set_attribute_myBase64Binary("")
+ #pyobj.set_attribute_myNOTATION("NOT")
+
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ soap = str(sw)
+
+ ps = ParsedSoap(soap)
+ pyobj2 = ps.Parse(pyobj.typecode)
+
+ test = pyobj2.get_attribute_myInt()
+ self.failUnlessEqual(myInt, test)
+
+ test = pyobj2.get_attribute_myDouble()
+ self.failUnlessEqual(myDouble, test)
+
+ test = pyobj2.get_attribute_myFloat()
+ self.failUnlessEqual(myFloat, test)
+
+ test = pyobj2.get_attribute_myDecimal()
+ self.failUnlessEqual(myDecimal, test)
+
+ test = pyobj2.get_attribute_myAnyURI()
+ self.failUnlessEqual(myAnyURI, test)
+
+ test = pyobj2.get_attribute_myQName()
+ self.failUnlessEqual(myQName, test)
+
+ test = pyobj2.get_attribute_myString()
+ self.failUnlessEqual(myString, test)
+
+ test = pyobj2.get_attribute_myHexBinary()
+ self.failUnlessEqual(myHexBinary, test)
+
+ # DateTime stuff
+ test = pyobj2.get_attribute_myDateTime()
+ self.failUnlessEqual(myGDateTime[:-3], test[:-3])
+
+ test = pyobj2.get_attribute_myDate()
+ self.failUnlessEqual(myGDateTime[:3], test[:3])
+
+ test = pyobj2.get_attribute_myTime()
+ self.failUnlessEqual(myGDateTime[4:5], test[4:5])
+
+ test = pyobj.get_attribute_myDuration()
+ self.failUnlessEqual(myGDateTime, test)
+
+ # Bug [ 1453421 ] Incorrect format for type gDay
+ test = pyobj2.get_attribute_myGDay()
+ self.failUnlessEqual(myGDateTime[2], test[2])
+
+ test = pyobj2.get_attribute_myGMonth()
+ self.failUnlessEqual(myGDateTime[1], test[1])
+
+ test = pyobj2.get_attribute_myGYear()
+ self.failUnlessEqual(myGDateTime[0], test[0])
+
+ test = pyobj2.get_attribute_myGYearMonth()
+ self.failUnlessEqual(myGDateTime[:2], test[:2])
+
+ # hmm? negated?
+ #test = pyobj2.get_attribute_myGMonthDay()
+ #self.failUnlessEqual(myGDateTime[1:3], test[1:3])
+
+ def test_local_empty_attribute(self):
+ # [ 1452752 ] attribute with empty value doesn't appear in parsed object
+ myString = ""
+ pyobj = GED("urn:example", "Test1").pyclass()
+ pyobj.set_attribute_myString(myString)
+
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ soap = str(sw)
+
+ print soap
+ ps = ParsedSoap(soap)
+ pyobj2 = ps.Parse(pyobj.typecode)
+
+ test = pyobj2.get_attribute_myString()
+ self.failUnlessEqual(myString, str(test))
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/test/wsdl2py/test_Attributes.xsd b/test/wsdl2py/test_Attributes.xsd
new file mode 100644
index 0000000..2ee3399
--- /dev/null
+++ b/test/wsdl2py/test_Attributes.xsd
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="urn:example"
+ xmlns:bt="urn:example">
+<!-- test all xsd simple types,
+test local and global attributes,
+test attribute groups
+-->
+
+<xs:element name="Test1" type="bt:Test1"/>
+
+<xs:attribute name="myName" type="xs:string"/>
+<xs:attribute name="myDouble" type="xs:double"/>
+<xs:attribute name="myString" type="xs:string"/>
+
+<xs:attributeGroup name="common">
+ <xs:attribute ref="bt:myString"/>
+ <xs:attribute name="myFloat" type="xs:float"/>
+ <xs:attribute name="myDecimal" type="xs:decimal"/>
+ <xs:attribute name="myDuration" type="xs:duration"/>
+ <xs:attribute name="myDateTime" type="xs:dateTime"/>
+ <xs:attribute name="myTime" type="xs:time"/>
+ <xs:attribute name="myDate" type="xs:date"/>
+ <xs:attribute name="myGYearMonth" type="xs:gYearMonth"/>
+ <xs:attribute name="myGYear" type="xs:gYear"/>
+ <xs:attribute name="myGMonthDay" type="xs:gMonthDay"/>
+ <xs:attribute name="myGDay" type="xs:gDay"/>
+ <xs:attribute name="myGMonth" type="xs:gMonth"/>
+ <xs:attribute name="myHexBinary" type="xs:hexBinary"/>
+ <xs:attribute name="myBase64Binary" type="xs:base64Binary"/>
+ <xs:attribute name="myAnyURI" type="xs:anyURI"/>
+ <xs:attribute name="myQName" type="xs:QName"/>
+ <xs:attribute name="myNOTATION" type="xs:NOTATION"/>
+</xs:attributeGroup>
+
+<xs:complexType name="Test1">
+ <xs:sequence/>
+ <xs:attribute name="myInt" type="xs:int"/>
+ <xs:attribute ref="bt:myDouble"/>
+ <xs:attributeGroup ref="bt:common"/>
+</xs:complexType>
+
+</xs:schema>
diff --git a/test/wsdl2py/test_Choice.py b/test/wsdl2py/test_Choice.py
new file mode 100644
index 0000000..7bd5584
--- /dev/null
+++ b/test/wsdl2py/test_Choice.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import os, sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import FaultException
+from ZSI.TC import _get_global_element_declaration as GED
+from ZSI.writer import SoapWriter
+
+"""
+Unittest for Bug Report
+[ 1441574 ] ZSI assumes minOccurs(1) for all parts
+
+WSDL:
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ChoiceTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ChoiceTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ChoiceTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ChoiceTestCase, 'test_'))
+ return suite
+
+
+class ChoiceTestCase(ServiceTestCase):
+ name = "test_Choice"
+ types_file_name = "test_Choice_xsd_services_types.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+ self.wsdl2py_args.append('-x')
+
+ def test_local_choice_default_facets_legal1(self):
+ """<choice minOccurs=1 maxOccurs=1>
+ """
+ pyobj = GED("urn:example", "Easy").pyclass()
+ pyobj.Rank = 1
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ print str(sw)
+
+ def test_local_choice_maxOccurs_unbounded(self):
+ """<choice minOccurs=1 maxOccurs=unbounded>
+ """
+ pyobj = GED("urn:example", "Hard").pyclass()
+ pyobj.Name = "steve"
+ pyobj.Name.append("mark")
+ pyobj.Any = "whatever"
+ pyobj.Rank = 2
+ pyobj.Rank.append(3)
+ pyobj.Rank.append(4)
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ print str(sw)
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/test/wsdl2py/test_Choice.xsd b/test/wsdl2py/test_Choice.xsd
new file mode 100644
index 0000000..70f4c34
--- /dev/null
+++ b/test/wsdl2py/test_Choice.xsd
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="urn:example"
+ xmlns:bt="urn:example">
+
+<xs:element name="Easy" type="bt:EasyChoice"/>
+<xs:element name="Hard" type="bt:HardChoice"/>
+
+<xs:complexType name="EasyChoice">
+ <xs:choice>
+ <xs:element name="name" type="xs:string"/>
+ <xs:element name="rank" type="xs:int"/>
+ <xs:element name="any"/>
+ </xs:choice>
+</xs:complexType>
+
+<xs:complexType name="HardChoice">
+ <xs:choice maxOccurs="unbounded">
+ <xs:element name="name" type="xs:string"/>
+ <xs:element name="rank" type="xs:int"/>
+ <xs:element name="any"/>
+ </xs:choice>
+</xs:complexType>
+
+</xs:schema>
diff --git a/test/wsdl2py/test_DerivedTypes.py b/test/wsdl2py/test_DerivedTypes.py
new file mode 100644
index 0000000..909f12b
--- /dev/null
+++ b/test/wsdl2py/test_DerivedTypes.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import os, sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import EvaluateException, FaultException
+from ZSI.writer import SoapWriter
+from ZSI.parse import ParsedSoap
+from ZSI.TC import _get_type_definition as GTD
+from ZSI.TC import _get_global_element_declaration as GED
+
+"""
+Unittest
+
+WSDL: derivedTypes.
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(DTTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(DTTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(DTTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(DTTestCase, 'test_'))
+ return suite
+
+
+class DTTestCase(ServiceTestCase):
+ name = "test_DerivedTypes"
+ client_file_name = None
+ types_file_name = "test_DerivedTypes_xsd_services_types.py"
+ server_file_name = None
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-x')
+ self.wsdl2py_args.append('-b')
+
+ def test_local_ged_substitution(self):
+ """This test is designed to fail, trying to dump
+ a GED in via type substitution.
+ """
+ self.types_module
+ pyobj = GED('urn:test', 'test').pyclass()
+
+ # use GED of a derived type
+ pyobj.Actor = sub = GED('urn:test', 'MiddleActor').pyclass()
+ sub.Element1 = 'foo'
+ sub.Element2 = 'bar'
+
+ sw = SoapWriter()
+ self.failUnlessRaises(TypeError, sw.serialize, pyobj)
+
+ def test_local_type_substitution_test2(self):
+ """test extension of extension"""
+
+ attr1 = 'aone'
+ attr2 = 'atwo'
+ attr3 = 'athree'
+ self.types_module
+ pyobj = GED('urn:test', 'test2').pyclass()
+
+ # Test maxOccurs>1 for substitution
+ #
+ pyobj.Actor = [GTD('urn:test', 'TopActor')(None).pyclass()]
+ sub1 = pyobj.Actor[0]
+ sub1.Element1 = 'one'
+ sub1.Element2 = 'two'
+ sub1.Element3 = 'three'
+ sub1.set_attribute_attr1(attr1)
+ sub1.set_attribute_attr2(attr2)
+ sub1.set_attribute_attr3(attr3)
+
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ xml = str(sw)
+ ps = ParsedSoap(xml)
+ pyobj2 = ps.Parse(pyobj.typecode)
+ sub2 = pyobj2.Actor[0]
+
+ self.failUnless(sub2.get_attribute_attr1() == attr1, 'bad attribute 1')
+ self.failUnless(sub2.get_attribute_attr2() == attr2, 'bad attribute 2')
+ self.failUnless(sub2.get_attribute_attr3() == attr3, 'bad attribute 3')
+
+ self.failUnless(sub2.Element1 == sub1.Element1, 'bad element 1')
+ self.failUnless(sub2.Element2 == sub1.Element2, 'bad element 2')
+ self.failUnless(sub2.Element3 == sub1.Element3, 'bad element 3')
+
+ # check parsed out correct type
+ self.failUnless(isinstance(sub2.typecode, sub1.typecode.__class__),
+ 'local element actor "%s" must be an instance of "%s"'%
+ (sub2.typecode, sub1.typecode.__class__))
+
+ # check local element is derived from base
+ base = GTD('urn:test', 'BaseActor')
+ self.failUnless(isinstance(sub2.typecode, base),
+ 'local element actor must be a derived type of "%s"'%
+ base)
+
+
+ def test_local_type_substitution2(self):
+ """test extension of extension"""
+
+ attr1 = 'aone'
+ attr2 = 'atwo'
+ attr3 = 'athree'
+ self.types_module
+ pyobj = GED('urn:test', 'test').pyclass()
+
+ # [ 1489129 ] Unexpected subsitution error message
+ # try to parse before type ever initialized
+ """
+ ps = ParsedSoap(MSG1)
+ pyobj0 = ps.Parse(pyobj.typecode)
+ sub0 = pyobj0.Actor
+ self.failUnless(sub0.get_attribute_attr1() == attr1, 'bad attribute1')
+ self.failUnless(sub0.get_attribute_attr2() == attr2, 'bad attribute2')
+ """
+
+ # [ 1489090 ] Derived type attributes don't populate the attr dictionary
+ # [ 1489677 ] Derivation from derived type missing derived element
+ #
+ pyobj.Actor = sub1 = GTD('urn:test', 'TopActor')(None).pyclass()
+ sub1.Element1 = 'one'
+ sub1.Element2 = 'two'
+ sub1.Element3 = 'three'
+ sub1.set_attribute_attr1(attr1)
+ sub1.set_attribute_attr2(attr2)
+ sub1.set_attribute_attr3(attr3)
+
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ xml = str(sw)
+ ps = ParsedSoap(xml)
+ pyobj2 = ps.Parse(pyobj.typecode)
+ sub2 = pyobj2.Actor
+
+ self.failUnless(sub2.get_attribute_attr1() == attr1, 'bad attribute 1')
+ self.failUnless(sub2.get_attribute_attr2() == attr2, 'bad attribute 2')
+ self.failUnless(sub2.get_attribute_attr3() == attr3, 'bad attribute 3')
+
+ self.failUnless(sub2.Element1 == sub1.Element1, 'bad element 1')
+ self.failUnless(sub2.Element2 == sub1.Element2, 'bad element 2')
+ self.failUnless(sub2.Element3 == sub1.Element3, 'bad element 3')
+
+ # check parsed out correct type
+ self.failUnless(isinstance(sub2.typecode, sub1.typecode.__class__),
+ 'local element actor "%s" must be an instance of "%s"'%
+ (sub2.typecode, sub1.typecode.__class__))
+
+ # check local element is derived from base
+ base = GTD('urn:test', 'BaseActor')
+ self.failUnless(isinstance(sub2.typecode, base),
+ 'local element actor must be a derived type of "%s"'%
+ base)
+
+ def test_local_parse_missing_type_substitution(self):
+ """attempt to substitute an unregistered/unknown type """
+ attr1 = 'myclass'
+ attr2 = 'whatever'
+ self.types_module
+ pyobj = GED('urn:test', 'test').pyclass()
+
+ ps = ParsedSoap(NO_SUB_MSG)
+ self.failUnlessRaises(EvaluateException, ps.Parse, pyobj.typecode)
+
+ def test_local_type_substitution1(self):
+ """test extension. Parse known instance, serialize an equivalent, Parse it back. """
+ attr1 = 'myclass'
+ attr2 = 'whatever'
+ self.types_module
+ pyobj = GED('urn:test', 'test').pyclass()
+
+ # [ 1489129 ] Unexpected subsitution error message
+ # try to parse before type ever initialized
+ ps = ParsedSoap(MSG1)
+ pyobj0 = ps.Parse(pyobj.typecode)
+ sub0 = pyobj0.Actor
+ self.failUnless(sub0.get_attribute_attr1() == attr1, 'bad attribute1')
+ self.failUnless(sub0.get_attribute_attr2() == attr2, 'bad attribute2')
+
+ # [ 1489090 ] Derived type attributes don't populate the attr dictionary
+ #
+ pyobj.Actor = sub1 = GTD('urn:test', 'MiddleActor')(None).pyclass()
+ sub1.Element1 = 'foo'
+ sub1.Element2 = 'bar'
+ sub1.set_attribute_attr1(attr1)
+ sub1.set_attribute_attr2(attr2)
+
+ sw = SoapWriter()
+ sw.serialize(pyobj)
+ xml = str(sw)
+ ps = ParsedSoap(xml)
+ pyobj2 = ps.Parse(pyobj.typecode)
+ sub2 = pyobj2.Actor
+
+ self.failUnless(sub2.get_attribute_attr1() == attr1, 'bad attribute class')
+ self.failUnless(sub2.get_attribute_attr2() == attr2, 'bad attribute name')
+
+ # check parsed out correct type
+ self.failUnless(isinstance(sub2.typecode, sub1.typecode.__class__),
+ 'local element actor "%s" must be an instance of "%s"'%
+ (sub2.typecode, sub1.typecode.__class__))
+
+ # check local element is derived from base
+ base = GTD('urn:test', 'BaseActor')
+ self.failUnless(isinstance(sub2.typecode, base),
+ 'local element actor must be a derived type of "%s"'%
+ base)
+
+
+MSG1 = """<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="urn:test"><ns1:test><actor attr1="myclass" attr2="whatever" xsi:type="ns1:MiddleActor"><element1 xsi:type="xsd:string">foo</element1><element2 xsi:type="xsd:string">bar</element2></actor></ns1:test></SOAP-ENV:Body></SOAP-ENV:Envelope>"""
+
+NO_SUB_MSG = """<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="urn:test"><ns1:test><actor attr1="myclass" attr2="whatever" xsi:type="ns1:Bogus"><element1 xsi:type="xsd:string">foo</element1><element2 xsi:type="xsd:string">bar</element2></actor></ns1:test></SOAP-ENV:Body></SOAP-ENV:Envelope>"""
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/test/wsdl2py/test_DerivedTypes.xsd b/test/wsdl2py/test_DerivedTypes.xsd
new file mode 100644
index 0000000..02fee19
--- /dev/null
+++ b/test/wsdl2py/test_DerivedTypes.xsd
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="urn:test"
+ xmlns="urn:test"
+ xmlns:typens="urn:test">
+
+ <!-- base actor type with no special options -->
+ <xs:complexType name="BaseActor">
+ <xs:sequence>
+ <xs:element name="element1" type="xs:string"/>
+ </xs:sequence>
+ <xs:attribute name="attr1" type="xs:string"/>
+ </xs:complexType>
+
+ <xs:complexType name="MiddleActor">
+ <xs:complexContent>
+ <xs:extension base="typens:BaseActor">
+ <xs:sequence>
+ <xs:element name="element2" type="xs:string"/>
+ </xs:sequence>
+ <xs:attribute name="attr2" type="xs:string"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <xs:complexType name="TopActor">
+ <xs:complexContent>
+ <xs:extension base="typens:MiddleActor">
+ <xs:sequence>
+ <xs:element name="element3" type="xs:string"/>
+ </xs:sequence>
+ <xs:attribute name="attr3" type="xs:string"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <xs:complexType name="test">
+ <xs:sequence>
+ <xs:element name="actor" type="typens:BaseActor"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="test2">
+ <xs:sequence>
+ <xs:element name="actor" type="typens:BaseActor" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:element name="test" type="typens:test"/>
+ <xs:element name="test2" type="typens:test2"/>
+ <xs:element name="TopActor" type="typens:TopActor"/>
+ <xs:element name="MiddleActor" type="typens:MiddleActor"/>
+ <xs:element name="BaseActor" type="typens:BaseActor"/>
+
+</xs:schema>
diff --git a/test/wsdl2py/test_Echo.py b/test/wsdl2py/test_Echo.py
new file mode 100644
index 0000000..9573aa0
--- /dev/null
+++ b/test/wsdl2py/test_Echo.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import os, sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import FaultException
+"""
+Unittest
+
+WSDL: ../../samples/Echo/Echo.wsdl
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(EchoTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(EchoTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(EchoTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(EchoTestCase, 'test_'))
+ return suite
+
+
+class EchoTestCase(ServiceTestCase):
+ name = "test_Echo"
+ client_file_name = "EchoServer_services.py"
+ types_file_name = "EchoServer_services_types.py"
+ server_file_name = "EchoServer_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_local_Echo(self):
+ msg = self.client_module.EchoRequest()
+ rsp = self.client_module.EchoResponse()
+
+ def test_dispatch_Echo(self):
+ loc = self.client_module.EchoServerLocator()
+ port = loc.getEchoServer(**self.getPortKWArgs())
+
+ msg = self.client_module.EchoRequest()
+ msg.EchoIn = 'bla bla bla'
+ rsp = port.Echo(msg)
+ self.failUnless(rsp.EchoResult == msg.EchoIn, "Bad Echo")
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/test/wsdl2py/test_InfoBil.py b/test/wsdl2py/test_InfoBil.py
new file mode 100644
index 0000000..181736c
--- /dev/null
+++ b/test/wsdl2py/test_InfoBil.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import os, sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import FaultException
+"""
+Unittest
+
+WSDL: http://javatest2.infodata.se/webservices/services/Infobil?wsdl
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(TestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(TestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(TestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(TestCase, 'test_'))
+ return suite
+
+
+class TestCase(ServiceTestCase):
+ name = "test_InfoBil"
+ client_file_name = "InfoBilServiceService_services.py"
+ types_file_name = "InfoBilServiceService_services_types.py"
+ server_file_name = "InfoBilServiceService_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_local_import(self):
+ pass
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/test/wsdl2py/test_MapPoint.py b/test/wsdl2py/test_MapPoint.py
new file mode 100644
index 0000000..1f1ee79
--- /dev/null
+++ b/test/wsdl2py/test_MapPoint.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+"""
+Unittest for contacting the Map Point Service.
+
+WSDL:
+"""
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(MapPointTest, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(MapPointTest, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(MapPointTest, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(MapPointTest, 'test_'))
+ return suite
+
+
+
+class MapPointTest(ServiceTestCase):
+ """Test case for OPCService Web service
+
+ """
+ name = "test_MapPoint"
+ client_file_name = "CommonService_services.py"
+ types_file_name = "CommonService_services_types.py"
+ server_file_name = "CommonService_services_server.py"
+
+ def test_net_GetVersionInfo(self):
+ """expect this to fail cause i'm not doing http authentication.
+ """
+ loc = self.client_module.CommonServiceLocator()
+ port = loc.getCommonServiceSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.GetVersionInfoSoapIn()
+ try:
+ rsp = port.GetVersionInfo(msg)
+ except RuntimeError:
+ # RuntimeError: HTTP Digest Authorization Failed
+ pass
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/test/wsdl2py/test_OpcDaGateway.py b/test/wsdl2py/test_OpcDaGateway.py
new file mode 100644
index 0000000..ac9b883
--- /dev/null
+++ b/test/wsdl2py/test_OpcDaGateway.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+import ZSI
+from ZSI import FaultException
+"""
+Unittest for contacting the OPC XML-DA Service.
+
+WSDL: http://tswinc.us/XMLDADemo/ts_sim/OpcDaGateway.asmx?WSDL
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(OPCServiceTest, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(OPCServiceTest, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(OPCServiceTest, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(OPCServiceTest, 'test_'))
+ return suite
+
+
+class OPCServiceTest(ServiceTestCase):
+ """Test case for OPCService Web service
+
+ def GetProperties(self, request):
+ def Subscribe(self, request):
+ def SubscriptionPolledRefresh(self, request):
+ def SubscriptionCancel(self, request):
+ def GetStatus(self, request):
+ def Browse(self, request):
+ def Read(self, request):
+ def Write(self, request):
+ """
+ name = "test_OpcDaGateway"
+ client_file_name = "OpcXmlDaSrv_services.py"
+ types_file_name = "OpcXmlDaSrv_services_types.py"
+ server_file_name = "OpcXmlDaSrv_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_local_anyType(self):
+ """serialize an int via anyType, then parse it back.
+ """
+ import time
+ pyobj = self.client_module.ReadSoapOut()
+ pyobj.RItemList = pyobj.new_RItemList()
+ item = pyobj.RItemList.new_Items()
+ pyobj.RItemList.Items = [item,]
+ item.typecode.ofwhat[1].processContents = 'lax'
+ item.Value = 123
+ s = str(ZSI.SoapWriter().serialize(pyobj))
+
+ ps = ZSI.ParsedSoap(s)
+ pyobj = ps.Parse(pyobj.typecode)
+ for item in pyobj.RItemList.Items:
+ item.Value
+
+ def test_net_Browse(self):
+ """FaultException: The item path is not known to the server.
+ """
+ loc = self.client_module.OpcXmlDaSrvLocator()
+ port = loc.getOpcXmlDaSrvSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.BrowseSoapIn()
+ msg._PropertyNames=['Static']
+ msg._attrs = {'ItemPath':'Static'}
+
+ self.failUnless(\
+ getattr(msg.typecode, 'attribute_typecode_dict', None) is not None,
+ )
+
+ self.failUnlessRaises(FaultException, port.Browse, msg)
+
+
+# def hangs_test_net_Read(self):
+# """FaultException: The item path is not known to the server.
+# """
+# msg = self.getInputMessageInstance('Read')
+#
+# #msg = ReadSoapIn()
+# op = msg.new_Options()
+# msg.Options = op
+# op.set_attribute_ReturnItemTime(True)
+# op.set_attribute_ReturnItemName(True)
+# op.set_attribute_ClientRequestHandle("")
+# op.set_attribute_LocaleID('en-us')
+#
+# item_list = msg.new_ItemList()
+# msg.ItemList = item_list
+# item_list.set_attribute_MaxAge(1000)
+#
+# item = item_list.new_Items()
+# item_list.Items = item
+# item.set_attribute_ItemPath("")
+# item.set_attribute_ItemName("Staic.Analog Types.Int")
+# item.set_attribute_ClientItemHandle("")
+#
+# self.failUnless(\
+# getattr(msg.typecode, 'attribute_typecode_dict', None) is not None,
+# )
+#
+# self._ports[0].Read(msg)
+#
+
+# def hangs_test_net_GetProperties(self):
+# """sending an empty GetProperties request,
+# receiving empty response.
+#
+# <GetPropertiesResult RcvTime="2005-12-16T13:23:55.8593750-05:00"
+# ReplyTime="2005-12-16T13:24:00.1093750-05:00"
+# RevisedLocaleID="en-us" ServerState="running" />
+# """
+# operationName = 'GetProperties'
+# msg = self.getInputMessageInstance(operationName)
+# msg._ItemIDs
+# msg._PropertyNames
+# response = self._ports[0].GetProperties(msg)
+# result = response._GetPropertiesResult
+#
+# # not sure these attributes are required but check for them.
+# self.failUnless(isinstance(getattr(result, '_attrs', None), dict))
+# for k in ['RcvTime','ReplyTime','RevisedLocaleID','ServerState']:
+# self.failUnless(result._attrs.has_key(k))
+#
+# self.failUnless(len(response._PropertyLists) == 0)
+# self.failUnless(len(response._Errors) == 0)
+
+
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/test/wsdl2py/test_Sabre.py b/test/wsdl2py/test_Sabre.py
new file mode 100644
index 0000000..8cc4b62
--- /dev/null
+++ b/test/wsdl2py/test_Sabre.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+from ZSI import FaultException
+"""
+Unittest for contacting
+
+WSDL: http://webservices.sabre.com/wsdl/sabreXML1.0.00/res/SessionCreateRQ.wsdl
+"""
+
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ServiceTest, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ServiceTest, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ServiceTest, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ServiceTest, 'test_'))
+ return suite
+
+
+class ServiceTest(ServiceTestCase):
+ """Test case for Sabre Web service
+
+ """
+ name = "test_Sabre"
+ client_file_name = "SessionCreateRQService_services.py"
+ types_file_name = "SessionCreateRQService_services_types.py"
+ server_file_name = "SessionCreateRQService_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_net_SessionCreate(self):
+ """
+_________________________________ Mon Jan 2 13:41:22 2006 REQUEST:
+<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="http://www.opentravel.org/OTA/2002/11"><ns1:SessionCreateRQ><ns1:POS><ns1:Source PseudoCityCode="SF"></ns1:Source></ns1:POS></ns1:SessionCreateRQ></SOAP-ENV:Body></SOAP-ENV:Envelope>
+_________________________________ Mon Jan 2 13:41:22 2006 RESPONSE:
+Server: Netscape-Enterprise/6.0
+Date: Mon, 02 Jan 2006 21:41:21 GMT
+Content-length: 1568
+Content-type: text/xml; charset="utf-8"
+Soapaction: ""
+
+<?xml version="1.0" encoding="UTF-8"?>
+<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"><soap-env:Header><wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext"/></soap-env:Header><soap-env:Body><soap-env:Fault><faultcode>soap-env:Client.ConversationIdRequired</faultcode><faultstring>Conversation id required</faultstring><detail><StackTrace>com.sabre.universalservices.base.session.SessionException: errors.session.USG_CONVERSATION_ID_REQUIRED
+ at com.sabre.universalservices.gateway.control.SecurityInterceptor.executeOnRequest(SecurityInterceptor.java:111)
+ at com.sabre.universalservices.base.interceptor.Interceptor.execute(Interceptor.java:113)
+ at com.sabre.universalservices.base.interceptor.InterceptorChain.applyInterceptors(InterceptorChain.java:32)
+ at com.sabre.universalservices.base.interceptor.InterceptorManager.process(InterceptorManager.java:116)
+ at com.sabre.universalservices.gateway.control.WSGateway.onMessage(WSGateway.java:297)
+ at com.sabre.universalservices.gateway.control.WSGateway.handleRequest(WSGateway.java:208)
+ at com.sabre.universalservices.gateway.control.WSGateway.doPost(WSGateway.java:156)
+ at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
+ at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
+ at com.iplanet.server.http.servlet.NSServletRunner.invokeServletService(NSServletRunner.java:919)
+ at com.iplanet.server.http.servlet.NSServletRunner.Service(NSServletRunner.java:483)
+</StackTrace></detail></soap-env:Fault></soap-env:Body></soap-env:Envelope>
+E
+ """
+ loc = self.client_module.SessionCreateRQServiceLocator()
+ port = loc.getSessionCreatePortType(**self.getPortKWArgs())
+
+ msg = self.client_module.GetSessionCreateInput()
+ msg.POS = msg.new_POS()
+ msg.POS.Source = msg.POS.new_Source()
+ msg.POS.Source.set_attribute_PseudoCityCode("SF")
+
+ self.failUnlessRaises(FaultException, port.SessionCreateRQ, msg)
+ #response = self._ports[0].SessionCreateRQ(msg)
+ #response.Success
+ #response.Warnings
+ #response.ConversationId
+ #response.Errors
+
+
+if __name__ == "__main__" :
+ main()
+
diff --git a/test/wsdl2py/test_TerraService.py b/test/wsdl2py/test_TerraService.py
new file mode 100755
index 0000000..b9b264a
--- /dev/null
+++ b/test/wsdl2py/test_TerraService.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python
+
+############################################################################
+# David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import ServiceTestCase, ServiceTestSuite
+
+import re
+from ZSI import EvaluateException
+"""
+Unittest for contacting the TerraService Web service.
+
+WSDL: http://terraservice.net/TerraService.asmx?WSDL
+"""
+
+CONFIG_FILE = 'config.txt'
+CONFIG_SECTION = 'complex_types'
+SERVICE_NAME = 'TerraService'
+PORT_NAME = 'TerraServiceSoap'
+EXCEPTION_STRING_SERIALIZE = r"Serializing ConvertPlaceToLonLatPt xmlns=\"http://terraserver-usa.com/terraserver/\"._place, Exception Serializing place xmlns=\"http://terraserver-usa.com/terraserver/\"._City, AttributeError 'int' object has no attribute \'replace\'"
+
+SERIALIZE_PATTERN = re.compile(EXCEPTION_STRING_SERIALIZE)
+
+class TerraServiceTest(ServiceTestCase):
+ """Test case for TerraService Web service
+ """
+ name = "test_TerraService"
+
+ def test_ConvertPlaceToLonLatPt(self):
+ operationName = 'ConvertPlaceToLonLatPt'
+ request = self.getInputMessageInstance(operationName)
+ request._place = self._moduleDict[self._typeModuleName].ns1.Place_Def()
+ request._place._City = 'Oak Harbor'
+ request._place._State = 'Washington'
+ request._place._Country = 'United States'
+ response = self.RPC(operationName, request)
+
+
+
+ def test_ConvertLonLatPtToNearestPlace(self):
+ operationName = 'ConvertLonLatPtToNearestPlace'
+ request = self.getInputMessageInstance(operationName)
+ request._place = self._moduleDict[self._typeModuleName].ns1.Place_Def()
+ request._point = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._point._Lon = -122.643
+ request._point._Lat = 48.297
+ response = self.RPC(operationName, request)
+
+ def test_ConvertLonLatPtToUtmPt(self):
+ operationName = 'ConvertLonLatPtToUtmPt'
+ request = self.getInputMessageInstance(operationName)
+ request._point = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._point._Lon = -122.643
+ request._point._Lat = 48.297
+ response = self.RPC(operationName, request)
+
+ def test_ConvertUtmPtToLonLatPt(self):
+ operationName = 'ConvertUtmPtToLonLatPt'
+ request = self.getInputMessageInstance(operationName)
+ request._utm = self._moduleDict[self._typeModuleName].ns1.UtmPt_Def()
+ request._utm._X = 526703.512403
+ request._utm._Y = 5348595.96493
+ request._utm._Zone = 10
+ response = self.RPC(operationName, request)
+
+ def test_CountPlacesInRect(self):
+ operationName = 'CountPlacesInRect'
+ request = self.getInputMessageInstance(operationName)
+ request._upperleft = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._upperleft._Lon = -122.647
+ request._upperleft._Lat = 48.293
+ request._lowerright = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._lowerright._Lon = request._upperleft._Lon + 1.0
+ request._lowerright._Lat = request._upperleft._Lon - 1.0
+ request._ptype = "HillMountain"
+ response = self.RPC(operationName, request)
+
+ def test_GetAreaFromPt(self):
+ operationName = 'GetAreaFromPt'
+ request = self.getInputMessageInstance(operationName)
+ request._center = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._center._Lon = -122.647
+ request._center._Lat = 48.293
+ request._theme = 'Topo'
+ request._scale = "Scale2m"
+ request._displayPixWidth = 2
+ request._displayPixHeight = 2
+ response = self.RPC(operationName, request)
+
+ def test_GetAreaFromRect(self):
+ operationName = 'GetAreaFromRect'
+ request = self.getInputMessageInstance(operationName)
+ request._upperLeft = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._upperLeft._Lon = -122.647
+ request._upperLeft._Lat = 48.293
+ request._lowerRight = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._lowerRight._Lon = request._upperLeft._Lon + 1.0
+ request._lowerRight._Lat = request._upperLeft._Lat - 1.0
+ request._theme = 'Topo'
+ request._scale = "Scale2m"
+ response = self.RPC(operationName, request)
+
+ def test_GetAreaFromTileId(self):
+ operationName = 'GetAreaFromTileId'
+ request = self.getInputMessageInstance(operationName)
+ id = self._moduleDict[self._typeModuleName].ns1.TileId_Def()
+ id._Theme = 'Topo'
+ id._Scale = "Scale2m"
+ id._Scene = 8
+ id._X = 20
+ id._y = 20
+ request._id = id
+ request._displayPixWidth = 2
+ request._displayPixHeight = 2
+ response = self.RPC(operationName, request)
+
+ def test_GetLatLonMetrics(self):
+ operationName = 'GetLatLonMetrics'
+ request = self.getInputMessageInstance(operationName)
+ request._point = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._point._Lon = -122.647
+ request._point._Lat = 48.293
+ response = self.RPC(operationName, request)
+
+ # derived type (enum) problem
+ # skipping it for now
+
+
+ # derived type (enum) problem
+ # also inconsistent timeout problem for this call
+
+
+ def test_GetPlaceListInRect(self):
+ operationName = 'GetPlaceListInRect'
+ request = self.getInputMessageInstance(operationName)
+ request._upperleft = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._upperleft._Lon = -123.0
+ request._upperleft._Lat = 44.0
+ request._lowerright = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ # needs to be small, otherwise different items
+ # returned each time
+ request._lowerright._Lon = -122.8
+ request._lowerright._Lat = 43.8
+ request._ptype = "HillMountain"
+ request._MaxItems = 3
+ response = self.RPC(operationName, request)
+
+ def test_GetTheme(self):
+ operationName = 'GetTheme'
+ request = self.getInputMessageInstance(operationName)
+ request._theme = 'Topo'
+ response = self.RPC(operationName, request)
+
+ def test_GetTile(self):
+ operationName = 'GetTile'
+ request = self.getInputMessageInstance(operationName)
+ request._id = self._moduleDict[self._typeModuleName].ns1.TileId_Def()
+ request._id._Theme = 'Topo'
+ request._id._Scale = 'Scale2m'
+ request._id._Scene = 8
+ request._id._X = 20
+ request._id._Y = 20
+ response = self.RPC(operationName, request)
+
+ def test_GetTileMetaFromLonLatPt(self):
+ operationName = 'GetTileMetaFromLonLatPt'
+ request = self.getInputMessageInstance(operationName)
+ request._theme = 'Topo'
+ request._point = self._moduleDict[self._typeModuleName].ns1.LonLatPt_Def()
+ request._point._Lon = -122.64
+ request._point._Lat = 48.29
+ request._scale = "Scale4m"
+ response = self.RPC(operationName, request)
+
+ def test_GetTileMetaFromTileId(self):
+ operationName = 'GetTileMetaFromTileId'
+ request = self.getInputMessageInstance(operationName)
+ request._id = self._moduleDict[self._typeModuleName].ns1.TileId_Def()
+ request._id._Theme = 'Topo'
+ request._id._Scale = 'Scale2m'
+ request._id._Scene = 8
+ request._id._X = 20
+ request._id._Y = 20
+ response = self.RPC(operationName, request)
+
+
+class TerraServiceTestFailures(ServiceTestCase):
+ name = "test_TerraService"
+
+ def test_ConvertPlaceToLonLatPt_x1(self):
+ """
+ This test should fail
+ """
+ operationName = 'ConvertPlaceToLonLatPt'
+ request = self.getInputMessageInstance(operationName)
+ request._place = self._moduleDict[self._typeModuleName].ns1.Place_Def()
+ request._place._City = 1
+ request._place._State = 'Washington'
+ request._place._Country = 'United States'
+
+ try:
+ response = self.RPC(operationName, request)
+
+ except Exception, msg:
+ exceptionString = str(msg)
+ if SERIALIZE_PATTERN.match(exceptionString):
+ pass
+ else:
+ raise
+
+ def test_GetPlaceFacts(self):
+ operationName = 'GetPlaceFacts'
+ request = self.getInputMessageInstance(operationName)
+ request._place = self._moduleDict[self._typeModuleName].ns1.Place_Def()
+ request._place._City = 'Seattle'
+ request._place._State = 'Washington'
+ request._place._Country = 'United States'
+ try:
+ response = self.RPC(operationName, request)
+ except EvaluateException, ex:
+ pass
+
+ def test_GetPlaceList(self):
+ operationName = 'GetPlaceList'
+ request = self.getInputMessageInstance(operationName)
+ request._placeName = 'New York'
+ request._MaxItems = 5
+ request._imagePresence = 0
+ try:
+ response = self.RPC(operationName, request)
+ except EvaluateException, ex:
+ pass
+
+def makeTestSuite():
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(TerraServiceTest, 'test_'))
+ suite.addTest(unittest.makeSuite(TerraServiceTestFailures, 'test_'))
+ return suite
+
+
+if __name__ == "__main__" :
+ unittest.TestProgram(defaultTest="makeTestSuite")
diff --git a/test/wsdl2py/test_ThreatService.py b/test/wsdl2py/test_ThreatService.py
new file mode 100755
index 0000000..2e00733
--- /dev/null
+++ b/test/wsdl2py/test_ThreatService.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+
+
+"""
+Unittest for contacting the threatService Web service.
+
+WSDL: http://www.boyzoid.com/threat.cfc?wsdl
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(HomelandTestCase, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(HomelandTestCase, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(HomelandTestCase, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(HomelandTestCase, 'test_'))
+ return suite
+
+
+class HomelandTestCase(ServiceTestCase):
+ """Test case for ZipCodeResolver Web service
+ """
+ name = "test_ThreatService"
+ client_file_name = "Current_Homeland_Security_Threat_Level_services.py"
+ types_file_name = "Current_Homeland_Security_Threat_Level_services_types.py"
+ server_file_name = None
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_net_threatLevel(self):
+ loc = self.client_module.Current_Homeland_Security_Threat_LevelLocator()
+ port = loc.getthreat(**self.getPortKWArgs())
+
+ msg = self.client_module.threatLevelRequest()
+ rsp = port.threatLevel(msg)
+ for item in rsp.ThreatLevelReturn.Item:
+ item.Key
+ item.Value
+
+
+
+if __name__ == "__main__" :
+ main()
diff --git a/test/wsdl2py/test_WhiteMesa.py b/test/wsdl2py/test_WhiteMesa.py
new file mode 100755
index 0000000..b74647e
--- /dev/null
+++ b/test/wsdl2py/test_WhiteMesa.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite, TestException
+
+"""
+Unittest for contacting the WhiteMesa web service for rpc/literal tests.
+
+WSDL: http://www.whitemesa.net/wsdl/test-rpc-lit.wsdl
+
+"""
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(WhiteMesaTest, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(WhiteMesaTest, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(WhiteMesaTest, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(WhiteMesaTest, 'test_'))
+ return suite
+
+
+class WhiteMesaTest(ServiceTestCase):
+ """Test case for ZipCodeResolver Web service
+ """
+ name = "test_WhiteMesa"
+ client_file_name = "RPC_Literal_TestDefinitions_services.py"
+ types_file_name = "RPC_Literal_TestDefinitions_services_types.py"
+ server_file_name = "RPC_Literal_TestDefinitions_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_local_EchoBoolean(self):
+ from ZSI.writer import SoapWriter
+ msg = self.client_module.echoBooleanRequest()
+ msg._inputBoolean = True
+ sw = SoapWriter()
+ sw.serialize(msg)
+
+ def test_net_EchoBoolean(self):
+ msg = self.client_module.echoBooleanRequest()
+ msg._inputBoolean = True
+
+ loc = self.client_module.WhiteMesaSoapRpcLitTestSvcLocator()
+ port = loc.getSoapTestPortTypeRpc(**self.getPortKWArgs())
+ rsp = port.echoBoolean(msg)
+
+ self.failUnless(msg._inputBoolean == rsp._return,
+ "EchoBoolean Failed")
+
+ def test_dispatch_EchoBoolean(self):
+ self.test_net_EchoBoolean()
+
+
+if __name__ == "__main__" :
+ main()
diff --git a/test/wsdl2py/test_XMethodsQuery.py b/test/wsdl2py/test_XMethodsQuery.py
new file mode 100755
index 0000000..d9b5d3c
--- /dev/null
+++ b/test/wsdl2py/test_XMethodsQuery.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+
+############################################################################
+# David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+
+"""
+Unittest for contacting the XMethodsQuery Web service.
+
+WSDL: http://www.xmethods.net/wsdl/query.wsdl
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(XMethodsQueryTest, 'test_dispatch', suiteClass=ServiceTestSuite))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(XMethodsQueryTest, 'test_local', suiteClass=ServiceTestSuite))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(XMethodsQueryTest, 'test_net', suiteClass=ServiceTestSuite))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(XMethodsQueryTest, 'test_', suiteClass=ServiceTestSuite))
+ return suite
+
+
+class XMethodsQueryTest(ServiceTestCase):
+ """Test case for XMethodsQuery Web service
+ """
+ name = "test_XMethodsQuery"
+ client_file_name = "XMethodsQuery_services.py"
+ types_file_name = "XMethodsQuery_services_types.py"
+ server_file_name = "XMethodsQuery_services_server.py"
+
+ def __init__(self, methodName):
+ ServiceTestCase.__init__(self, methodName)
+ self.wsdl2py_args.append('-b')
+
+ def test_net_getAllServiceNames(self):
+ loc = self.client_module.XMethodsQueryLocator()
+ port = loc.getXMethodsQuerySoapPortType(**self.getPortKWArgs())
+
+ msg = self.client_module.getAllServiceNames2SoapIn()
+ rsp = port.getAllServiceNames(msg)
+ return rsp
+
+ def test_net_getAllServiceSummaries(self):
+ loc = self.client_module.XMethodsQueryLocator()
+ port = loc.getXMethodsQuerySoapPortType(**self.getPortKWArgs())
+
+ msg = self.client_module.getAllServiceSummaries1SoapIn()
+ rsp = port.getAllServiceSummaries(msg)
+ return rsp
+
+ def test_net_getServiceDetail(self):
+ loc = self.client_module.XMethodsQueryLocator()
+ port = loc.getXMethodsQuerySoapPortType(**self.getPortKWArgs())
+
+ msg = self.client_module.getServiceDetail4SoapIn()
+ msg._id = 'uuid:A29C0D6C-5529-0D27-A91A-8E02D343532B'
+ rsp = port.getServiceDetail(msg)
+ return rsp
+
+ def test_net_getServiceNamesByPublisher(self):
+ loc = self.client_module.XMethodsQueryLocator()
+ port = loc.getXMethodsQuerySoapPortType(**self.getPortKWArgs())
+
+ msg = self.client_module.getServiceNamesByPublisher3SoapIn()
+ msg._publisherID = 'xmethods.net'
+ rsp = port.getServiceNamesByPublisher(msg)
+ return rsp
+
+ def test_net_getServiceSummariesByPublisher(self):
+ loc = self.client_module.XMethodsQueryLocator()
+ port = loc.getXMethodsQuerySoapPortType(**self.getPortKWArgs())
+
+ msg = self.client_module.getServiceSummariesByPublisher0SoapIn()
+ msg._publisherID = 'xmethods.net'
+ rsp = port.getServiceSummariesByPublisher(msg)
+ return rsp
+
+
+if __name__ == "__main__" :
+ main()
diff --git a/test/wsdl2py/test_ZipCodeResolver.py b/test/wsdl2py/test_ZipCodeResolver.py
new file mode 100755
index 0000000..2f6fd37
--- /dev/null
+++ b/test/wsdl2py/test_ZipCodeResolver.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+
+############################################################################
+# David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import sys, unittest
+from ServiceTest import main, ServiceTestCase, ServiceTestSuite
+
+"""
+Unittest for contacting the ZipCodeResolver Web service.
+
+WSDL: http://webservices.eraserver.net/zipcoderesolver/zipcoderesolver.asmx?WSDL
+
+"""
+
+# General targets
+def dispatch():
+ """Run all dispatch tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ZipCodeResolverTest, 'test_dispatch'))
+ return suite
+
+def local():
+ """Run all local tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ZipCodeResolverTest, 'test_local'))
+ return suite
+
+def net():
+ """Run all network tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ZipCodeResolverTest, 'test_net'))
+ return suite
+
+def all():
+ """Run all tests"""
+ suite = ServiceTestSuite()
+ suite.addTest(unittest.makeSuite(ZipCodeResolverTest, 'test_'))
+ return suite
+
+
+class ZipCodeResolverTest(ServiceTestCase):
+ """Test case for ZipCodeResolver Web service
+ """
+ name = "test_ZipCodeResolver"
+ client_file_name = "ZipCodeResolver_services.py"
+ types_file_name = "ZipCodeResolver_services_types.py"
+ #server_file_name = "ZipCodeResolver_services_server.py"
+
+ def test_net_CorrectedAddressHtml(self):
+ loc = self.client_module.ZipCodeResolverLocator()
+ port = loc.getZipCodeResolverSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.CorrectedAddressHtmlSoapIn()
+ msg._address = '636 Colusa Avenue'
+ msg._city = 'Berkeley'
+ msg._state = 'California'
+ rsp = port.CorrectedAddressHtml(msg)
+
+ def test_net_CorrectedAddressXml(self):
+ loc = self.client_module.ZipCodeResolverLocator()
+ port = loc.getZipCodeResolverSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.CorrectedAddressXmlSoapIn()
+ msg._address = '636 Colusa Avenue'
+ msg._city = 'Berkeley'
+ msg._state = 'California'
+ rsp = port.CorrectedAddressXml(msg)
+
+ def test_net_FullZipCode(self):
+ loc = self.client_module.ZipCodeResolverLocator()
+ port = loc.getZipCodeResolverSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.FullZipCodeSoapIn()
+ msg._address = '636 Colusa Avenue'
+ msg._city = 'Berkeley'
+ msg._state = 'California'
+ rsp = port.FullZipCode(msg)
+
+ def test_net_ShortZipCode(self):
+ loc = self.client_module.ZipCodeResolverLocator()
+ port = loc.getZipCodeResolverSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.ShortZipCodeSoapIn()
+ msg._address = '636 Colusa Avenue'
+ msg._city = 'Berkeley'
+ msg._state = 'California'
+ rsp = port.ShortZipCode(msg)
+
+ def test_net_VersionInfo(self):
+ loc = self.client_module.ZipCodeResolverLocator()
+ port = loc.getZipCodeResolverSoap(**self.getPortKWArgs())
+
+ msg = self.client_module.VersionInfoSoapIn()
+ rsp = port.VersionInfo(msg)
+
+
+if __name__ == "__main__" :
+ main()
+