diff -Nru python-soaplib-0.8.1/debian/changelog python-soaplib-0.9.3-alpha3/debian/changelog --- python-soaplib-0.8.1/debian/changelog 2010-01-26 22:19:48.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/debian/changelog 2010-09-15 14:30:22.000000000 +0000 @@ -1,3 +1,9 @@ +python-soaplib (0.9.3-alpha3-1ppa1) lucid; urgency=low + + * upgraded to latest stable version + + -- David Fraser Wed, 15 Sep 2010 16:30:00 +0200 + python-soaplib (0.8.1-2) unstable; urgency=low * debian/control: bump python-version to >= 2.5 (closes: #566858) diff -Nru python-soaplib-0.8.1/debian/docs python-soaplib-0.9.3-alpha3/debian/docs --- python-soaplib-0.8.1/debian/docs 2010-01-23 20:10:15.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/debian/docs 2010-09-15 14:33:37.000000000 +0000 @@ -1 +0,0 @@ -examples diff -Nru python-soaplib-0.8.1/examples/async.py python-soaplib-0.9.3-alpha3/examples/async.py --- python-soaplib-0.8.1/examples/async.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/examples/async.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -from soaplib.wsgi_soap import SimpleWSGISoapApp -from soaplib.service import soapmethod -from soaplib.serializers.primitive import String, Integer, Array -from soaplib.serializers.binary import Attachment -from soaplib.util import get_callback_info - -from threading import Thread -from tempfile import mkstemp -import time - -''' -This is a very simple async service that sleeps for a specified -number of seconds and then call back the caller with a message. -This kicks off a new Thread for each request, which is not recommended -for a real-world application. Soaplib does not provide any thread -management or scheduling mechanism, the service is responsible for the -execution of the async. process. -''' - -class SleepingService(SimpleWSGISoapApp): - - @soapmethod(Integer,_isAsync=True) - def sleep(self,seconds): - msgid, replyto = get_callback_info() - - def run(): - time.sleep(seconds) - - client = create_service_client(replyto, self) - client.woke_up('good morning',msgid=msgid) - - Thread(target=run).start() - - @soapmethod(String,_isCallback=True) - def woke_up(self,message): - pass - -if __name__=='__main__': - try: - from wsgiref.simple_server import make_server - server = make_server('localhost', 7789, SleepingService()) - server.serve_forever() - except ImportError: - print "Error: example server code requires Python >= 2.5" \ No newline at end of file diff -Nru python-soaplib-0.8.1/examples/binary.py python-soaplib-0.9.3-alpha3/examples/binary.py --- python-soaplib-0.8.1/examples/binary.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/examples/binary.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -from soaplib.wsgi_soap import SimpleWSGISoapApp -from soaplib.service import soapmethod -from soaplib.serializers.primitive import String, Integer, Array -from soaplib.serializers.binary import Attachment - - -from tempfile import mkstemp -import os - -class DocumentArchiver(SimpleWSGISoapApp): - - @soapmethod(Attachment,_returns=String) - def archive_document(self,document): - ''' - This method accepts an Attachment object, and returns the filename of the - archived file - ''' - fd,fname = mkstemp() - os.close(fd) - - document.fileName = fname - document.save_to_file() - - return fname - - @soapmethod(String,_returns=Attachment) - def get_archived_document(self,file_path): - ''' - This method loads a document from the specified file path - and returns it. If the path isn't found, an exception is - raised. - ''' - if not os.path.exists(file_path): - raise Exception("File [%s] not found"%file_path) - - document = Attachment(fileName=file_path) - # the service automatically loads the data from the file. - # alternatively, The data could be manually loaded into memory - # and loaded into the Attachment like: - # document = Attachment(data=data_from_file) - return document - - -def make_client(): - from soaplib.client import make_service_client - client = make_service_client('http://localhost:7889/',DocumentArchiver()) - return client - -if __name__=='__main__': - try: - from wsgiref.simple_server import make_server - server = make_server('localhost', 7889, DocumentArchiver()) - server.serve_forever() - except ImportError: - print "Error: example server code requires Python >= 2.5" \ No newline at end of file diff -Nru python-soaplib-0.8.1/examples/classserializer.py python-soaplib-0.9.3-alpha3/examples/classserializer.py --- python-soaplib-0.8.1/examples/classserializer.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/examples/classserializer.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -from soaplib.wsgi_soap import SimpleWSGISoapApp -from soaplib.service import soapmethod -from soaplib.serializers.primitive import String, Integer, Array -from soaplib.serializers.clazz import ClassSerializer - -''' -This example shows how to define and use complex structures -in soaplib. This example uses an extremely simple in-memory -dictionary to store the User objects. -''' - -user_database = {} -userid_seq = 1 - -from soaplib.serializers.primitive import String, Integer, Array -from soaplib.serializers.clazz import ClassSerializer -class Permission(ClassSerializer): - class types: - application = String - feature = String - -class User(ClassSerializer): - class types: - userid = Integer - username = String - firstname = String - lastname = String - permissions = Array(Permission) - -class UserManager(SimpleWSGISoapApp): - - @soapmethod(User,_returns=Integer) - def add_user(self,user): - global user_database - global userid_seq - user.userid = userid_seq - userid_seq = userid_seq+1 - user_database[user.userid] = user - return user.userid - - @soapmethod(Integer,_returns=User) - def get_user(self,userid): - global user_database - return user_database[userid] - - @soapmethod(User) - def modify_user(self,user): - global user_database - user_database[user.userid] = user - - @soapmethod(Integer) - def delete_user(self,userid): - global user_database - del user_database[userid] - - @soapmethod(_returns=Array(User)) - def list_users(self): - global user_database - return [v for k,v in user_database.items()] - -if __name__=='__main__': - try: - from wsgiref.simple_server import make_server - server = make_server('localhost', 7789, UserManager()) - server.serve_forever() - except ImportError: - print "Error: example server code requires Python >= 2.5" diff -Nru python-soaplib-0.8.1/examples/client_helloworld_attach.py python-soaplib-0.9.3-alpha3/examples/client_helloworld_attach.py --- python-soaplib-0.8.1/examples/client_helloworld_attach.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/examples/client_helloworld_attach.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -#!/usr/bin/env python - -from soaplib.client import make_service_client -from soaplib.serializers.binary import Attachment -from helloworld_attach import HelloWorldService -from soaplib.client import debug -debug(True) - -client = make_service_client('http://localhost:7789/',HelloWorldService()) -print client.say_hello(Attachment(data="Dave"),5,mtom=True) diff -Nru python-soaplib-0.8.1/examples/helloworld_attach.py python-soaplib-0.9.3-alpha3/examples/helloworld_attach.py --- python-soaplib-0.8.1/examples/helloworld_attach.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/examples/helloworld_attach.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -from soaplib.wsgi_soap import SimpleWSGISoapApp -from soaplib.service import soapmethod -from soaplib.serializers.primitive import String, Integer, Array -from soaplib.serializers.binary import Attachment - -class HelloWorldService(SimpleWSGISoapApp): - - @soapmethod(Attachment,Integer,_returns=Array(String), _mtom=True) - def say_hello(self,name,times): - results = [] - for i in range(0,times): - results.append('Hello, %s'%name.data) - return results - -if __name__=='__main__': - try: - from wsgiref.simple_server import make_server - server = make_server('localhost', 7789, HelloWorldService()) - server.serve_forever() - except ImportError: - print "Error: example server code requires Python >= 2.5" diff -Nru python-soaplib-0.8.1/examples/helloworld.py python-soaplib-0.9.3-alpha3/examples/helloworld.py --- python-soaplib-0.8.1/examples/helloworld.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/examples/helloworld.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -from soaplib.wsgi_soap import SimpleWSGISoapApp -from soaplib.service import soapmethod -from soaplib.serializers.primitive import String, Integer, Array - -''' -This is a simple HelloWorld example to show the basics of writing -a webservice using soaplib, starting a server, and creating a service -client. -''' - -class HelloWorldService(SimpleWSGISoapApp): - - @soapmethod(String,Integer,_returns=Array(String)) - def say_hello(self,name,times): - ''' - Docstrings for service methods appear as documentation in the wsdl - what fun - @param name the name to say hello to - @param the number of times to say hello - @return the completed array - ''' - results = [] - for i in range(0,times): - results.append('Hello, %s'%name) - return results - -def make_client(): - from soaplib.client import make_service_client - client = make_service_client('http://localhost:7889/',HelloWorldService()) - return client - -if __name__=='__main__': - try: - from wsgiref.simple_server import make_server - server = make_server('localhost', 7889, HelloWorldService()) - server.serve_forever() - except ImportError: - print "Error: example server code requires Python >= 2.5" diff -Nru python-soaplib-0.8.1/examples/hooks.py python-soaplib-0.9.3-alpha3/examples/hooks.py --- python-soaplib-0.8.1/examples/hooks.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/examples/hooks.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -from soaplib.wsgi_soap import SimpleWSGISoapApp -from soaplib.service import soapmethod -from soaplib.serializers.primitive import String, Integer, Array -from soaplib.wsgi_soap import request - -from time import time - -''' -This example is an enhanced version of the HelloWorld example that -uses service 'hooks' to apply cross-cutting behavior to the service. -In this example, the service hooks are used to gather performance -information on both the method execution as well as the duration -of the entire call, including serialization and deserialization. The -available hooks are: - - * onCall - * onWsdl - * onWsdlException - * onMethodExec - * onResults - * onException - * onReturn - -These method can be used to easily apply cross-cutting functionality -accross all methods in the service to do things like database transaction -management, logging and measuring performance. This example also -employs the threadlocal request (soaplib.wsgi_soap.request) object -to hold the data points for this request. -''' - -class HelloWorldService(SimpleWSGISoapApp): - - @soapmethod(String,Integer,_returns=Array(String)) - def say_hello(self,name,times): - results = [] - raise Exception("this is some crazy crap") - for i in range(0,times): - results.append('Hello, %s'%name) - return results - - def onCall(self,environ): - request.additional['call_start'] = time() - - def onMethodExec(self,environ,body,py_params,soap_params): - request.additional['method_start'] = time() - - def onResults(self,environ,py_results,soap_results): - request.additional['method_end'] = time() - - def onReturn(self,environ,returnString): - call_start = request.additional['call_start'] - call_end = time() - method_start = request.additional['method_start'] - method_end = request.additional['method_end'] - - print 'Method took [%s] - total execution time[%s]'%(method_end-method_start,call_end-call_start) - - -def make_client(): - from soaplib.client import make_service_client - client = make_service_client('http://localhost:7889/',HelloWorldService()) - return client - -if __name__=='__main__': - try: - from wsgiref.simple_server import make_server - server = make_server('localhost', 7889, HelloWorldService()) - server.serve_forever() - except ImportError: - print "Error: example server code requires Python >= 2.5" \ No newline at end of file diff -Nru python-soaplib-0.8.1/examples/override.py python-soaplib-0.9.3-alpha3/examples/override.py --- python-soaplib-0.8.1/examples/override.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/examples/override.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -from soaplib.wsgi_soap import SimpleWSGISoapApp -from soaplib.service import soapmethod -from soaplib.serializers.primitive import String, Integer, Array - -''' -This example shows how to override the variable names for fun and profit. -This is very useful for situations that require the use of variable names -that are python keywords like, from, to, import, return, etc. -''' - -class EmailManager(SimpleWSGISoapApp): - - @soapmethod(String,String,String, - _inVariableNames={'_to':'to','_from':'from','_message':'message'}, - _outVariableName='return') - def sendEmail(self,_to,_from,message): - # do email sending here - return 'sent!' - -if __name__=='__main__': - try: - from wsgiref.simple_server import make_server - server = make_server('localhost', 7789, EmailManager()) - server.serve_forever() - except ImportError: - print "Error: example server code requires Python >= 2.5" diff -Nru python-soaplib-0.8.1/.gitignore python-soaplib-0.9.3-alpha3/.gitignore --- python-soaplib-0.8.1/.gitignore 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -# All files to globally ignore -*.pyc -*.pyo -*$py.class -*.swp -*.patch -*# -*~ -MANIFEST -dist -build -soaplib_lxml.egg-info diff -Nru python-soaplib-0.8.1/LICENSE python-soaplib-0.9.3-alpha3/LICENSE --- python-soaplib-0.8.1/LICENSE 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,458 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS \ No newline at end of file diff -Nru python-soaplib-0.8.1/PKG-INFO python-soaplib-0.9.3-alpha3/PKG-INFO --- python-soaplib-0.8.1/PKG-INFO 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/PKG-INFO 2010-08-25 19:27:50.000000000 +0000 @@ -0,0 +1,25 @@ +Metadata-Version: 1.0 +Name: soaplib +Version: 0.9.3-alpha3 +Summary: A simple library for writing soap web services +Home-page: http://github.com/arskom/soaplib +Author: Burak Arslan +Author-email: burak-soaplib@arskom.com.tr +License: LGPL +Description: This is a simple, easily extendible soap library that provides several useful + tools for creating and publishing soap web services in python. This package + features on-demand wsdl generation for the published services, a + wsgi-compliant web application, support for complex class structures, binary + attachments, simple framework for creating additional serialization mechanisms + and a client library. + + This prokect now uses lxml as it's XML API, providing full namespace support. + +Keywords: soap,wsdl,wsgi +Platform: UNKNOWN +Classifier: Programming Language :: Python +Classifier: Operating System :: OS Independent +Classifier: Natural Language :: English +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content diff -Nru python-soaplib-0.8.1/setup.cfg python-soaplib-0.9.3-alpha3/setup.cfg --- python-soaplib-0.8.1/setup.cfg 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/setup.cfg 2010-08-25 19:27:50.000000000 +0000 @@ -1,3 +1,5 @@ [egg_info] -tag_build = dev -tag_svn_revision = true +tag_build = -alpha3 +tag_date = 0 +tag_svn_revision = 0 + diff -Nru python-soaplib-0.8.1/setup.py python-soaplib-0.9.3-alpha3/setup.py --- python-soaplib-0.8.1/setup.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/setup.py 2010-08-24 09:43:26.000000000 +0000 @@ -1,9 +1,8 @@ #!/usr/bin/env python from setuptools import setup, find_packages -import sys -VERSION = '0.8.1' +VERSION = '0.9.3' LONG_DESC = """\ This is a simple, easily extendible soap library that provides several useful tools for creating and publishing soap web services in python. This package @@ -15,32 +14,33 @@ This prokect now uses lxml as it's XML API, providing full namespace support. """ -setup(name='soaplib', - version=VERSION, - description="A simple library for writing soap web services", - long_description=LONG_DESC, - classifiers=[ - 'Programming Language :: Python', - 'Operating System :: OS Independent', - 'Natural Language :: English', - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - ], - keywords='soap', - author='Aaron Bickell', - author_email='abickell@optio.com', - maintainer = 'Jamie Kirkpatrick', - maintainer_email = 'jkp@kirkconsulting.co.uk', - url='http://wiki.github.com/jkp/soaplib-lxml', - license='LGPL', - packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), - zip_safe=False, - install_requires=[ - 'pytz', - 'lxml>=2.2.1', - ], - test_suite='tests.test_suite', - entry_points=""" - """, - ) +setup( + name='soaplib', + packages=find_packages('src'), + package_dir={'':'src'}, + + version=VERSION, + description="A simple library for writing soap web services", + long_description=LONG_DESC, + classifiers=[ + 'Programming Language :: Python', + 'Operating System :: OS Independent', + 'Natural Language :: English', + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + ], + keywords=('soap', 'wsdl', 'wsgi'), + author='Soaplib Contributors', + author_email='soap@python.org', + maintainer = 'Burak Arslan', + maintainer_email = 'burak-soaplib@arskom.com.tr', + url='http://github.com/arskom/soaplib', + license='LGPL', + zip_safe=False, + install_requires=[ + 'pytz', + 'lxml>=2.2.1', + ], + test_suite='tests.test_suite', +) diff -Nru python-soaplib-0.8.1/soaplib/client.py python-soaplib-0.9.3-alpha3/soaplib/client.py --- python-soaplib-0.8.1/soaplib/client.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,200 +0,0 @@ -from soaplib.etimport import ElementTree - -import httplib -from soaplib.soap import from_soap, make_soap_envelope, collapse_swa, apply_mtom -from soaplib.util import split_url, create_relates_to_header -from soaplib.serializers.primitive import Fault -from cStringIO import StringIO - -import sys - -# This sets the HTTP version string sent to the server to 1.0 -# preventing the response from bein 'chunked'. This is done -# because of a know bug in python (#900744). Rather than apply -# the patch to all the installed systems, it is simpler to set this -# version string, to be later removed in python 2.5 -# -httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0' - -_debug = False -_out = sys.stdout - -def debug(is_on,out=sys.stdout): - ''' - This is a utility method for debugging client request and responses - @param boolean for turning debug on or off - @param filelike object to write to - ''' - global _out, _debug - _out = out - _debug = is_on - -def dump(host,path,headers,envelope): - ''' - Debugging method for dumping request information to a file or stdout - @param host server host - @param path server path - @param headers http headers - @param envelope soap envelope - ''' - global _out, _debug - if not _debug: return - - def writeln(text): - _out.write(text) - _out.write('\r\n') - _out.flush() - - writeln('-------------------------------------------------') - writeln('Host: '+host) - writeln('Path: '+path) - writeln('Headers:') - for k,v in headers.items(): - writeln(' %s -> %s'%(k.ljust(20),v)) - writeln('Envelope:') - writeln(envelope) - writeln('-------------------------------------------------') - -_err_format = ''' -Parameter Do Not Match: -+ Arguments Passed In: -%s -+ Parameters Required: -%s''' - -class SimpleSoapClient(object): - ''' - SimpleSoapClient is the lowest level http soap client in soaplib, - and represents a single remote soap method. This class can be - used by itself by passing it the url information and MethodDescriptor, - but is most frequently used by the ServiceClient object. - ''' - - def __init__(self,host,path,descriptor,scheme="http"): - ''' - @param remote host - @param remote path - @param MethodDescriptor of the remote method being called - @param remote scheme - ''' - self.host = host - self.path = path - self.descriptor = descriptor - self.scheme = scheme - - def __call__(self,*args,**kwargs): - ''' - This method executes the http request to the remote web service. With - the exception of 'headers', 'msgid', and 'mtom'; all keyword arguments - to this method are put in the http header. The 'headers' keyword is to - denote a list of elements to be included in the soap header, 'msgid' - is a convenience keyword used in async web services which creates a - WS-Addressing messageid header to be included in the soap headers, and - 'mtom' enables the Message Transmission Optimization Mechanism. - - @param the arguments to the remote method - @param the keyword arguments - ''' - if len(args) != len(self.descriptor.inMessage.params): - argstring = '\r\n'.join([' '+str(arg) for arg in args]) - paramstring = '\r\n'.join([' '+str(p[0]) for p in self.descriptor.inMessage.params]) - err_msg = _err_format%(argstring,paramstring) - raise Exception(err_msg) - - msg = self.descriptor.inMessage.to_xml(*args) - - # grab the soap headers passed into this call - headers = kwargs.get('headers',[]) - mtom = kwargs.get('mtom',False) - msgid = kwargs.get('msgid') - if msgid: - # special case for the msgid field as a convenience - # when dealing with async callback methods - headers.append(create_relates_to_header(msgid)) - - tns = self.descriptor.inMessage.ns - envelope = make_soap_envelope(msg, tns, header_elements=headers) - - body = ElementTree.tostring(envelope) - methodName = '\"%s\"'%self.descriptor.soapAction - httpHeaders = {"Content-Length":len(body), - "Content-type":'text/xml; charset="UTF-8"', - "Accept":"application/soap+xml, application/dime, multipart/related, text/*", - 'User-Agent':'Soaplib/1.0', - 'SOAPAction':methodName - } - - for k,v in kwargs.items(): - # add all the other keywords to the http headers - if k not in ('headers','msgid','mtom'): - httpHeaders[k]=v - - if mtom: - httpHeaders, body = apply_mtom( httpHeaders, body, - self.descriptor.inMessage.params, - args ) - - dump(self.host,self.path,httpHeaders,body) - - if self.scheme == "http": - conn = httplib.HTTPConnection(self.host) - elif self.scheme == "https": - conn = httplib.HTTPSConnection(self.host) - else: - raise RuntimeError("Unsupported URI connection scheme: %s" % scheme) - - conn.request("POST",self.path,body=body,headers=httpHeaders) - response = conn.getresponse() - data = response.read() - - dump(self.host,self.path,dict(response.getheaders()),data) - - contenttype = response.getheader('Content-Type') - data = collapse_swa(contenttype, data) - - conn.close() - if str(response.status) not in['200','202']: - # consider everything NOT 200 or 202 as an error response - - if str(response.status) == '500': - fault = None - try: - payload, headers = from_soap(data) - fault = Fault.from_xml(payload) - except: - trace = StringIO() - import traceback - traceback.print_exc(file=trace) - - fault = Exception('Unable to read response \n %s %s \n %s \n %s'%(response.status,response.reason,trace.getvalue(),data)) - raise fault - else: - raise Exception('%s %s'%(response.status,response.reason)) - - if not self.descriptor.outMessage.params: - return - - payload, headers = from_soap(data) - results = self.descriptor.outMessage.from_xml(payload) - return results[0] - -class ServiceClient(object): - ''' - This class is a simple, convenient class for calling remote web services. - @param host the host of the SOAP service being called - @param path the path to the web service - @param impl the SimpleWSGISoapApp which defines the remote service - @param mtom whether or not to send attachments using MTOM - ''' - - def __init__(self,host,path,server_impl, scheme="http"): - if host.startswith("http://"): - host = host[6:] - - self.server = server_impl - for method in self.server.methods(): - setattr(self,method.name,SimpleSoapClient(host,path,method,scheme)) - -def make_service_client(url,impl): - scheme,host,path = split_url(url) - return ServiceClient(host,path,impl, scheme) diff -Nru python-soaplib-0.8.1/soaplib/easy_client.py python-soaplib-0.9.3-alpha3/soaplib/easy_client.py --- python-soaplib-0.8.1/soaplib/easy_client.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/easy_client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ -from soaplib.serializers.clazz import ClassSerializer -from soaplib.serializers.primitive import * -from soaplib.soap import * -from soaplib.util import split_url -from soaplib.etimport import ElementTree as et - -import new, datetime, httplib - -_builtin = { - int : Integer, - str : String, - datetime.datetime : DateTime, - float : Float, - bool : Boolean, -} - -class caller(object): - def __init__(self,host,namespace,method): - self.method = method - self.host = host - self.namespace = namespace - - def __call__(self,data,raw=False): - e = data.to_xml(data,data.__class__.__name__) - envelope = make_soap_envelope(e) - body = et.tostring(envelope) - methodName = '\"%s\"'%self.method - httpHeaders = {"Content-Length":len(body), - "Content-type":'text/xml; charset="UTF-8"', - "Accept":"application/soap+xml, application/dime, multipart/related, text/*", - 'User-Agent':'Soaplib/1.0', - 'SOAPAction':methodName - } - scheme,host,path = split_url(self.host) - if scheme == "http": - conn = httplib.HTTPConnection(host) - elif scheme == "https": - conn = httplib.HTTPSConnection(host) - else: - raise RuntimeError("Unsupported URI connection scheme: %s" % scheme) - - conn.request("POST",path,body=body,headers=httpHeaders) - response = conn.getresponse() - raw_data = response.read() - - retval = et.fromstring(raw_data) - d = retval.find('{http://schemas.xmlsoap.org/soap/envelope/}Body').getchildren()[0] - - if raw: - return d - return objectify(d) - -class DocumentClient(object): - - def __init__(self,host,methods,namespace=None): - for method in methods: - setattr(self,method,caller(host,namespace,method)) - -def get_serializer(value): - _type = type(value) - if value is None: - return Null - elif hasattr(value,'to_xml') and hasattr(value,'from_xml'): - # this object can act as it's own serializer - return value - elif _builtin.has_key(_type): - # found primitive: string, int, etc. - return _builtin[_type] - elif _type == list: - # must assume that all the elements in the list are of the same type - # and use the same serializer - if not len(value): - return Null - else: - return Array(get_serializer(value[0])) - else: - raise Exception("Could not find serializer for [%s]"%value) - -def denamespace(tag): - if tag.find('}') > -1: - tag = tag.replace("{","") - return tag.split('}') - return None, tag - -def objectify(element): - class ElementWrapper(object): - def __init__(self,element): - self.__element = element - self.__tags = {} - for child in element.getchildren(): - ns,tag = denamespace(child.tag) - if self.__tags.has_key(tag): - self.__tags[tag].append(child) - else: - self.__tags[tag] = child - - if hasattr(self,tag): - spot = getattr(self,tag) - if type(spot) != list: - spot = [spot] - #spot.append(objectify(child)) - if len(child.getchildren()): - spot.append(new.classobj(tag,(ElementWrapper,),{})(child)) - else: - spot.append(child.text) - setattr(self,tag,spot) - setattr(self,'__islist__',True) - elif len(child.getchildren()): - setattr(self,tag,new.classobj(tag,(ElementWrapper,),{})(child)) - else: - setattr(self,denamespace(child.tag)[1],child.text) # marshall the type here! - - def __getitem__(self,index): - if len(self.__tags.items()) == 1: - # this *could* be an array - k = self.__tags.keys()[0] - return getattr(self,k)[index] - if index == 0: - return self - raise StopIteration - - ns,tag = denamespace(element.tag) - return new.classobj(tag,(ElementWrapper,),{})(element) - - -def make(__type_name,**kwargs): - serializer = new.classobj(__type_name,(ClassSerializer,),{}) - namespace = None - - setattr(serializer,'soap_members',{}) - setattr(serializer,'namespace',namespace) - for k,v in kwargs.items(): - serializer.soap_members[k] = get_serializer(v) - o = serializer() - for k,v in kwargs.items(): - setattr(o,k,v) - return o - -if __name__ == '__main__': - echoInteger = make('echoInteger',i=34) - print et.tostring(echoInteger.to_xml(echoInteger,'echoInteger')) - - c = DocumentClient('http://localhost:9753/',['echoInteger','echoSimpleClass','echoSimpleClassArray']) - print c.echoInteger(make('echoInteger',i=3)).retval - - print c.echoSimpleClass(make('echoSimpleClass',sc=make('SimpleClass',i=34,s='bobo'))).retval.s - - d = c.echoSimpleClassArray(make('echoSimpleClassArray', - sca=[ - make('SimpleClass',i=34,s='jasdf'), - make('SimpleClass',i=34,s='bobo'), - make('SimpleClass',i=34,s='poo'), - ] - )) - print '*'*80 - for sc in d.retval: - print sc.s - diff -Nru python-soaplib-0.8.1/soaplib/etimport.py python-soaplib-0.9.3-alpha3/soaplib/etimport.py --- python-soaplib-0.8.1/soaplib/etimport.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/etimport.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -# only import lxml in this fork -import lxml.etree as ElementTree \ No newline at end of file diff -Nru python-soaplib-0.8.1/soaplib/ext/comproxy.py python-soaplib-0.9.3-alpha3/soaplib/ext/comproxy.py --- python-soaplib-0.8.1/soaplib/ext/comproxy.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/ext/comproxy.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,230 +0,0 @@ -''' -COM Bridge for SOAP Services -============================ - -This soaplib extension provides a simple COM bridge for accessing SOAP web -services, especially soaplib web services, via a COM-compliant language. It -was developed primarily for use with standard Windows VBScript/VBA. - -To use the COM bridge to access a web service, you must follow the following -steps: - - 1. Register the COM bridge with Windows by executing this module from - the command-line: - - python soaplib\ext\comproxy.py - - You should see output stating that the object was registered. - - 2. Place service stubs for the web service onto the PYTHONPATH so that - soaplib can create a service client for that web service. This is - usually as easy as creating a soaplib web service object with empty - methods that describes the web service you want to access, along with - any special types. For example: - - class Title(ClassSerializer): - class types: - titleID = Integer - name = String - description = String - - class Person(ClassSerializer): - class types: - personID = Integer - firstName = String - lastName = String - birthdate = DateTime - titles = Array(Title) - - class PeopleService(SimpleWSGISoapApp): - - @soapmethod(Person, _returns=Integer) - def addPerson(self, person): pass - - @soapmethod(Integer, _returns=Person) - def getPerson(self, personID): pass - - 3. From your COM-compliant language, create an instance of the soaplib - client object, then tell it about your web service: - - Set client = CreateObject("SoapLib.ServiceClient") - - uri = "http://webservicehost:port/" - service_import_path = "services.people.PeopleService" - - client.SetServiceInfo uri, service_import_path - - 4. Once you have a client object instantiated, you can use it to create - instances of any complex types, and call remote methods: - - ' instantiate a person object and two title objects - Set person = client.CreateObject("services.people.Person") - Set titleOne = client.CreateObject("services.people.Title") - Set titleTwo = client.CreateObject("services.people.Title") - - ' set some attributes on the first title - titleOne.name = "Team Lead" - titleOne.description = "Development Team Leader" - - ' set some attribtues on the second title - titleTwo.name = "Smart Guy" - titleTwo.description = "All-Around Smart Guy" - - ' set some attributes on the person, including a date/time - ' and an Array of complex types - person.firstName = "Jonathan" - person.lastName = "LaCour" - person.birthdate = Now() - person.titles = Array(titleOne, titleTwo) - - ' call the web service to add this person to the database - personID = client.addPerson(person) - - ' fetch the person back again, using the ID - Set theperson = client.getPerson(personID) - - ' echo the results - WScript.Echo "Retrieved person: " & theperson.personID - WScript.Echo "First name: " & theperson.firstName - WScript.Echo "Last name: " & theperson.lastName - WScript.Echo "Birthdate: " & theperson.birthdate - - titles = theperson.titles - For i = 0 to UBound(titles) - Set title = titles(i) - WScript.Echo "Title: " & title.name & ": " & title.description - Next - -In the future, we would like to make this easier to use by being able to just -pass the URI to the WSDL for the service into the client, rather than having -to create Python stubs. -''' - -from warnings import warn -warn('This module is under active development and should not be used in a production scenario') - -from win32com.server.exception import COMException -from win32com.server import util -from soaplib.client import make_service_client -from soaplib.serializers.clazz import ClassSerializer -from soaplib.serializers.primitive import DateTime -from datetime import datetime - -import winerror -import types -import time - -# COM object wrapping and unwrapping utility functions -def coerce_date_time(dt): - return datetime(*time.strptime(str(dt), '%m/%d/%y %H:%M:%S')[0:5]) - -def unwrap_complex_type(param, param_type): - param = util.unwrap(param) - for membername, membertype in param_type.soap_members.items(): - member = getattr(param, membername) - if type(member).__name__ == 'PyIDispatch': - member = unwrap_complex_type(member, membertype) - elif membertype is DateTime: - member = coerce_date_time(member) - elif type(member) in [types.ListType, types.TupleType]: - newmember = [] - for item in member: - if type(item).__name__ == 'PyIDispatch': - item = unwrap_complex_type(item, membertype.serializer) - newmember.append(item) - member = newmember - setattr(param, membername, member) - return param - -def wrap_complex_type(data, data_type): - for membername, membertype in data_type.soap_members.items(): - member = getattr(data, membername) - if isinstance(member, ClassSerializer): - member = wrap_complex_type(member, membertype) - elif type(member) in [types.ListType, types.TupleType]: - newmember = [] - for item in member: - if isinstance(item, ClassSerializer): - item = wrap_complex_type(item, item.__class__) - newmember.append(item) - member = newmember - setattr(data, membername, member) - data = util.wrap(data) - return data - - -class WebServiceClient: - _reg_progid_ = 'SoapLib.ServiceClient' - _reg_clsid_ = '{BAC77389-8687-4A8A-9DD0-2E4409FEF900}' - _reg_policy_spec_ = 'DynamicPolicy' - - def SetServiceInfo(self, serviceURI, serviceName): - try: - parts = serviceName.split('.') - item = __import__('.'.join(parts[:-1])) - for part in parts[1:]: - item = getattr(item, part) - - self.client_type = item() - self.client = make_service_client(str(serviceURI), self.client_type) - except: - raise COMException('No such service', winerror.DISP_E_BADVARTYPE) - - def CreateObject(self, typename): - try: - parts = typename.split('.') - item = __import__('.'.join(parts[:-1])) - for part in parts[1:]: - item = getattr(item, part) - return util.wrap(item()) - except: - raise COMException('No such type', winerror.DISP_E_BADVARTYPE) - - def _dynamic_(self, name, lcid, wFlags, args): - # Look up the requested method. First, check to see if the - # method is present on ourself (utility functions), then - # check to see if it exists on the client service. - is_service_method = False - item = getattr(self, name, None) - if item is None and hasattr(self, 'client'): - item = getattr(self.client, name) - is_service_method = True - - if item is None: - raise COMException('No attribute of that name.', - winerror.DISP_E_MEMBERNOTFOUND) - - # Figure out what parameters this web service call accepts, - # and what it returns, so that we can properly wrap the objects - # on the way in and unwrap them on the way out. - if is_service_method: - all_methods = self.client_type.methods() - method_descriptor = [method for method in all_methods if method.name == name][0] - return_type = method_descriptor.outMessage.params[0][1] - parameter_types = [parameter[1] for parameter in method_descriptor.inMessage.params] - - # Now that we have this data, go ahead and unwrap any wrapped parameters, - # recursively. - newargs = [] - for param_type, param in zip(parameter_types, args): - if hasattr(param_type, '__bases__') and ClassSerializer in param_type.__bases__: - param = unwrap_complex_type(param, param_type) - elif param_type is DateTime: - param = coerce_date_time(param) - newargs.append(param) - args = newargs - - # Call the supplied method - result = apply(item, args) - - # Now wrap the return value, recursively. - if isinstance(result, ClassSerializer): - result = wrap_complex_type(result, return_type) - - # Return our data - return result - - -if __name__ == '__main__': - import win32com.server.register - win32com.server.register.UseCommandLine(WebServiceClient) diff -Nru python-soaplib-0.8.1/soaplib/ext/wsdl2py.py python-soaplib-0.9.3-alpha3/soaplib/ext/wsdl2py.py --- python-soaplib-0.8.1/soaplib/ext/wsdl2py.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/ext/wsdl2py.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,206 +0,0 @@ -import urllib2 -import new -from soaplib.soap import Message, MethodDescriptor -from soaplib.serializers import primitive -from soaplib.serializers.clazz import ClassSerializer -from soaplib.client import * -from soaplib.etimport import ElementTree as et - -from warnings import warn -warn('This module is under active development and should not be used in a production scenario') - - -_primitives = { - 'xs:string':primitive.String, - 'xs:int':primitive.Integer, - 'xs:dateTime':primitive.DateTime, - 'xs:float':primitive.Float, - 'xs:boolean':primitive.Boolean, - 'string':primitive.String, - 'int':primitive.Integer, - 'dateTime':primitive.DateTime, - 'float':primitive.Float, - 'boolean':primitive.Boolean -} - -_types = {} -_messages = {} -_methods = {} -_clients = {} -_service_name = '' -_location = '' - -wsdl = 'http://schemas.xmlsoap.org/wsdl/' -xs = 'http://www.w3.org/2001/XMLSchema' - -_serializers = {} -ns = 'test' - -_type_elements = {} - -def is_array(name): - # need better checking for arrays - #print _type_elements - print _types - if len(_type_elements[name]) == 1 and _type_elements[name][0].get('maxOccurs') == 'unbounded': - return True - return False - -def get_serializer(name): - print name - print _types - if _primitives.has_key(name): - return _primitives[name] - - if not _serializers.has_key(name): - name = name.split(':')[-1] - - if is_array(name): - s = _types[name] - inner_serializer = get_serializer( s.keys()[0] ) - class_serializer = primitive.Array(inner_serializer) - else: - s = _types[name] - print s - class_serializer = new.classobj(name,(ClassSerializer,),{}) - setattr(class_serializer,'soap_members',{}) - setattr(class_serializer,'namespace',None) - for k,v in s.items(): - class_serializer.soap_members[k] = get_serializer(v) - _serializers[name] = class_serializer - - return _serializers[name] - -def qn(prefix,typ): - return '{%s}%s'%(globals()[prefix],typ) - -def handle_wsdl(wsdl): - handle_types(wsdl.find(qn('wsdl','types'))) - handle_messages(wsdl.findall(qn('wsdl','message'))) - handle_portType(wsdl.find(qn('wsdl','portType'))) - handle_service(wsdl.find(qn('wsdl','service'))) - print _types - -def handle_service(service): - _service_name = service.get('name') - location = service.getchildren()[0].getchildren()[0].get('location') - _location = location - -def handle_types(types): - schema = types[0] - elements = schema.findall(qn('xs','element')) - for element in elements: - for c in schema.findall(qn('xs','complexType')): - if c.get('name') == element.get('name'): - handle_element(element,c) - for k in _types.keys(): - print get_serializer(k) - -def handle_element(element,complexType): - parts = {} - se = complexType.find(qn('xs','sequence')).findall(qn('xs','element')) - name = element.get('name') - _type_elements[name] = se - print se - for e in se: - print 'adding',e.get('name') - parts[e.get('name')] = e.get('type') - #array = '' - #if e.get('maxOccurs') == 'unbounded': # check for arrays? - # array = 'array' - #print ' ',e.get('name'),e.get('type'), array - print parts - _types[name] = parts - #_serializers = {} - -def get_params(msg): - print msg - params = _types[msg] - msg_params = [] - for k,v in params.items(): - if v.startswith('xs'): - msg_params.append((k,_primitives[v])) - else: - print 'k,v',k,v - v = v.split(':')[-1] - msg_params.append((k,_serializers[v])) - #print k,v - #msg_params.append() - return msg_params - -def handle_messages(messages): - print messages - print "^"*80 - for m in messages: - name = m.get('name') - p = m.find(qn('wsdl','part')) - _messages[name] = (p.get('element'),p.get('name')) - params = get_params(name) - print 'params',params - - msg_obj = Message(name,name,params) - _messages[name] = msg_obj - -def handle_portType(pt): - operations = pt.findall(qn('wsdl','operation')) - for operation in operations: - name = operation.get('name') - input = operation.find(qn('wsdl','input')) - in_msg = input.get('message') - in_name = input.get('name') - - output = operation.find(qn('wsdl','output')) - if output != None: - out_msg = output.get('message') - out_name = output.get('name') - else: - out_msg = '' - out_name = '' - - print '*'*80 - print _messages[in_msg.split(':')[-1]] - print ' name ',name - print ' input ',in_msg,in_name - print ' output ',out_msg,out_name - - method = MethodDescriptor(name,_messages[in_name],_messages[out_name]) - _methods[name] = method - -def make_clients(): - print _service_name - for name, method in _methods.items(): - _clients[name] = SimpleSoapClient('127.0.0.1:9753','/',method) - service = new.classobj(_service_name,(object,),{}) - for operation, client in _clients.items(): - setattr(service,operation,client) - return service - -def run(url): - content = urllib2.urlopen(url).read() - wsdl = et.fromstring(content) - handle_wsdl(wsdl) - return make_clients() - -if __name__ == "__main__": - service = run('http://127.0.0.1:9753/service.wsdl') - #print _serializers - #a = _serializers['echoSimpleClass'] - #print a - #print dir(a) - #print dir(a.soap_members) - - #print '*'*80 - #r = a() - #r.sc = _serializers['SimpleClass']() - #r.sc.i = 15 - #r.sc.s = 'af' - #print _type_elements - nc = _serializers['NestedClass']() - nc.s = 'blah' - #print dir(nc) - simple = _serializers['SimpleClass']() - simple.i = 13 - simple.s = 'ab' - nc.simple = [simple] - print service.echoNestedClass(nc).s - print service.echoSimpleClass(simple).i diff -Nru python-soaplib-0.8.1/soaplib/__init__.py python-soaplib-0.9.3-alpha3/soaplib/__init__.py --- python-soaplib-0.8.1/soaplib/__init__.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -# diff -Nru python-soaplib-0.8.1/soaplib/serializers/binary.py python-soaplib-0.9.3-alpha3/soaplib/serializers/binary.py --- python-soaplib-0.8.1/soaplib/serializers/binary.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/serializers/binary.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,122 +0,0 @@ -import base64 -import cStringIO -from soaplib.xml import ns, create_xml_element - -class Attachment(object): - - def __init__(self,data=None,fileName=None): - self.data = data - self.fileName = fileName - - def save_to_file(self): - '''This method writes the data to the specified file. This method - assumes that the filename is the full path to the file to be written. - This method also assumes that self.data is the base64 decoded data, - and will do no additional transformations on it, simply write it to - disk. - ''' - if not self.data: - raise Exception("No data to write") - if not self.fileName: - raise Exception("No filename specified") - f = open(self.fileName,'wb') - f.write(self.data) - f.flush() - f.close() - - def load_from_file(self): - ''' - This method loads the data from the specified file, and does - no encoding/decoding of the data - ''' - if not self.fileName: - raise Exception("No filename specified") - f = open(self.fileName,'rb') - self.data = f.read() - f.close() - - @classmethod - def to_xml(cls,value,name='retval',nsmap=ns): - '''This class method takes the data from the attachment and - base64 encodes it as the text of an Element. An attachment can - specify a filename and if no data is given, it will read the data - from the file - ''' - if value.__class__ is not Attachment: - raise Exception("Do not know how to serialize class %s"%type(value)) - - element = create_xml_element(name, nsmap) - if value.data: - # the data has already been loaded, just encode - # and return the element - element.text = base64.encodestring(value.data) - elif value.fileName: - # the data hasn't been loaded, but a file has been - # specified - data_string = cStringIO.StringIO() - - fileName = value.fileName - file = open(fileName,'rb') - base64.encode(file, data_string) - file.close() - - # go back to the begining of the data - data_string.seek(0) - element.text = str(data_string.read()) - else: - raise Exception("Neither data nor a filename has been specified") - - return element - - @classmethod - def from_xml(cls,element): - ''' - This method returns an Attachment object that contains - the base64 decoded string of the text of the given element - ''' - data = base64.decodestring(element.text) - a = Attachment(data=data) - return a - - @classmethod - def get_datatype(cls,nsmap=None): - '''Returns the datatype base64Binary''' - if nsmap is not None: - return nsmap.get(cls.get_namespace_id()) + 'base64Binary' - return 'base64Binary' - - @classmethod - def get_namespace_id(cls): - return 'xs' - - @classmethod - def add_to_schema(cls,added_params,nsmap): - ''' - Nothing needs to happen here as base64Binary is a standard - schema element - ''' - pass - - @classmethod - def collect_namespaces(cls, ns_map): - pass - -if __name__ == '__main__': - import os - os.system('rm /tmp/boxer2.tiff') - - a = Attachment(fileName='/tmp/boxer.tiff') - a.load_data() - #print a.data - element = Attachment.to_xml(a,'bob') - a1 = Attachment.from_xml(element) - - a1.fileName = '/tmp/boxer2.tiff' - a1.save_data() - - os.system('open /tmp/boxer2.tiff') - - - - - diff -Nru python-soaplib-0.8.1/soaplib/serializers/clazz.py python-soaplib-0.9.3-alpha3/soaplib/serializers/clazz.py --- python-soaplib-0.8.1/soaplib/serializers/clazz.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/serializers/clazz.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,119 +0,0 @@ -import inspect -from soaplib.xml import ns, create_xml_element, create_xml_subelement, qualify - -from primitive import Null - -class ClassSerializerMeta(type): - ''' - This is the metaclass that populates ClassSerializer instances with - the appropriate datatypes for (de)serialization - ''' - - def __init__(cls, clsname, bases, dictionary): - ''' - This initializes the class, and sets all the appropriate - types onto the class for serialization. This implementation - assumes that all attributes assigned to this class are internal - serializers for this class - ''' - if not hasattr(cls,'types'): - return - types = cls.types - members = dict(inspect.getmembers(types)) - cls.soap_members = {} - cls.namespace = None - - for k,v in members.items(): - if k == '_namespace_': - cls.namespace=v - - elif not k.startswith('__'): - cls.soap_members[k] = v - - # COM bridge attributes that are otherwise harmless - cls._public_methods_ = [] - cls._public_attrs_ = cls.soap_members.keys() - -class ClassSerializer(object): - __metaclass__ = ClassSerializerMeta - - def __init__(self): - cls = self.__class__ - for k,v in cls.soap_members.items(): - setattr(self,k,None) - - @classmethod - def to_xml(cls,value,name='retval', nsmap=ns): - element = create_xml_element( - nsmap.get(cls.get_namespace_id()) + name, nsmap) - - for k,v in cls.soap_members.items(): - member_value = getattr(value,k,None) - - subvalue = getattr(value,k,None) - if subvalue is None: - v = Null - - subelements = v.to_xml(subvalue,name=k,nsmap=nsmap) - if type(subelements) != list: - subelements = [subelements] - for s in subelements: - element.append(s) - return element - - @classmethod - def from_xml(cls, element): - obj = cls() - children = element.getchildren() - d = {} - for c in children: - name = c.tag.split('}')[-1] - if not d.has_key(name): - d[name] = [] - d[name].append(c) - - for tag,v in d.items(): - member = cls.soap_members.get(tag) - - value = member.from_xml(*v) - setattr(obj,tag,value) - return obj - - @classmethod - def get_datatype(cls,nsmap=None): - if nsmap is not None: - return nsmap.get(cls.get_namespace_id()) + cls.__name__ - return cls.__name__ - - @classmethod - def get_namespace_id(cls): - return 'tns' - - @classmethod - def add_to_schema(cls, schemaDict, nsmap): - - if not schemaDict.has_key(cls.get_datatype(nsmap)): - for k,v in cls.soap_members.items(): - v.add_to_schema(schemaDict, nsmap) - - schema_node = create_xml_element( - nsmap.get("xs") + "complexType", nsmap) - schema_node.set('name',cls.__name__) - - sequence_node = create_xml_subelement( - schema_node, nsmap.get('xs') + 'sequence') - for k,v in cls.soap_members.items(): - member_node = create_xml_subelement( - sequence_node, nsmap.get('xs') + 'element') - member_node.set('name',k) - member_node.set('minOccurs','0') - member_node.set('type', - "%s:%s" % (v.get_namespace_id(), v.get_datatype())) - - typeElement = create_xml_element( - nsmap.get('xs') + 'element', nsmap) - typeElement.set('name',cls.__name__) - typeElement.set('type', - "%s:%s" % (cls.get_namespace_id(),cls.__name__)) - schemaDict[cls.get_datatype(nsmap)+'Complex'] = schema_node - schemaDict[cls.get_datatype(nsmap)] = typeElement diff -Nru python-soaplib-0.8.1/soaplib/serializers/primitive.py python-soaplib-0.9.3-alpha3/soaplib/serializers/primitive.py --- python-soaplib-0.8.1/soaplib/serializers/primitive.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/serializers/primitive.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,486 +0,0 @@ -from soaplib.xml import ns, create_xml_element, create_xml_subelement -from soaplib.etimport import ElementTree -import datetime -import re -import cStringIO - -import pytz -from pytz import FixedOffset - -####################################################### -# Utility Functions -####################################################### - -string_encoding = 'utf-8' - -_datetime_pattern = r'(?P\d{4})-(?P\d{2})-(?P\d{2})[T ](?P
\d{2}):(?P\d{2}):(?P\d{2})(?P\.\d+)?' -_local_re = re.compile(_datetime_pattern) -_utc_re = re.compile(_datetime_pattern + 'Z') -_offset_re = re.compile(_datetime_pattern + r'(?P[+-]\d{2}):(?P\d{2})') - -def _is_null_element(element): - for k in element.keys(): - if k.endswith('null'): - return True - return False - -def _element_to_datetime(element): - # expect ISO formatted dates - # - text = element.text - if not text: - return None - - def parse_date(date_match, tz=None): - fields = date_match.groupdict(0) - year, month, day, hr, min, sec = [ int(fields[x]) for x in - ("year", "month", "day", "hr", "min", "sec")] - # use of decimal module here (rather than float) might be better - # here, if willing to require python 2.4 or higher - microsec = int(float(fields.get("fractional_sec", 0)) * 10**6) - return datetime.datetime(year, month, day, hr, min, sec, microsec, tz) - - match = _utc_re.match(text) - if match: - return parse_date(match, tz=pytz.utc) - match = _offset_re.match(text) - if match: - tz_hr, tz_min = [int(match.group(x)) for x in "tz_hr", "tz_min"] - return parse_date(match, tz=FixedOffset(tz_hr*60 + tz_min, {})) - match = _local_re.match(text) - if match: - return parse_date(match) - raise Exception("DateTime [%s] not in known format"%text) - -def _element_to_string(element): - text = element.text - if text: - return text.decode(string_encoding) - else: - return None - -def _element_to_integer(element): - i = element.text - if not i: - return None - try: - return int(str(i)) - except: - try: return long(i) - except: return None - -def _element_to_float(element): - f = element.text - if f is None: - return None - return float(f) - -def _element_to_unicode(element): - u = element.text - if not u: - return None - try: - u = str(u) - return u.encode(string_encoding) - except: - return u - -def _unicode_to_xml(value, name, cls, nsmap): - retval = create_xml_element(name, nsmap) - if value == None: - return Null.to_xml(value,name,nsmap) - if type(value) == unicode: - retval.text = value - else: - retval.text = unicode(value,string_encoding) - retval.set( - nsmap.get('xsi') + 'type', - "%s:%s" % (cls.get_namespace_id(), cls.get_datatype())) - return retval - -def _generic_to_xml(value, name, cls, nsmap): - retval = create_xml_element(name, nsmap) - if value: - retval.text = value - retval.set( - nsmap.get('xsi') + 'type', - "%s:%s" % (cls.get_namespace_id(), cls.get_datatype())) - return retval - -def _get_datatype(cls, typename, nsmap): - if nsmap is not None: - return nsmap.get(cls.get_namespace_id()) + typename - return typename - -class Any: - - @classmethod - def to_xml(cls,value,name='retval',nsmap=ns): - if type(value) == str: - value = ElementTree.fromstring(value) - e = create_xml_element(name, nsmap) - e.append(value) - return e - - @classmethod - def from_xml(cls,element): - children = element.getchildren() - if children: - return element.getchildren()[0] - return None - - @classmethod - def get_datatype(cls, nsmap=None): - return _get_datatype(cls, 'anyType', nsmap) - - @classmethod - def get_namespace_id(cls): - return 'xs' - - @classmethod - def add_to_schema(cls,added_params,nsmap): - pass - -class String: - - @classmethod - def to_xml(cls,value,name='retval',nsmap=ns): - e = _unicode_to_xml(value, name, cls, nsmap) - return e - - @classmethod - def from_xml(cls,element): - return _element_to_unicode(element) - - @classmethod - def get_datatype(cls, nsmap=None): - return _get_datatype(cls, 'string', nsmap) - - @classmethod - def get_namespace_id(cls): - return 'xs' - - @classmethod - def add_to_schema(cls,added_params,nsmap): - pass - -class Fault(Exception): - - def __init__(self, faultcode = 'Server', faultstring = None, detail = None, name = 'ExceptionFault'): - self.faultcode = faultcode - self.faultstring = faultstring - self.detail = detail - self.name = name - - @classmethod - def to_xml(cls, value, name, nsmap=ns): - fault = create_xml_element(name, nsmap) - create_xml_subelement(fault, 'faultcode').text = value.faultcode - create_xml_subelement(fault, 'faultstring').text = value.faultstring - detail = create_xml_subelement(fault, 'detail').text = value.detail - return fault - - - @classmethod - def from_xml(cls, element): - code = _element_to_string(element.find('faultcode')) - string = _element_to_string(element.find('faultstring')) - detail_element = element.find('detail') - if detail_element is not None: - if len(detail_element.getchildren()): - detail = ElementTree.tostring(detail_element) - else: - detail = _element_to_string(element.find('detail')) - else: - detail = '' - return Fault(faultcode = code, faultstring = string, detail = detail) - - @classmethod - def get_datatype(cls, nsmap=None): - return _get_datatype(cls, 'ExceptionFaultType', nsmap) - - @classmethod - def get_namespace_id(cls): - return 'tns' - - @classmethod - def add_to_schema(cls,schema_dict,nsmap): - complexTypeNode = create_xml_element('complexType', nsmap) - complexTypeNode.set('name', cls.get_datatype()) - sequenceNode = create_xml_subelement(complexTypeNode, 'sequence') - faultTypeElem = create_xml_subelement(sequenceNode,'element') - faultTypeElem.set('name','detail') - faultTypeElem.set(nsmap.get('xsi') + 'type', 'xs:string') - faultTypeElem = create_xml_subelement(sequenceNode,'element') - faultTypeElem.set('name','message') - faultTypeElem.set(nsmap.get('xsi') + 'type', 'xs:string') - - schema_dict[cls.get_datatype()] = complexTypeNode - - typeElementItem = create_xml_element('element', nsmap) - typeElementItem.set('name', 'ExceptionFaultType') - typeElementItem.set(nsmap.get('xsi') + 'type', cls.get_datatype(nsmap)) - schema_dict['%sElement'%(cls.get_datatype(nsmap))] = typeElementItem - - def __str__(self): - io = cStringIO.StringIO() - io.write("*"*80) - io.write("\r\n") - io.write(" Recieved soap fault \r\n") - io.write(" FaultCode %s \r\n"%self.faultcode) - io.write(" FaultString %s \r\n"%self.faultstring) - io.write(" FaultDetail \r\n") - if self.detail is not None: - io.write(self.detail) - return io.getvalue() - -class Integer: - - @classmethod - def to_xml(cls,value,name='retval',nsmap=ns): - e = _generic_to_xml(str(value), name, cls, nsmap) - return e - - @classmethod - def from_xml(cls,element): - return _element_to_integer(element) - - @classmethod - def get_datatype(cls, nsmap=None): - return _get_datatype(cls, 'int', nsmap) - - @classmethod - def get_namespace_id(cls): - return 'xs' - - @classmethod - def add_to_schema(cls,added_params,nsmap): - pass - - -class Double: - - @classmethod - def to_xml(cls,value,name='retval',nsmap=ns): - e = _generic_to_xml(str(value), name, cls, nsmap) - return e - - @classmethod - def from_xml(cls,element): - return _element_to_integer(element) - - @classmethod - def get_datatype(cls, nsmap=None): - return _get_datatype(cls, 'double', nsmap) - - @classmethod - def get_namespace_id(cls): - return 'xs' - - @classmethod - def add_to_schema(cls,added_params,nsmap): - pass - - -class DateTime: - - @classmethod - def to_xml(cls,value,name='retval',nsmap=ns): - if type(value) == datetime.datetime: - value = value.isoformat('T') - e = _generic_to_xml(value, name, cls, nsmap) - return e - - @classmethod - def from_xml(cls,element): - return _element_to_datetime(element) - - @classmethod - def get_datatype(cls, nsmap=None): - return _get_datatype(cls, 'dateTime', nsmap) - - @classmethod - def get_namespace_id(cls): - return 'xs' - - @classmethod - def add_to_schema(cls,added_params,nsmap): - pass - -class Float: - - @classmethod - def to_xml(cls,value,name='retval',nsmap=ns): - e = _generic_to_xml(str(value), name, cls, nsmap) - return e - - @classmethod - def from_xml(cls,element): - return _element_to_float(element) - - @classmethod - def get_datatype(cls, nsmap=None): - return _get_datatype(cls, 'float', nsmap) - - @classmethod - def get_namespace_id(cls): - return 'xs' - - @classmethod - def add_to_schema(cls,added_params,nsmap): - pass - -class Null: - - @classmethod - def to_xml(cls,value,name='retval',nsmap=ns): - element = create_xml_element(name, nsmap) - element.set(cls.get_datatype(nsmap),'1') - return element - - @classmethod - def from_xml(cls,element): - return None - - @classmethod - def get_datatype(cls, nsmap=None): - return _get_datatype(cls, 'null', nsmap) - - @classmethod - def get_namespace_id(cls): - return 'xs' - - @classmethod - def add_to_schema(cls,added_params,nsmap): - pass - -class Boolean: - - @classmethod - def to_xml(cls,value,name='retval',nsmap=ns): - # applied patch from Julius Volz - #e = _generic_to_xml(str(value).lower(),name,cls.get_datatype(nsmap)) - if value == None: - return Null.to_xml('', name, nsmap) - else: - e = _generic_to_xml(str(bool(value)).lower(), name, cls, nsmap) - return e - - @classmethod - def from_xml(cls,element): - s = _element_to_string(element) - if s == None: - return None - if s and s.lower()[0] == 't': - return True - return False - - @classmethod - def get_datatype(cls, nsmap=None): - return _get_datatype(cls, 'boolean', nsmap) - - @classmethod - def get_namespace_id(cls): - return 'xs' - - @classmethod - def add_to_schema(cls,added_params,nsmap): - pass - -class Array: - - def __init__(self,serializer,type_name=None,namespace_id='tns'): - self.serializer = serializer - self.namespace_id = namespace_id - if not type_name: - self.type_name = '%sArray'%self.serializer.get_datatype() - else: - self.type_name = type_name - - def to_xml(self,values,name='retval',nsmap=ns): - res = create_xml_element(name, nsmap) - typ = self.get_datatype(nsmap) - if values == None: - values = [] - res.set('type', - "%s:%s" % (self.get_namespace_id(), self.get_datatype())) - for value in values: - serializer = self.serializer - if value == None: - serializer = Null - res.append( - serializer.to_xml(value, serializer.get_datatype(), nsmap)) - return res - - def from_xml(self,element): - results = [] - for child in element.getchildren(): - results.append(self.serializer.from_xml(child)) - return results - - def get_datatype(self, nsmap=None): - return _get_datatype(self, self.type_name, nsmap) - - def get_namespace_id(self): - return self.namespace_id - - def add_to_schema(self,schema_dict,nsmap): - typ = self.get_datatype() - - self.serializer.add_to_schema(schema_dict, nsmap) - - if not schema_dict.has_key(typ): - - complexTypeNode = create_xml_element( - nsmap.get('xs') + 'complexType', nsmap) - complexTypeNode.set('name',self.get_datatype()) - - sequenceNode = create_xml_subelement( - complexTypeNode, nsmap.get('xs') + 'sequence') - elementNode = create_xml_subelement( - sequenceNode, nsmap.get('xs') + 'element') - elementNode.set('minOccurs','0') - elementNode.set('maxOccurs','unbounded') - elementNode.set('type', - "%s:%s" % (self.namespace_id, self.serializer.get_datatype())) - elementNode.set('name',self.serializer.get_datatype()) - - typeElement = create_xml_element( - nsmap.get('xs') + 'element', nsmap) - typeElement.set('name',typ) - typeElement.set('type', - "%s:%s" % (self.namespace_id, self.get_datatype())) - - schema_dict['%sElement'%(self.get_datatype(nsmap))] = typeElement - schema_dict[self.get_datatype(nsmap)] = complexTypeNode - -class Repeating(object): - - def __init__(self,serializer,type_name=None,namespace_id='tns'): - self.serializer = serializer - self.namespace_id = namespace_id - - def to_xml(self,values,name='retval',nsmap=ns): - if values == None: - values = [] - res = [] - for value in values: - serializer = self.serializer - if value == None: - serializer = Null - res.append( - serializer.to_xml(value,name,nsmap) - ) - return res - - def get_namespace_id(self): - return self.namespace_id - - def from_xml(self,*elements): - results = [] - for child in elements: - results.append(self.serializer.from_xml(child)) - return results - - def add_to_schema(self,schema_dict,nsmap): - raise Exception("The Repeating serializer is experimental and not supported for wsdl generation") diff -Nru python-soaplib-0.8.1/soaplib/service.py python-soaplib-0.9.3-alpha3/soaplib/service.py --- python-soaplib-0.8.1/soaplib/service.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/service.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,391 +0,0 @@ -from soaplib.xml import create_xml_element, create_xml_subelement -from soaplib.xml import ElementTree, NamespaceLookup -from soaplib.soap import Message, MethodDescriptor - -def soapmethod(*params, **kparams): - ''' - This is a method decorator to flag a method as a soap 'rpc' method. It - will behave like a normal python method on a class, and will only behave - differently when the keyword '_soap_descriptor' is passed in, returning - a 'MethodDescriptor' object. This decorator does none of the soap/xml - serialization, only flags a method as a soap method. This decorator - should only be used on methods that are in instances of SoapServiceBase. - ''' - def explain(f): - def explainMethod(*args, **kwargs): - if kwargs.has_key('_soap_descriptor'): - name = f.func_name - - _returns = kparams.get('_returns') - _isCallback = kparams.get('_isCallback',False) - _soapAction = kparams.get('_soapAction',name) - _isAsync = kparams.get('_isAsync',False) - _inMessage = kparams.get('_inMessage',name) - _inVariableNames = kparams.get('_inVariableNames',{}) - _outMessage = kparams.get('_outMessage','%sResponse'%name) - _outVariableName = kparams.get('_outVariableName', '%sResult'%name) - _mtom = kparams.get('_mtom',False) - - ns = None - - # the decorator function does not have a reference to the - # class and needs to be passed in - if kwargs.has_key('klazz'): - ns = getTNS(kwargs['klazz']) - - # input message - param_names = f.func_code.co_varnames[1:f.func_code.co_argcount] - try: - in_params = [(_inVariableNames.get(param_names[i], param_names[i]), params[i]) for i in range(0, len(params))] - except IndexError,e: - print f.func_name - raise Exception("%s has parameter numbers mismatching" %f.func_name) - - in_message = Message(_inMessage,in_params,ns=ns,typ=_inMessage) - - # output message - if _returns: - out_params = [(_outVariableName,_returns)] - else: - out_params = [] - out_message = Message(_outMessage,out_params,ns=ns,typ=_outMessage) - doc = getattr(f,'__doc__') - descriptor = MethodDescriptor(f.func_name,_soapAction,in_message,out_message,doc,_isCallback,_isAsync,_mtom) - - return descriptor - return f(*args, **kwargs) - explainMethod.__doc__ = f.__doc__ - explainMethod.func_name = f.func_name - explainMethod._is_soap_method = True - return explainMethod - return explain - -def getTNS(cls): - ''' - Utility function to get the namespace of a given service class - @param the service in question - @return the namespace - ''' - serviceName = cls.__name__.split('.')[-1] - if hasattr(cls,'__tns__'): - return cls.__tns__ - if cls.__module__ == '__main__': - return '.'.join((serviceName,serviceName)) - return '.'.join((cls.__module__,serviceName)) - -class SoapServiceBase(object): - ''' - This class serves as the base for all soap services. Subclasses of this - class will use the soapmethod and soapdocument decorators to flag methods - to be exposed via soap. This class is responsible for generating the - wsdl for this object. - ''' - def __init__(self): - self._soap_methods = [] - self.__wsdl__ = None - self.__tns__ = getTNS(self.__class__) - self._soap_methods = self._get_soap_methods() - - def _get_soap_methods(self): - '''Returns a list of method descriptors for this object''' - soap_methods = [] - for funcName in dir(self): - func = getattr(self,funcName) - if callable(func) and hasattr(func,'_is_soap_method'): - descriptor = func(_soap_descriptor=True,klazz=self.__class__) - soap_methods.append(descriptor) - return soap_methods - - def methods(self): - ''' - returns the soap methods for this object - @return method descriptor list - ''' - return self._soap_methods - - def _hasCallbacks(self): - '''Determines if this object has callback methods or not''' - for method in self.methods(): - if method.isCallback: - return True - return False - - def header_objects(self): - return [] - - def getServiceNames(self): - '''Returns the service name(s) for this service. If this - object has callbacks, then a second service is declared in - the wsdl for those callbacks''' - serviceName = self.__class__.__name__.split('.')[-1] - if self._hasCallbacks(): - return [serviceName,'%sCallback'%serviceName] - return [serviceName] - - def wsdl(self, url): - ''' - This method generates and caches the wsdl for this object based - on the soap methods designated by the soapmethod or soapdocument - descriptors - @param url the url that this service can be found at. This must be - passed in by the caller because this object has no notion of the - server environment in which it runs. - @returns the string of the wsdl - ''' - if not self.__wsdl__ == None: - # return the cached __wsdl__ - return self.__wsdl__ - url = url.replace('.wsdl','') - # otherwise build it - serviceName = self.__class__.__name__.split('.')[-1] - - tns = self.__tns__ - methods = self.methods() - hasCallbacks = self._hasCallbacks() - - nsmap = NamespaceLookup(tns, True) - if hasCallbacks: - nsmap.set('wsa','http://schemas.xmlsoap.org/ws/2003/03/addressing') - - root = create_xml_element( - "definitions", nsmap, 'http://schemas.xmlsoap.org/wsdl/') - root.set('targetNamespace',tns) - root.set('name',serviceName) - - types = create_xml_subelement(root, "types") - - self._add_schema(types, methods, nsmap) - self._add_messages_for_methods(root, methods, nsmap) - - # add necessary async headers - # WS-Addressing -> RelatesTo ReplyTo MessageID - # callback porttype - if hasCallbacks: - wsaSchemaNode = create_xml_subelement(types, "schema") - wsaSchemaNode.set("targetNamespace", tns+'Callback') - wsaSchemaNode.set("xmlns", "http://www.w3.org/2001/XMLSchema") - - importNode = create_xml_subelement(wsaSchemaNode, "import") - importNode.set("namespace", "http://schemas.xmlsoap.org/ws/2003/03/addressing") - importNode.set("schemaLocation", "http://schemas.xmlsoap.org/ws/2003/03/addressing/") - - - reltMessage = create_xml_subelement(root,'message') - reltMessage.set('name','RelatesToHeader') - reltPart = create_xml_subelement(reltMessage,'part') - reltPart.set('name','RelatesTo') - reltPart.set('element','wsa:RelatesTo') - - replyMessage = create_xml_subelement(root,'message') - replyMessage.set('name','ReplyToHeader') - replyPart = create_xml_subelement(replyMessage,'part') - replyPart.set('name','ReplyTo') - replyPart.set('element','wsa:ReplyTo') - - idHeader = create_xml_subelement(root,'message') - idHeader.set('name','MessageIDHeader') - idPart = create_xml_subelement(idHeader,'part') - idPart.set('name','MessageID') - idPart.set('element','wsa:MessageID') - - # make portTypes - callbackPortType = create_xml_subelement(root,'portType') - callbackPortType.set('name','%sCallback'%serviceName) - - cbServiceName = '%sCallback'%serviceName - cbService = create_xml_subelement(root,'service') - cbService.set('name',cbServiceName) - cbWsdlPort = create_xml_subelement(cbService,'port') - cbWsdlPort.set('name',cbServiceName) - cbWsdlPort.set('binding','tns:%s'%cbServiceName) - cbAddr = create_xml_subelement(cbWsdlPort, nsmap.get('soap') + 'address') - cbAddr.set('location',url) - - - serviceName = self.__class__.__name__.split('.')[-1] - portType = create_xml_subelement(root,'portType') - portType.set('name',serviceName) - for method in methods: - if method.isCallback: - operation = create_xml_subelement(callbackPortType,'operation') - else: - operation = create_xml_subelement(portType,'operation') - - operation.set('name',method.name) - params = [] - for name,param in method.inMessage.params: - params.append(name) - - documentation = create_xml_subelement(operation,'documentation') - documentation.text = method.doc - operation.set('parameterOrder',method.inMessage.typ) - opInput = create_xml_subelement(operation,'input') - opInput.set('name',method.inMessage.typ) - opInput.set('message','tns:%s'%method.inMessage.typ) - - if method.outMessage.params != None and not method.isCallback and not method.isAsync: - opOutput = create_xml_subelement(operation,'output') - opOutput.set('name',method.outMessage.typ) - opOutput.set('message','tns:%s'%method.outMessage.typ) - - # make partner link - plink = create_xml_subelement(root, nsmap.get('plnk') + 'partnerLinkType') - plink.set('name',serviceName) - role = create_xml_subelement(plink, nsmap.get('plnk') + 'role') - role.set('name', serviceName) - plinkPortType = create_xml_subelement(role, nsmap.get('plnk') + 'portType') - plinkPortType.set('name','tns:%s'%serviceName) - - if hasCallbacks: - role = create_xml_subelement(plink, nsmap.get('plnk') + 'role') - role.set('name', '%sCallback'%serviceName) - plinkPortType = create_xml_subelement(role, nsmap.get('plnk') + 'portType') - plinkPortType.set('name','tns:%sCallback'%serviceName) - - self._add_bindings_for_methods(root, serviceName, methods, nsmap) - - service = create_xml_subelement(root,'service') - service.set('name',serviceName) - wsdlPort = create_xml_subelement(service,'port') - wsdlPort.set('name',serviceName) - wsdlPort.set('binding','tns:%s'%serviceName) - addr = create_xml_subelement(wsdlPort, nsmap.get('soap') + 'address') - addr.set('location',url) - - wsdl = ElementTree.tostring(root) - wsdl = "%s"%(wsdl) - - #cache the wsdl for next time - self.__wsdl__ = wsdl - return self.__wsdl__ - - def _add_schema(self, types, methods, nsmap): - '''A private method for adding the appropriate entries - to the schema for the types in the specified methods - @param the schema node to add the schema elements to - @param the list of methods. - ''' - schema_entries = {} - for method in methods: - params = method.inMessage.params - returns = method.outMessage.params - - for name,param in params: - param.add_to_schema(schema_entries, nsmap) - - if returns: - returns[0][1].add_to_schema(schema_entries, nsmap) - - method.inMessage.add_to_schema(schema_entries, nsmap) - method.outMessage.add_to_schema(schema_entries, nsmap) - - - schemaNode = create_xml_subelement(types, "schema") - schemaNode.set("targetNamespace", self.__tns__) - schemaNode.set("xmlns", "http://www.w3.org/2001/XMLSchema") - - for xxx, node in schema_entries.items(): - schemaNode.append(node) - - return schemaNode - - def _add_messages_for_methods(self, root, methods, nsmap): - ''' - A private method for adding message elements to the wsdl - @param the the root element of the wsdl - @param the list of methods. - ''' - messages = [] - #make messages - for method in methods: - methodName = method.name - # making in part - inMessage = create_xml_element('message', nsmap) - inMessage.set('name',method.inMessage.typ) - - if len(method.inMessage.params) > 0: - inPart = create_xml_subelement(inMessage,'part') - inPart.set('name',method.inMessage.name) - inPart.set('element', 'tns:' + method.inMessage.typ) - - messages.append(inMessage) - - # making out part - outMessage = create_xml_element('message', nsmap) - outMessage.set('name',method.outMessage.typ) - if len(method.outMessage.params) > 0: - outPart = create_xml_subelement(outMessage,'part') - outPart.set('name', method.outMessage.name) - outPart.set('element', 'tns:' + method.outMessage.typ) - messages.append(outMessage) - - for message in messages: - root.append(message) - - - def _add_bindings_for_methods(self, root, serviceName, methods, nsmap): - ''' - A private method for adding bindings to the wsdld - @param the root element of the wsdl - @param the name of this service - @param the methods to be add to the binding node - ''' - hasCallbacks = self._hasCallbacks() - - # make binding - binding = create_xml_subelement(root,'binding') - binding.set('name',serviceName) - binding.set('type','tns:%s'%serviceName) - - sbinding = create_xml_subelement(binding, nsmap.get('soap') + 'binding') - sbinding.set('style','document') - sbinding.set('transport','http://schemas.xmlsoap.org/soap/http') - - if hasCallbacks: - callbackBinding = create_xml_subelement(root,'binding') - callbackBinding.set('name','%sCallback'%serviceName) - callbackBinding.set('type','typens:%sCallback'%serviceName) - - sbinding = create_xml_subelement(callbackBinding, nsmap.get('soap') + 'binding') - sbinding.set('transport','http://schemas.xmlsoap.org/soap/http') - - for method in methods: - operation = create_xml_element('operation', nsmap) - operation.set('name',method.name) - - soapOperation = create_xml_subelement(operation, nsmap.get('soap') + 'operation') - soapOperation.set('soapAction',method.soapAction) - - soapOperation.set('style','document') - - input = create_xml_subelement(operation,'input') - input.set('name',method.inMessage.typ) - soapBody = create_xml_subelement(input, nsmap.get('soap') + 'body') - soapBody.set('use','literal') - - if method.outMessage.params != None and not method.isAsync and not method.isCallback: - output = create_xml_subelement(operation,'output') - output.set('name',method.outMessage.typ) - soapBody = create_xml_subelement(output, nsmap.get('soap') + 'body') - soapBody.set('use','literal') - - if method.isCallback: - relatesTo = create_xml_subelement(input, nsmap.get('soap') + 'header') - relatesTo.set('message','tns:RelatesToHeader') - relatesTo.set('part','RelatesTo') - relatesTo.set('use','literal') - - callbackBinding.append(operation) - else: - if method.isAsync: - rtHeader = create_xml_subelement(input, nsmap.get('soap') + 'header') - rtHeader.set('message','tns:ReplyToHeader') - rtHeader.set('part','ReplyTo') - rtHeader.set('use','literal') - - midHeader = create_xml_subelement(input, nsmap.get('soap') + 'header') - midHeader.set('message','tns:MessageIDHeader') - midHeader.set('part','MessageID') - midHeader.set('use','literal') - - binding.append(operation) diff -Nru python-soaplib-0.8.1/soaplib/soap.py python-soaplib-0.9.3-alpha3/soaplib/soap.py --- python-soaplib-0.8.1/soaplib/soap.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/soap.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,446 +0,0 @@ -from soaplib.xml import create_xml_element, create_xml_subelement -from soaplib.xml import NamespaceLookup, ElementTree -from soaplib.serializers.primitive import Fault -from soaplib.serializers.binary import Attachment - -try: - #python 2.5 - from email.mime.multipart import MIMEMultipart - from email.mime.application import MIMEApplication - from email.encoders import encode_7or8bit -except: - #python 2.4 - from email.MIMENonMultipart import MIMENonMultipart - from email.MIMEMultipart import MIMEMultipart - from email.Encoders import encode_7or8bit - -from email import message_from_string - - -from base64 import b64encode -from StringIO import StringIO - -class Message(object): - def __init__(self,name,params,ns=None,typ=None): - self.name = name - self.params = params - if typ == None: - typ = name - self.typ = typ - self.ns = ns - - def to_xml(self,*data): - if len(self.params): - if len(data) != len(self.params): - raise Exception("Parameter number mismatch expected [%s] got [%s]"%(len(self.params),len(data))) - - nsmap = NamespaceLookup(self.ns) - element = create_xml_element(self.name, nsmap, self.ns) - - for i in range(0,len(self.params)): - name, serializer = self.params[i] - d = data[i] - e = serializer.to_xml(d, name, nsmap) - if type(e) in (list,tuple): - elist = e - for e in elist: - element.append(e) - elif e == None: - pass - else: - element.append(e) - - ElementTree.cleanup_namespaces(element) - return element - - def from_xml(self,element): - results = [] - try: - children = element.getchildren() - except: - return [] - - def findall(name): - # inner method for finding child node - nodes = [] - for c in children: - if c.tag.split('}')[-1] == name: - nodes.append(c) - return nodes - - for name, serializer in self.params: - childnodes = findall(name) - if len(childnodes) == 0: - results.append(None) - else: - results.append(serializer.from_xml(*childnodes)) - return results - - def add_to_schema(self,schemaDict, nsmap): - complexType = create_xml_element(nsmap.get('xs') + 'complexType', nsmap) - complexType.set('name',self.typ) - - sequence = create_xml_subelement(complexType, nsmap.get('xs') + 'sequence') - if self.params: - for name,serializer in self.params: - e = create_xml_subelement(sequence, nsmap.get('xs') + 'element') - e.set('name',name) - e.set('type', - "%s:%s" % (serializer.get_namespace_id(), serializer.get_datatype())) - - element = create_xml_element(nsmap.get('xs') + 'element', nsmap) - element.set('name',self.typ) - element.set('type','%s:%s' % ('tns',self.typ)) - - schemaDict[self.typ] = complexType - schemaDict[self.typ+'Element'] = element - -class MethodDescriptor: - ''' - This class represents the method signature of a soap method, and is returned - by the soapdocument, or soapmethod decorators. - ''' - - def __init__(self, name, soapAction, inMessage, outMessage, doc, isCallback=False, isAsync=False, mtom=False): - self.inMessage = inMessage - self.outMessage = outMessage - self.soapAction = soapAction - self.name = name - self.isCallback = isCallback - self.isAsync = isAsync - self.doc = doc - self.mtom = mtom - -def from_soap(xml_string): - ''' - Parses the xml string into the header and payload - ''' - root, xmlids = ElementTree.XMLID(xml_string) - if xmlids: - resolve_hrefs(root,xmlids) - body = None - header = None - - # find the body and header elements - for e in root.getchildren(): - name = e.tag.split('}')[-1].lower() - if name == 'body': - body = e - elif name == 'header': - header = e - payload = None - if len(body.getchildren()): - payload = body.getchildren()[0] - - return payload, header - -def resolve_hrefs(element,xmlids): - for e in element: - if e.get('id'): - continue # don't need to resolve this element - elif e.get('href'): - resolved_element = xmlids[e.get('href').replace('#','')] - if resolved_element is None: continue - resolve_hrefs(resolved_element,xmlids) - - [e.set(k,v) for k,v in resolved_element.items()] # copies the attributes - [e.append(child) for child in resolved_element.getchildren()] # copies the children - - else: - resolve_hrefs(e,xmlids) - return element - - -def make_soap_envelope(message, tns='', header_elements=None): - ''' - This method takes the results from a soap method call, and wraps them - in the appropriate soap envelope with any specified headers - - @param the message of the soap envelope, either an element or list of elements - @param any header elements to be included in the soap response - @returns the envelope element - ''' - nsmap = NamespaceLookup(tns) - - envelope = create_xml_element(nsmap.get('SOAP-ENV') + 'Envelope', nsmap, tns) - - if header_elements: - headerElement = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Header') - - for h in header_elements: - headerElement.append(h) - - body = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Body') - - if type(message) == list: - for m in message: - body.append(m) - elif message != None: - body.append(message) - - return envelope - -def join_attachment(id, envelope, payload, prefix=True): - ''' - Helper function for swa_to_soap. - - Places the data from an attachment back into a SOAP message, replacing - its xop:Include element or href. - - @param id content-id or content-location of attachment - @param prefix Set this to true if id is content-id or false if - it is content-location. It prefixes a "cid:" to - the href value. - @param envelope soap envelope string to be operated on - @param payload attachment data - @return tuple of length 2 with the new message and the - number of replacements made - ''' - - # grab the XML element of the message in the SOAP body - soapmsg = StringIO(envelope) - soaptree = ElementTree.parse(soapmsg) - soapns = soaptree.getroot().tag.split('}')[0].strip('{') - soapbody = soaptree.getroot().find("{%s}Body" % soapns) - message = None - for child in list(soapbody): - if child.tag != "%sFault" % (soapns,): - message = child - break - - numreplaces = 0 - idprefix = '' - if prefix == True: idprefix = "cid:" - id = "%s%s" % (idprefix, id, ) - - # Make replacement. - for param in message: - # Look for Include subelement. - for sub in param: - if sub.tag.split('}')[-1] == 'Include' and \ - sub.attrib.get('href') == id: - param.remove(sub) - param.text = payload - numreplaces += 1 - if numreplaces < 1 and param.attrib.get('href') == id: - del(param.attrib['href']) - param.text = payload - numreplaces += 1 - - soapmsg.close() - soapmsg = StringIO() - soaptree.write(soapmsg) - joinedmsg = soapmsg.getvalue() - soapmsg.close() - - return (joinedmsg, numreplaces) - -def collapse_swa(content_type, envelope): - ''' - Translates an SwA multipart/related message into an application/soap+xml message. - - References: - SwA http://www.w3.org/TR/SOAP-attachments - XOP http://www.w3.org/TR/xop10/ - MTOM http://www.w3.org/TR/soap12-mtom/ - http://www.w3.org/Submission/soap11mtom10/ - - @param content_type value of the Content-Type header field - @param envelope body of the HTTP message, a soap envelope - @return appication/soap+xml version of the given HTTP body - ''' - # convert multipart messages back to pure SOAP - mime_type = content_type.lower().split(';') - if 'multipart/related' not in mime_type: - return envelope - - # parse the body into an email.Message object - msgString = "MIME-Version: 1.0\r\n" \ - "Content-Type: %s\r\n" % ( - content_type, ) - msgString += "\r\n" + envelope - msg = message_from_string(msgString) # our message - - soapmsg = None - root = msg.get_param('start') - - # walk through sections, reconstructing pure SOAP - for part in msg.walk(): - # skip the multipart container section - if part.get_content_maintype() == 'multipart': - continue - - # detect main soap section - if (part.get('Content-ID') and part.get('Content-ID') == root) or \ - (root == None and part == msg.get_payload()[0]): - soapmsg = part.get_payload() - continue - - # binary packages - cte = part.get("Content-Transfer-Encoding") - payload = None - if cte != 'base64': - payload = b64encode(part.get_payload()) - else: - payload = part.get_payload() - cid = part.get("Content-ID").strip("<>") - cloc = part.get("Content-Location") - numreplaces = None - - # Check for Content-ID and make replacement - if cid: - soapmsg, numreplaces = join_attachment(cid, soapmsg, payload) - - # Check for Content-Location and make replacement - if cloc and not cid and not numreplaces: - soapmsg, numreplaces = join_attachment(cloc,soapmsg,payload,False) - - return soapmsg - -def apply_mtom(headers, envelope, params, paramvals): - ''' - Apply MTOM to a SOAP envelope, separating attachments into a - MIME multipart message. - - References: - XOP http://www.w3.org/TR/xop10/ - MTOM http://www.w3.org/TR/soap12-mtom/ - http://www.w3.org/Submission/soap11mtom10/ - - @param headers Headers dictionary of the SOAP message that would - originally be sent. - @param envelope SOAP envelope string that would have originally been sent. - @param params params attribute from the Message object used for the SOAP - @param paramvals values of the params, passed to Message.to_xml - @return tuple of length 2 with dictionary of headers and - string of body that can be sent with HTTPConnection - ''' - - # grab the XML element of the message in the SOAP body - soapmsg = StringIO(envelope) - soaptree = ElementTree.parse(soapmsg) - soapns = soaptree.getroot().tag.split('}')[0].strip('{') - soapbody = soaptree.getroot().find("{%s}Body" % soapns) - message = None - for child in list(soapbody): - if child.tag != "%sFault" % (soapns,): - message = child - break - - # Get additional parameters from original Content-Type - ctarray = [] - for n, v in headers.items(): - if n.lower() == 'content-type': - ctarray = v.split(';') - break - roottype = ctarray[0].strip() - rootparams = {} - for ctparam in ctarray[1:]: - n, v = ctparam.strip().split('=') - rootparams[n] = v.strip("\"'") - - # Set up initial MIME parts - mtompkg = MIMEMultipart('related',boundary='?//<><>soaplib_MIME_boundary<>') - rootpkg = None - try: - rootpkg = MIMEApplication(envelope,'xop+xml', encode_7or8bit) - except NameError: - rootpkg = MIMENonMultipart("application", "xop+xml") - rootpkg.set_payload(envelope) - encode_7or8bit(rootpkg) - - # Set up multipart headers. - del(mtompkg['mime-version']) - mtompkg.set_param('start-info',roottype) - mtompkg.set_param('start','') - if headers.has_key('SOAPAction'): - mtompkg.add_header('SOAPAction', headers.get('SOAPAction')) - - # Set up root SOAP part headers - del(rootpkg['mime-version']) - rootpkg.add_header('Content-ID','') - for n, v in rootparams.items(): - rootpkg.set_param(n,v) - rootpkg.set_param('type',roottype) - - mtompkg.attach(rootpkg) - - # Extract attachments from SOAP envelope. - for i in range(len(params)): - name, typ = params[i] - if typ == Attachment: - id = "soaplibAttachment_%s" % (len(mtompkg.get_payload()),) - param = message[i] - param.text = "" - incl = create_xml_subelement( - param, - "{http://www.w3.org/2004/08/xop/include}Include" - ) - incl.attrib["href"] = "cid:%s" % id - if paramvals[i].fileName and not paramvals[i].data: - paramvals[i].load_from_file() - data = paramvals[i].data - attachment = None - try: - attachment = MIMEApplication(data, _encoder=encode_7or8bit) - except NameError: - attachment = MIMENonMultipart("application", "octet-stream") - attachment.set_payload(data) - encode_7or8bit(attachment) - del(attachment['mime-version']) - attachment.add_header('Content-ID','<%s>'%(id,)) - mtompkg.attach(attachment) - - # Update SOAP envelope. - soapmsg.close() - soapmsg = StringIO() - soaptree.write(soapmsg) - rootpkg.set_payload(soapmsg.getvalue()) - soapmsg.close() - - # extract body string from MIMEMultipart message - bound = '--%s' % (mtompkg.get_boundary(),) - marray = mtompkg.as_string().split(bound) - mtombody = bound - mtombody += bound.join(marray[1:]) - - # set Content-Length - mtompkg.add_header("Content-Length", str(len(mtombody))) - - # extract dictionary of headers from MIMEMultipart message - mtomheaders = {} - for name, value in mtompkg.items(): - mtomheaders[name] = value - - if len(mtompkg.get_payload()) <= 1: - return (headers, envelope) - - return (mtomheaders, mtombody) - -def make_soap_fault(faultString, faultCode = 'Server', detail = None, - header_elements = None): - ''' - This method populates a soap fault message with the provided - fault string and details. - @param faultString the short description of the error - @param detail the details of the exception, such as a stack trace - @param faultCode defaults to 'Server', but can be overridden - @param header_elements A list of XML elements to add to the fault header. - @returns the element corresponding to the fault message - ''' - - nsmap = NamespaceLookup() - envelope = create_xml_element(nsmap.get('SOAP-ENV') + 'Envelope', nsmap) - - if header_elements: - header = create_xml_subelement( - envelope, nsmap.get('SOAP-ENV') + 'Header') - for element in header_elements: - header.append(element) - - body = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Body') - - f = Fault(faultCode,faultString,detail) - body.append(Fault.to_xml(f, nsmap.get('SOAP-ENV') + "Fault", nsmap)) - - return envelope diff -Nru python-soaplib-0.8.1/soaplib/util.py python-soaplib-0.9.3-alpha3/soaplib/util.py --- python-soaplib-0.8.1/soaplib/util.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/util.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,209 +0,0 @@ -import httplib -import datetime -import urllib -from urllib import quote -from soaplib.etimport import ElementTree - -def create_relates_to_header(relatesTo,attrs={}): - '''Creates a 'relatesTo' header for async callbacks''' - relatesToElement = ElementTree.Element('{http://schemas.xmlsoap.org/ws/2003/03/addressing}RelatesTo') - for k,v in attrs.items(): - relatesToElement.set(k,v) - relatesToElement.text = relatesTo - return relatesToElement - -def create_callback_info_headers(messageId,replyTo): - '''Creates MessageId and ReplyTo headers for initiating an async function''' - messageIdElement = ElementTree.Element('{http://schemas.xmlsoap.org/ws/2003/03/addressing}MessageID') - messageIdElement.text = messageId - - replyToElement = ElementTree.Element('{http://schemas.xmlsoap.org/ws/2003/03/addressing}ReplyTo') - addressElement = ElementTree.SubElement(replyToElement,'{http://schemas.xmlsoap.org/ws/2003/03/addressing}Address') - addressElement.text = replyTo - return messageIdElement, replyToElement - -def get_callback_info(): - '''Retrieves the messageId and replyToAddress from the message header. - This is used for async calls.''' - messageId = None - replyToAddress = None - from soaplib.wsgi_soap import request - headerElement = request.header - if headerElement: - headers = headerElement.getchildren() - for header in headers: - if header.tag.lower().endswith("messageid"): - messageId = header.text - if header.tag.lower().find("replyto") != -1: - replyToElems = header.getchildren() - for replyTo in replyToElems: - if replyTo.tag.lower().endswith("address"): - replyToAddress = replyTo.text - return messageId, replyToAddress - -def get_relates_to_info(): - '''Retrives the relatesTo header. This is used for callbacks''' - from soaplib.wsgi_soap import request - - headerElement = request.header - if headerElement: - headers = headerElement.getchildren() - for header in headers: - if header.tag.lower().find('relatesto') != -1: - return header.text - -def split_url(url): - '''Splits a url into (uri_scheme, host[:port], path)''' - scheme, remainder = urllib.splittype(url) - host, path = urllib.splithost(remainder) - return scheme.lower(), host, path - -def reconstruct_url(environ): - ''' - Rebuilds the calling url from values found in the - environment. - - This algorithm was found via PEP 333, the wsgi spec and - contributed by Ian Bicking. - ''' - url = environ['wsgi.url_scheme']+'://' - if environ.get('HTTP_HOST'): - url += environ['HTTP_HOST'] - else: - url += environ['SERVER_NAME'] - - if environ['wsgi.url_scheme'] == 'https': - if environ['SERVER_PORT'] != '443': - url += ':' + environ['SERVER_PORT'] - else: - if environ['SERVER_PORT'] != '80': - url += ':' + environ['SERVER_PORT'] - - if quote(environ.get('SCRIPT_NAME','')) == '/' and quote(environ.get('PATH_INFO',''))[0:1] == '/': - #skip this if it is only a slash - pass - elif quote(environ.get('SCRIPT_NAME',''))[0:2] == '//': - url += quote(environ.get('SCRIPT_NAME',''))[1:] - else: - url += quote(environ.get('SCRIPT_NAME','')) - - url += quote(environ.get('PATH_INFO','')) - if environ.get('QUERY_STRING'): - url += '?' + environ['QUERY_STRING'] - return url - - -################################################################### -# Deprecated Functionality -################################################################### -from warnings import warn -def deprecate(name): - warn("Method [%s] will be removed at the end of this iteration"%name,DeprecationWarning) - -def convertDateTime(date): - deprecate('convertDateTime') - date = date.replace("T"," ") - d, t = date.split(' ') - y,mo,da = d.split('-') - h,mi,s = t.split(':') - ms = 0 - try: s,ms = s.split('.') - except: pass - return datetime.datetime(int(y),int(mo),int(da),int(h),int(mi),int(s),int(ms)) - -converters = { - 'datetime':convertDateTime, - 'integer':int, - 'float':float, - 'boolean':bool, -} - -def element2dict(element): - deprecate('element2dict') - if type(element) == str: - element = ElementTree.fromstring(element) - - children = element.getchildren() - tag = element.tag.split('}')[-1] - return {tag:_element2dict(children)} - -def _get_element_value(element): - deprecate('_get_element_value') - xsd_type = None - for k in element.keys(): - if k.lower().endswith('type'): - xsd_type = element.get(k) - if element.text == None: - return None - if xsd_type: - t = xsd_type.lower().split(':')[-1] - conv = converters.get(t) - if conv: return conv(element.text) - else: return element.text - return element.text - -def _element2dict(child_elements): - deprecate('_element2dict') - d = {} - for child in child_elements: - - tag = child.tag.split('}')[-1] - children = child.getchildren() - if children: - typ = None - for k in child.keys(): - if k.lower().endswith('type'): - typ = child.get(k) - if typ and typ.lower().endswith('array'): - d[tag] = [] - for c in child.getchildren(): - if c.getchildren(): - d[tag].append(_element2dict(c.getchildren())) - else: - d[tag].append(_get_element_value(c)) - else: - d[tag] = _element2dict(children) - else: - typ = None - for k in child.keys(): - if k.lower().endswith('type'): - typ = child.get(k) - value = _get_element_value(child) - d[tag] = _get_element_value(child) - return d - - -def dict2element(*args,**kwargs): - deprecate('dict2element') - if len(kwargs) == 1: - dictionary = kwargs - else: - dictionary = args[0] - if not len(dictionary.keys()): - return ElementTree.Element('none') - root = dictionary.keys()[0] - element = _dict2element(dictionary[root],root) - element.set('xmlns:optio','http://www.optio.com/schemas') - return element - -def _dict2element(data,tag): - deprecate('_dict2element') - d = { datetime.datetime:'xs:dateTime', - int:'xs:integer', - bool:'xs:boolean', - float:'xs:float', - } - root = ElementTree.Element(tag) - if type(data) == dict: - for k,v in data.items(): - root.append(_dict2element(v,k)) - elif type(data) == list or type(data) == tuple: - root.set('type','optio:array') - for item in data: - root.append(_dict2element(item,'item')) - elif data is not None: - t = d.get(type(data),'xs:string') - root.text = str(data) - root.set('type',t) - return root - diff -Nru python-soaplib-0.8.1/soaplib/wsgi_soap.py python-soaplib-0.9.3-alpha3/soaplib/wsgi_soap.py --- python-soaplib-0.8.1/soaplib/wsgi_soap.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/wsgi_soap.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,354 +0,0 @@ -import cStringIO -import traceback - -from soaplib.soap import make_soap_envelope, make_soap_fault, from_soap, collapse_swa, apply_mtom -from soaplib.service import SoapServiceBase -from soaplib.util import reconstruct_url -from soaplib.serializers.primitive import string_encoding, Fault -from soaplib.etimport import ElementTree -from threading import local - -request = local() - -_exceptions = False -_exception_logger = None - -_debug = False -_debug_logger = None - -################################################################### -# Logging / Debugging Utilities # -# replace with python logger? # -################################################################### - -def _dump(e): # util? - print e - -def log_exceptions(on, out=_dump): - global _exceptions - global _exception_logger - - _exceptions=on - _exception_logger=out - -def log_debug(on,out=_dump): - global _debug - global _debug_logger - - _debug=on - _debug_logger=out - -def debug(msg): - global _debug - global _debug_logger - - if _debug: - _debug_logger(msg) - -def exceptions(msg): - global _exceptions - global _exception_logger - - if _exceptions: - _exception_logger(msg) - -def reset_request(): - ''' - This method clears the data stored in the threadlocal - request object - ''' - request.environ = None - request.header = None - request.additional = {} - -class WSGISoapApp(object): - ''' - This is the base object representing a soap web application, and conforms - to the WSGI specification (PEP 333). This object should be overridden - and getHandler(environ) overridden to provide the object implementing - the specified functionality. Hooks have been added so that the subclass - can react to various events that happen durring the execution of the - request. - ''' - - def onCall(self,environ): - ''' - This is the first method called when this WSGI app is invoked - @param the wsgi environment - ''' - pass - - def onWsdl(self,environ,wsdl): - ''' - This is called when a wsdl is requested - @param the wsgi environment - @param the wsdl string - ''' - pass - - def onWsdlException(self,environ,exc,resp): - ''' - Called when an exception occurs durring wsdl generation - @param the wsgi environment - @param exc the exception - @param the fault response string - ''' - pass - - def onMethodExec(self,environ,body,py_params,soap_params): - ''' - Called BEFORE the service implementing the functionality is called - @param the wsgi environment - @param the body element of the soap request - @param the tuple of python params being passed to the method - @param the soap elements for each params - ''' - pass - - def onResults(self,environ,py_results,soap_results): - ''' - Called AFTER the service implementing the functionality is called - @param the wsgi environment - @param the python results from the method - @param the xml serialized results of the method - ''' - pass - - def onException(self,environ,exc,resp): - ''' - Called when an error occurs durring execution - @param the wsgi environment - @param the exception - @param the response string - ''' - pass - - def onReturn(self,environ,returnString): - ''' - Called before the application returns - @param the wsgi environment - @param return string of the soap request - ''' - pass - - def getHandler(self,environ): - ''' - This method returns the object responsible for processing a given request, and - needs to be overridden by a subclass to handle the application specific - mapping of the request to the appropriate handler. - @param the wsgi environment - @returns the object to be called for the soap operation - ''' - raise Exception("Not implemented") - - def __call__(self, environ, start_response, address_url=None): - ''' - This method conforms to the WSGI spec for callable wsgi applications (PEP 333). - This method looks in environ['wsgi.input'] for a fully formed soap request envelope, - will deserialize the request parameters and call the method on the object returned - by the getHandler() method. - @param the http environment - @param a callable that begins the response message - @returns the string representation of the soap call - ''' - methodname = '' - try: - reset_request() - request.environ = environ - - # implementation hook - self.onCall(environ) - - serviceName = environ['PATH_INFO'].split('/')[-1] - service = self.getHandler(environ) - if (environ['QUERY_STRING'].endswith('wsdl') or environ['PATH_INFO'].endswith('wsdl')) and environ['REQUEST_METHOD'].lower() == 'get': - # get the wsdl for the service - # - # Assume path_info matches pattern - # /stuff/stuff/stuff/serviceName.wsdl or ?WSDL - # - serviceName = serviceName.split('.')[0] - if address_url: - url = address_url - else: - url = reconstruct_url(environ).split('.wsdl')[0] - - start_response('200 OK',[('Content-type','text/xml')]) - try: - wsdl_content = service.wsdl(url) - - # implementation hook - self.onWsdl(environ,wsdl_content) - except Exception, e: - - # implementation hook - buffer = cStringIO.StringIO() - traceback.print_exc(file=buffer) - buffer.seek(0) - stacktrace = str(buffer.read()) - faultStr = ElementTree.tostring(make_soap_fault( str(e), detail=stacktrace), encoding=string_encoding) - - exceptions(faultStr) - - self.onWsdlException(environ,e,faultStr) - - # initiate the response - start_response('500',[('Content-type','text/xml'),('Content-length',str(len(faultStr)))]) - return [faultStr] - - reset_request() - return [wsdl_content] - - if environ['REQUEST_METHOD'].lower() != 'post': - start_response('405 Method Not Allowed',[('Allow','POST')]) - return '' - - input = environ.get('wsgi.input') - length = environ.get("CONTENT_LENGTH") - body = input.read(int(length)) - debug(body) - body = collapse_swa( environ.get("CONTENT_TYPE"), body) - - # deserialize the body of the message - try: - payload, header = from_soap(body) - except SyntaxError,e: - payload = None - header = None - - if payload: - methodname = payload.tag.split('}')[-1] - else: - # check HTTP_SOAPACTION - methodname = environ.get("HTTP_SOAPACTION") - if methodname.startswith('"') and methodname.endswith('"'): - methodname = methodname[1:-1] - if methodname.find('/') >0: - methodname = methodname.split('/')[1] - - request.header = header - - # call the method - func = getattr(service, methodname) - - # retrieve the method descriptor - descriptor = func(_soap_descriptor=True, klazz=service.__class__) - if payload: - params = descriptor.inMessage.from_xml(*[payload]) - else: - params = () - # implementation hook - self.onMethodExec(environ,body,params,descriptor.inMessage.params) - - # call the method - retval = func(*params) - - # transform the results into an element - # only expect a single element - results = None - if not (descriptor.isAsync or descriptor.isCallback): - results = descriptor.outMessage.to_xml(*[retval]) - - # implementation hook - self.onResults(environ,results,retval) - - # grab any headers that were included in the request - response_headers = None - if hasattr(request,'response_headers'): - response_headers = request.response_headers - - # construct the soap response, and serialize it - envelope = make_soap_envelope(results,tns=service.__tns__,header_elements=response_headers) - ElementTree.cleanup_namespaces(envelope) - resp = ElementTree.tostring(envelope, encoding=string_encoding) - headers = {'Content-Type': 'text/xml'} - - if descriptor.mtom: - headers, resp = apply_mtom( headers, resp, - descriptor.outMessage.params, - (retval,) ) - - - if environ.has_key('CONTENT_LENGTH'): - del(environ['CONTENT_LENGTH']) - - # initiate the response - start_response('200 OK',headers.items()) - - self.onReturn(environ,resp) - - debug(resp) - - # return the serialized results - reset_request() - return [resp] - - except Fault,e: - - # grab any headers that were included in the request - response_headers = None - if hasattr(request,'response_headers'): - response_headers = request.response_headers - - # The user issued a Fault, so handle it just like an exception! - fault = make_soap_fault( - e.faultstring, - e.faultcode, - e.detail, - header_elements = response_headers) - - faultStr = ElementTree.tostring(fault, encoding=string_encoding) - exceptions(faultStr) - - self.onException(environ,e,faultStr) - reset_request() - - # initiate the response - start_response('500 Internal Server Error',[('Content-type','text/xml')]) - return [faultStr] - - except Exception, e: - # Dump the stack trace to a buffer to be sent - # back to the caller - - # capture stacktrace - buffer = cStringIO.StringIO() - traceback.print_exc(file=buffer) - buffer.seek(0) - stacktrace = str(buffer.read()) - - faultstring = str(e) - if methodname: - faultcode = faultCode='%sFault'%methodname - else: - faultcode = 'Server' - detail = stacktrace - - faultStr = ElementTree.tostring(make_soap_fault(faultstring,faultcode,detail), encoding=string_encoding) - exceptions(faultStr) - - self.onException(environ,e,faultStr) - reset_request() - - # initiate the response - start_response('500 Internal Server Error',[('Content-type','text/xml')]) - return [faultStr] - -class SimpleWSGISoapApp(WSGISoapApp, SoapServiceBase): - ''' - This object is a VERY simple extention of the base WSGISoapApp. - It subclasses both WSGISoapApp, and SoapServiceBase, so that - an object can simply subclass this single object, and it will - be both a wsgi application and a soap service. This is convenient - if you want to only expose some functionality, and dont need - complex handler mapping, and all of the functionality can be put - in a single class. - ''' - def __init__(self): - WSGISoapApp.__init__(self) - SoapServiceBase.__init__(self) - - def getHandler(self,environ): - return self - - diff -Nru python-soaplib-0.8.1/soaplib/xml.py python-soaplib-0.9.3-alpha3/soaplib/xml.py --- python-soaplib-0.8.1/soaplib/xml.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib/xml.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -from soaplib.etimport import ElementTree - -class NamespaceLookup(object): - ''' - Class to manage XML namespaces - ''' - - def __init__(self, tns = None, wsdl_map = False): - self.nsmap = { - 'xs': 'http://www.w3.org/2001/XMLSchema', - 'xsi': 'http://www.w3.org/1999/XMLSchema-instance', - 'plnk':'http://schemas.xmlsoap.org/ws/2003/05/partner-link/', - } - if wsdl_map: - self.nsmap['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/' - self.nsmap['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/' - else: - self.nsmap['SOAP-ENC'] \ - = 'http://schemas.xmlsoap.org/soap/encoding/' - self.nsmap['SOAP-ENV'] \ - = 'http://schemas.xmlsoap.org/soap/envelope/' - if tns is not None: - self.nsmap['tns'] = tns - self.nsmap['typens'] = tns - - def get_all(self): - ''' - Return all namespaces - ''' - return self.nsmap - - def get(self, key): - ''' - Lookup and return a given namespace - ''' - ns = self.nsmap[key] if key in self.nsmap else '' - return "{%s}" % ns - - def set(self, key, ns): - ''' - Add a namespace to the map (replaces) - ''' - self.nsmap[key] = ns - -''' -Default namespace lookup -''' -ns = NamespaceLookup() - - -def qualify(name, ns): - ''' - Qualify an idenifier with a namespace - ''' - return "{%s}%s" % (ns, name) - - -def create_xml_element(name, nslookup, default_ns=None): - ''' - Factory method to create a new XML element - @param default_ns The default namespace to use for the element. - @param extended_map A mapping of any additional namespaces to add. - ''' - namespace_map = { None: default_ns } if default_ns is not None else {} - for key, value in nslookup.get_all().iteritems(): - if value != default_ns: - namespace_map[key] = value - return ElementTree.Element(name, nsmap=namespace_map) - - -def create_xml_subelement(parent, name): - ''' - Factory method to create a new XML subelement - ''' - if not name.startswith("{") and None in parent.nsmap: - name = qualify(name, parent.nsmap[None]) - return ElementTree.SubElement(parent, name) - diff -Nru python-soaplib-0.8.1/soaplib_docs.markdown python-soaplib-0.9.3-alpha3/soaplib_docs.markdown --- python-soaplib-0.8.1/soaplib_docs.markdown 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/soaplib_docs.markdown 1970-01-01 00:00:00.000000000 +0000 @@ -1,452 +0,0 @@ -Soaplib Documentation -===================== - -Table of Contents ------------------ -1. Overview - * What is soaplib? - * Requirements -2. Intro Examples - * HelloWorld - * UserManager -3. Serializers - * Primitive - * Collections - * Class - * Fault - * Binary - * Any - * Custom -4. Client -5. Message API -6. WSGI - * Deployment - * Hooks - * servers -7. Async. Web Services - * Correlation -8. Interoperability - * Axis - * .NET -9. wsdl2py -10. security - * none - -Overview -======== - -What is soaplib? ----------------- - -Soaplib is an easy to use python library written at -[Optio Software, Inc.](http://www.optio.com/) -for writing and calling soap web services. Writing soap web services in -any language has always been extremely difficult and yielded mixed -results. With a very small amount of code, soaplib allows you to write -a useful web service and deploy it as a WSGI application. WSGI is a python -web standard for writing portable, extendable web applications in python. -More information on WSGI can be found [here](http://wsgi.org/wsgi). - -Features --------- -* deploy services as WSGI applications -* handles all xml (de)serialization -* on-demand WSDL generation -* doesn't get in your way!!! - -Requirements ------------- -* Python 2.4 or greater (tested mainly on 2.4.3) -* [ElementTree](http://effbot.org/downloads/elementtree-1.2.6-20050316.tar.gz) (available through easy_install) -* [cElementTree](http://effbot.org/downloads/cElementTree-1.0.5-20051216.tar.gz) (available through easy_install) -* a WSGI-compliant web server (CherryPy, WSGIUtils, Flup, etc.) -* [pytz](http://pytz.sourceforge.net/)(available through easy_install) -* [easy_install](http://peak.telecommunity.com/dist/ez_setup.py) (optional) - -Intro Examples -============== - -HelloWorld ----------- - -1. Declaring a Soaplib Service - - from soaplib.wsgi_soap import SimpleWSGISoapApp - from soaplib.service import soapmethod - from soaplib.serializers.primitive import String, Integer, Array - - class HelloWorldService(SimpleWSGISoapApp): - @soapmethod(String,Integer,_returns=Array(String)) - def say_hello(self,name,times): - results = [] - for i in range(0,times): - results.append('Hello, %s'%name) - return results - - if __name__=='__main__': - from wsgiref.simple_server import make_server - server = make_server('localhost', 7789, HelloWorldService()) - server.serve_forever() - - Dissecting this example: - - `SimpleWSGISoapApp` is the base class for WSGI soap services. - - from soaplib.wsgi_soap import SimpleWSGISoapApp - - The `soapmethod` decorator exposes methods as soap method and declares the - data types it accepts and returns - - from soaplib.service import soapmethod - - Import the serializers to for this method (more on serializers later) - - from soaplib.serializers.primitive import String, Integer, Array - - Extending `SimpleWSGISoapApp` is an easy way to soap service that can - be deployed as a WSGI application. - - class HelloWorldService(SimpleWSGISoapApp): - - The `soapmethod` decorator flags each method as a soap method, - and defines the types and order of the soap parameters, as well - as the return value. This method takes in a `String`, an `Integer` - and returns an Array of Strings -> `Array(String)`. - - @soapmethod(String,Integer,_returns=Array(String)) - - The method itself has nothing special about it whatsoever. All - input variables and return types are standard python objects. - - def say_hello(self,name,times): - results = [] - for i in range(0,times): - results.append('Hello, %s'%name) - return results - -2. Deploying the service - - deploy this web service. Soaplib has been tested with several other web servers, - This example uses the reference WSGI web server (available in Python 2.5+) to - and any WSGI-compliant server *should* work. - - if __name__=='__main__': - from wsgiref.simple_server import make_server - server = make_server('localhost', 7789, HelloWorldService()) - server.serve_forever() - -3. Calling this service - - >>> from soaplib.client import make_service_client - >>> client = make_service_client('http://localhost:7789/',HelloWorldService()) - >>> print client.say_hello("Dave",5) - ['Hello, Dave','Hello, Dave','Hello, Dave','Hello, Dave','Hello, Dave'] - - `soaplib.client.make_service_client` is a utility method to construct a callable - client to the remote web service. `make_service_client` takes the url of the - remote functionality, as well as a _stub_ of the remote service. As in this case, - the _stub_ can be the instance of the remote functionality, however the requirements - are that it just have the same method signatures and definitions as the server - implementation. - -User Manager ------------- - -Lets try a more complicated example than just strings and integers! The following is -an extremely simple example using complex, nested data. - - from soaplib.wsgi_soap import SimpleWSGISoapApp - from soaplib.service import soapmethod - from soaplib.serializers.primitive import String, Integer, Array - from soaplib.serializers.clazz import ClassSerializer - - user_database = {} - userid_seq = 1 - - class Permission(ClassSerializer): - class types: - application = String - feature = String - - class User(ClassSerializer): - class types: - userid = Integer - username = String - firstname = String - lastname = String - permissions = Array(Permission) - - class UserManager(SimpleWSGISoapApp): - - @soapmethod(User,_returns=Integer) - def add_user(self,user): - global user_database - global userid_seq - user.userid = userid_seq - userid_seq = user_seq+1 - user_database[user.userid] = user - return user.userid - - @soapmethod(Integer,_returns=User) - def get_user(self,userid): - global user_database - return user_database[userid] - - @soapmethod(User) - def modify_user(self,user): - global user_database - user_database[user.userid] = user - - @soapmethod(Integer) - def delete_user(self,userid): - global user_database - del user_database[userid] - - @soapmethod(_returns=Array(User)) - def list_users(self): - global user_database - return [v for k,v in user_database.items()] - - if __name__=='__main__': - from wsgiref.simple_server import make_server - server = make_server('localhost', 7789, UserManager()) - server.start() - -Jumping into what's new: - - class Permission(ClassSerializer): - class types: - application = String - feature = String - - class User(ClassSerializer): - class types: - userid = Integer - username = String - firstname = String - lastname = String - permissions = Array(Permission) - -The `Permission` and `User` structures in the example are standard python objects -that extend `ClassSerializer`. The `ClassSerializer` uses an innerclass called -`types` to declare the attributes of this class. At instantiation time, a -metaclass is used to inspect the `types` and assigns the value of `None` to -each attribute of the `types` class to the new object. - - >>> u = User() - >>> u.username = 'jimbob' - >>> print u.userid - None - >>> u.firstname = 'jim' - >>> print u.firstname - jim - >>> - -Serializers -=========== -In soaplib, the serializers are the components responsible for converting indivdual -parameters to and from xml, as well as supply the information necessary to build the -wsdl. Soaplib has many built-in serializers that give you most of the common datatypes -generally needed. - -Primitives ----------- -The basic primitive types are `String`, `Integer`, `DateTime`, `Null`, `Float`, `Boolean`. These are some -of the most basic blocks within soaplib. - - >>> from soaplib.serializers.primitive import * - >>> import cElementTree as et - >>> element = String.to_xml('abcd','nodename') - >>> print et.tostring(element) - abcd - >>> print String.from_xml(element) - abcd - >>> String.get_datatype() - 'string' - >>> String.get_datatype(nsmap) - 'xs:string' - >>> - - -Collections ------------ -The two collections available in soaplib are `Array`s and `Map`s. Unlike the primitive -serializers, both of these serializers need to be instantiated with the proper internal -type so it can properly (de)serialize the data. All `Array`s and `Map`s are homogeneous, -meaning that the data they hold are all of the same type. For mixed typing or more dynamic -data, use the `Any` type. - - >>> from soaplib.serializers.primitive import * - >>> import cElementTree as et - >>> array_serializer = Array(String) - >>> element = array_serializer.to_xml(['a','b','c','d']) - >>> print et.tostring(element) - abcd - >>> print array_serializer.from_xml(element) - ['a', 'b', 'c', 'd'] - >>> - -Class ------ -The `ClassSerializer` is used to define and serialize complex, nested structures. - - >>> from soaplib.serializers.primitive import * - >>> import cElementTree as et - >>> from soaplib.serializers.clazz import * - >>> class Permission(ClassSerializer): - ... class types: - ... application = String - ... feature = String - >>> - >>> class User(ClassSerializer): - ... class types: - ... userid = Integer - ... username = String - ... firstname = String - ... lastname = String... - ... permissions = Array(Permission) - >>> - >>> u = User() - >>> u.username = 'bill' - >>> u.permissions = [] - >>> p = Permission() - >>> p.application = 'email' - >>> p.feature = 'send' - >>> u.permissions.append(p) - >>> element = User.to_xml(u) - >>> et.tostring(element) - 'billemailsend' - >>> User.from_xml(element).username - 'bill' - >>> - -Attachment ----------- -The `Attachment` serializer is used for transmitting binary data as base64 encoded strings. -Data in `Attachment` objects can be loaded manually, or read from file. All encoding of -the binary data is done just prior to the data being sent, and decoding immediately upon -receipt of the `Attachment`. - - >>> from soaplib.serializers.binary import Attachment - >>> import cElementTree as et - >>> a = Attachment(data='my binary data') - >>> element = Attachment.to_xml(a) - >>> print et.tostring(element) - bXkgYmluYXJ5IGRhdGE= - - >>> print Attachment.from_xml(element) - - >>> print Attachment.from_xml(element).data - my binary data - >>> a2 = Attachment(fileName='test.data') # load from file - - -Any ---- -The `Any` type is a serializer used to transmit unstructured xml data. `Any` types are very -useful for handling dynamic data, and provides a very pythonic way for passing data using -soaplib. The `Any` serializer does not perform any useful task because the data passed in -and returned are `Element` objects. The `Any` type's main purpose is to declare its presence -in the `wsdl`. - - -Custom ------- -Soaplib provides a very simple interface for writing custom serializers. Any object conforming -to the following interface can be used as a soaplib serializer. - - class MySerializer: - - def to_xml(self,value,name='retval',nsmap=None): - pass - - def from_xml(self,element): - pass - - def get_datatype(self,nsmap=None): - pass - - def get_namespace_id(self): - pass - - def add_to_schema(self,added_params,nsmap): - pass - -This feature is particularly useful when adapting soaplib to an existing project and converting existing -object to `ClassSerializers` is impractical. - -Client -====== -Soaplib provides a simple soap client to call remote soap implementations. Using the `ServiceClient` object -is the simplest way to make soap client requests. The `ServiceClient` uses an example or stub -implementation to know how to properly construct the soap messages. - - >>> from soaplib.client import make_service_client - >>> client = make_service_client('http://localhost:7789/',HelloWorldService()) - >>> print client.say_hello("Dave",5) - -This method provides the most straightforward method of creating a SOAP client using soaplib. - -Message API -=========== -TODO - -WSGI -==== -All soaplib services can be deployed as WSGI applications, and this gives soaplib great flexibility -with how they can be deployed. Any WSGI middleware layer can be put between the WSGI webserver and -the WSGI soap application. - -Deployment ----------- -Soaplib has been extensively used with [Paste](http://www.pythonpaste.org) for server configuration -and application composition. - -Hooks ------ -`WSGISoapApp`s have a set of extensible 'hooks' that can be implemented to capture different -events in the execution of the wsgi request. - - def onCall(self,environ): - '''This is the first method called when this WSGI app is invoked''' - pass - - def onWsdl(self,environ,wsdl): - '''This is called when a wsdl is requested''' - pass - - def onWsdlException(self,environ,exc,resp): - '''Called when an exception occurs durring wsdl generation''' - pass - - def onMethodExec(self,environ,body,py_params,soap_params): - '''Called BEFORE the service implementing the functionality is called''' - pass - - def onResults(self,environ,py_results,soap_results): - '''Called AFTER the service implementing the functionality is called''' - pass - - def onException(self,environ,exc,resp): - '''Called when an error occurs durring execution''' - pass - - def onReturn(self,environ,returnString): - '''Called before the application returns''' - pass - -These hooks are useful for transaction handling, logging and measuring performance. - -Servers -------- - -Soaplib services can be deployed as WSGI applications, in any WSGI-compliant -web server. Soaplib services have been successfully run on the following web -servers: - -* CherryPy 2.2 -* Flup -* Twisted.web2 -* WSGIUtils 0.9 - - diff -Nru python-soaplib-0.8.1/src/soaplib/__init__.py python-soaplib-0.9.3-alpha3/src/soaplib/__init__.py --- python-soaplib-0.8.1/src/soaplib/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/__init__.py 2010-08-25 17:58:12.000000000 +0000 @@ -0,0 +1,96 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +# namespace map + +ns_xsd = 'http://www.w3.org/2001/XMLSchema' +ns_xsi = 'http://www.w3.org/2001/XMLSchema-instance' +ns_plink = 'http://schemas.xmlsoap.org/ws/2003/05/partner-link/' +ns_soap = 'http://schemas.xmlsoap.org/wsdl/soap/' +ns_wsdl = 'http://schemas.xmlsoap.org/wsdl/' +ns_soap_enc = 'http://schemas.xmlsoap.org/soap/encoding/' +ns_soap_env = 'http://schemas.xmlsoap.org/soap/envelope/' +ns_soap12_env = 'http://www.w3.org/2003/05/soap-envelope/' +ns_soap12_enc = 'http://www.w3.org/2003/05/soap-encoding/' +ns_wsa = 'http://schemas.xmlsoap.org/ws/2003/03/addressing' +ns_xop = 'http://www.w3.org/2004/08/xop/include' + +nsmap = { + 'xs': ns_xsd, + 'xsi': ns_xsi, + 'plink': ns_plink, + 'soap': ns_soap, + 'wsdl': ns_wsdl, + 'senc': ns_soap_enc, + 'senv': ns_soap_env, + 's12env': ns_soap12_env, + 's12enc': ns_soap12_enc, + 'wsa': ns_wsa, + 'xop': ns_xop, +} + +# prefix map +prefmap = dict([(b,a) for a,b in nsmap.items()]) + +const_prefmap = dict(prefmap) +const_nsmap = dict(nsmap) + +_ns_counter = 0 +def get_namespace_prefix(ns): + global _ns_counter + + assert ns != "__main__" + assert ns != "soaplib.serializers.base" + + assert (isinstance(ns, str) or isinstance(ns, unicode)), ns + + if not (ns in prefmap): + pref = "s%d" % _ns_counter + while pref in nsmap: + _ns_counter += 1 + pref = "s%d" % _ns_counter + + prefmap[ns] = pref + nsmap[pref] = ns + + _ns_counter += 1 + + else: + pref = prefmap[ns] + + return pref + +def set_namespace_prefix(ns, pref): + if pref in nsmap and nsmap[pref] != ns: + for k,v in nsmap.items(): + print k,v + print + ns_old = nsmap[pref] + del prefmap[ns_old] + get_namespace_prefix(ns_old) + + for k,v in nsmap.items(): + print k,v + print + print + cpref = get_namespace_prefix(ns) + del nsmap[cpref] + + prefmap[ns] = pref + nsmap[pref] = ns diff -Nru python-soaplib-0.8.1/src/soaplib/serializers/base.py python-soaplib-0.9.3-alpha3/src/soaplib/serializers/base.py --- python-soaplib-0.8.1/src/soaplib/serializers/base.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/serializers/base.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,208 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import soaplib + +from lxml import etree + +def nillable_value(func): + def wrapper(cls, value, tns, *args, **kwargs): + if value is None: + return Null.to_xml(value, tns, *args, **kwargs) + return func(cls, value, tns, *args, **kwargs) + return wrapper + +def nillable_element(func): + def wrapper(cls, element): + if bool(element.get('{%s}nil' % soaplib.ns_xsi)): # or (element.text is None and len(element.getchildren()) == 0): + return None + return func(cls, element) + return wrapper + +def string_to_xml(cls, value, tns, name): + assert isinstance(value, str) or isinstance(value, unicode), "'value' must " \ + "be string or unicode. it is instead '%s'" % repr(value) + + retval = etree.Element("{%s}%s" % (tns,name)) + + retval.set('{%s}type' % soaplib.ns_xsi, cls.get_type_name_ns()) + retval.text = value + + return retval + +class Base(object): + __namespace__ = None + __type_name__ = None + + class Attributes(object): + nillable = True + min_occurs = 0 + max_occurs = 1 + + class Empty(object): + pass + + @classmethod + def is_default(cls): + return (cls.Attributes.nillable == Base.Attributes.nillable + and cls.Attributes.min_occurs == Base.Attributes.min_occurs + and cls.Attributes.max_occurs == Base.Attributes.max_occurs) + + @classmethod + def get_namespace_prefix(cls): + ns = cls.get_namespace() + + retval = soaplib.get_namespace_prefix(ns) + + return retval + + @classmethod + def get_namespace(cls): + return cls.__namespace__ + + @classmethod + def resolve_namespace(cls, default_ns): + if cls.__namespace__ in soaplib.const_prefmap and not cls.is_default(): + cls.__namespace__ = None + + if cls.__namespace__ is None: + cls.__namespace__ = cls.__module__ + + if (cls.__namespace__.startswith("soaplib") + or cls.__namespace__ == '__main__'): + cls.__namespace__ = default_ns + + @classmethod + def get_type_name(cls): + retval = cls.__type_name__ + if retval is None: + retval = cls.__name__.lower() + + return retval + + @classmethod + def get_type_name_ns(cls): + if cls.get_namespace() != None: + return "%s:%s" % (cls.get_namespace_prefix(), cls.get_type_name()) + + @classmethod + def to_xml(cls, value, tns, name='retval'): + return string_to_xml(cls, value, tns, name) + + @classmethod + def add_to_schema(cls, schema_entries): + ''' + Nothing needs to happen when the type is a standard schema element + ''' + pass + + @classmethod + def customize(cls, **kwargs): + """ + This function duplicates and customizes the class it belongs to. The + original class remains unchanged. + """ + + cls_dict = {} + + for k in cls.__dict__: + if not (k in ("__dict__", "__module__", "__weakref__")): + cls_dict[k] = cls.__dict__[k] + + class Attributes(cls.Attributes): + pass + + cls_dict['Attributes'] = Attributes + + for k,v in kwargs.items(): + setattr(Attributes,k,v) + + cls_dup = type(cls.__name__, cls.__bases__, cls_dict) + + return cls_dup + +class Null(Base): + @classmethod + def to_xml(cls, value, tns, name='retval'): + element = etree.Element("{%s}%s" % (tns,name)) + element.set('{%s}nil' % soaplib.ns_xsi, 'true') + + return element + + @classmethod + def from_xml(cls, element): + return None + +class SimpleType(Base): + __namespace__ = "http://www.w3.org/2001/XMLSchema" + __base_type__ = None + + class Attributes(Base.Attributes): + values = set() + + def __new__(cls, **kwargs): + """ + Overriden so that any attempt to instantiate a primitive will return a + customized class instead of an instance. + + See serializers.base.Base for more information. + """ + + retval = cls.customize(**kwargs) + + if not retval.is_default(): + if retval.get_namespace() is None: + retval.__base_type__ = cls.__base_type__ + else: + retval.__base_type__ = cls.get_type_name_ns() + + if retval.get_namespace() in soaplib.const_prefmap: + retval.__namespace__ = None + + if retval.__type_name__ is None: + retval.__type_name__ = kwargs.get("type_name", Base.Empty) + + return retval + + @classmethod + def is_default(cls): + return (Base.is_default() + and cls.Attributes.values == SimpleType.Attributes.values) + + @classmethod + def get_restriction_tag(cls, schema_entries): + simple_type = etree.Element('{%s}simpleType' % soaplib.ns_xsd) + simple_type.set('name', cls.get_type_name()) + schema_entries.add_simple_type(cls, simple_type) + + restriction = etree.SubElement(simple_type, + '{%s}restriction' % soaplib.ns_xsd) + restriction.set('base', cls.__base_type__) + + for v in cls.Attributes.values: + enumeration = etree.SubElement(restriction, + '{%s}enumeration' % soaplib.ns_xsd) + enumeration.set('value', str(v)) + + return restriction + + @classmethod + def add_to_schema(cls, schema_entries): + if not schema_entries.has_class(cls) and not cls.is_default(): + cls.get_restriction_tag(schema_entries) diff -Nru python-soaplib-0.8.1/src/soaplib/serializers/binary.py python-soaplib-0.9.3-alpha3/src/soaplib/serializers/binary.py --- python-soaplib-0.8.1/src/soaplib/serializers/binary.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/serializers/binary.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,112 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import base64 +import cStringIO + +from soaplib.serializers.base import Base +from soaplib.serializers import nillable_value, nillable_element + +from lxml import etree + +class Attachment(Base): + __type_name__ = 'base64Binary' + __namespace__ = "http://www.w3.org/2001/XMLSchema" + + def __init__(self, data=None, file_name=None): + self.data = data + self.file_name = file_name + + def save_to_file(self): + ''' + This method writes the data to the specified file. This method + assumes that the file_name is the full path to the file to be written. + This method also assumes that self.data is the base64 decoded data, + and will do no additional transformations on it, simply write it to + disk. + ''' + + if not self.data: + raise Exception("No data to write") + + if not self.file_name: + raise Exception("No file_name specified") + + f = open(self.file_name, 'wb') + f.write(self.data) + f.close() + + def load_from_file(self): + ''' + This method loads the data from the specified file, and does + no encoding/decoding of the data + ''' + if not self.file_name: + raise Exception("No file_name specified") + f = open(self.file_name, 'rb') + self.data = f.read() + f.close() + + @classmethod + @nillable_value + def to_xml(cls, value, tns, name='retval'): + ''' + This class method takes the data from the attachment and + base64 encodes it as the text of an Element. An attachment can + specify a file_name and if no data is given, it will read the data + from the file + ''' + + assert isinstance(value, cls) + + element = etree.Element('{%s}%s' % (tns,name)) + if value.data: + # the data has already been loaded, just encode + # and return the element + element.text = base64.encodestring(value.data) + + elif value.file_name: + # the data hasn't been loaded, but a file has been + # specified + data_string = cStringIO.StringIO() + + file_name = value.file_name + file = open(file_name, 'rb') + base64.encode(file, data_string) + file.close() + + # go back to the begining of the data + data_string.seek(0) + element.text = str(data_string.read()) + + else: + raise Exception("Neither data nor a file_name has been specified") + + return element + + @classmethod + @nillable_element + def from_xml(cls, element): + ''' + This method returns an Attachment object that contains + the base64 decoded string of the text of the given element + ''' + data = base64.decodestring(element.text) + a = Attachment(data=data) + return a diff -Nru python-soaplib-0.8.1/src/soaplib/serializers/clazz.py python-soaplib-0.9.3-alpha3/src/soaplib/serializers/clazz.py --- python-soaplib-0.8.1/src/soaplib/serializers/clazz.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/serializers/clazz.py 2010-08-25 18:18:17.000000000 +0000 @@ -0,0 +1,282 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +from lxml import etree + +import soaplib + +from soaplib.serializers import Base +from soaplib.serializers import nillable_element +from soaplib.serializers import nillable_value + +from soaplib.serializers.primitive import Array + +from soaplib.util.odict import odict as TypeInfo + +class ClassSerializerMeta(type): + ''' + This is the metaclass that populates ClassSerializer instances with + the appropriate datatypes for (de)serialization. + ''' + + def __new__(cls, cls_name, cls_bases, cls_dict): + ''' + This initializes the class, and sets all the appropriate types onto the + class for serialization. + ''' + + type_name = cls_dict.get("__type_name__", None) + if type_name is None: + cls_dict["__type_name__"] = cls_name + + # get base class (if exists) and enforce single inheritance + extends = cls_dict.get("__extends__", None) + if cls_name == "ExtensionClass": + print cls_name, cls_bases + + if extends is None: + for b in cls_bases: + base_types = getattr(b, "_type_info", None) + + if not (base_types is None): + assert extends is None or cls_dict["__extends__"] is b, \ + "WSDL 1.1 does not support multiple inheritance" + + try: + if len(base_types) > 0 and issubclass(b, Base): + cls_dict["__extends__"] = extends = b + except: + print extends + raise + + # populate soap members + if not ('_type_info' in cls_dict): + cls_dict['_type_info'] = _type_info = TypeInfo() + + for k,v in cls_dict.items(): + if not k.startswith('__'): + subc = False + try: + if issubclass(v,Base): + subc = True + except: + pass + + if subc: + _type_info[k] = v + if issubclass(v, Array) and v.serializer is None: + raise Exception("%s.%s is an array of what?" % + (cls_name, k)) + else: + _type_info = cls_dict['_type_info'] + if not isinstance(_type_info, TypeInfo): + cls_dict['_type_info'] = TypeInfo(_type_info) + + return type.__new__(cls, cls_name, cls_bases, cls_dict) + +class NonExtendingClass(object): + def __setattr__(self, k, v): + if not hasattr(self,k) and getattr(self, '__NO_EXTENSION', False): + raise Exception("'%s' object is not extendable at this point in " + "code.\nInvalid member '%s'" % + (self.__class__.__name__, k) ) + object.__setattr__(self,k,v) + +class ClassSerializerBase(NonExtendingClass, Base): + """ + If you want to make a better class serializer, this is what you should + inherit from + """ + + def __init__(self, **kwargs): + cls = self.__class__ + + for k in cls._type_info.keys(): + setattr(self, k, kwargs.get(k, None)) + + self.__NO_EXTENSION=True + + def __len__(self): + return len(self._type_info) + + def __getitem__(self,i): + return getattr(self, self._type_info.keys()[i], None) + + @classmethod + @nillable_value + def to_xml(cls, value, tns, name=None, element=None): + if name is None: + name = cls.get_type_name() + + if element is None: + element = etree.Element("{%s}%s" % (cls.get_namespace(), name)) + + if isinstance(value, list) or isinstance(value, tuple): + assert len(value) <= len(cls._type_info) + + array = value + value = cls() + + keys = cls._type_info.keys() + for i in range(len(array)): + setattr(value, keys[i], array[i]) + + elif isinstance(value, dict): + map = value + value = cls() + + for k,v in map.items(): + if k in cls._type_info: + setattr(value, k, v) + else: + raise KeyError(k) + + for k, v in cls._type_info.items(): + subvalue = getattr(value, k, None) + subelement = v.to_xml(subvalue, cls.get_namespace(), k) + element.append(subelement) + + clz = getattr(cls,'__extends__', None) + while not (clz is None): + clz.to_xml(value, tns, name, element) + clz = getattr(clz,'__extends__', None) + + return element + + @classmethod + @nillable_element + def from_xml(cls, element): + inst = cls() + children = element.getchildren() + + for c in children: + if isinstance(c, etree._Comment): + continue + key = c.tag.split('}')[-1] + + member = cls._type_info.get(key, None) + clz = getattr(cls,'__extends__', None) + while not (clz is None) and (member is None): + member = clz._type_info.get(key, None) + clz = getattr(clz,'__extends__', None) + + if member is None: + raise Exception('the %s object does not have a "%s" member' % + (cls.__name__,key)) + + value = member.from_xml(c) + setattr(inst, key, value) + + return inst + + @classmethod + def resolve_namespace(cls, default_ns): + if getattr(cls, '__extends__', None) != None: + cls.__extends__.resolve_namespace(default_ns) + if not (cls.get_namespace() in soaplib.const_prefmap): + default_ns = cls.get_namespace() + + if cls.__namespace__ is None: + cls.__namespace__ = cls.__module__ + + if (cls.__namespace__.startswith("soaplib") or + cls.__namespace__ == '__main__'): + cls.__namespace__ = default_ns + + for k, v in cls._type_info.items(): + if v.__type_name__ is Base.Empty: + v.__namespace__ = cls.get_namespace() + v.__type_name__ = "%s_%sType" % (cls.get_type_name(), k) + + v.resolve_namespace(cls.get_namespace()) + + @classmethod + def add_to_schema(cls, schema_entries): + if not schema_entries.has_class(cls): + if not (getattr(cls, '__extends__', None) is None): + cls.__extends__.add_to_schema(schema_entries) + + complex_type = etree.Element("{%s}complexType" % soaplib.ns_xsd) + complex_type.set('name',cls.get_type_name()) + + sequence_parent = complex_type + if not (getattr(cls, '__extends__', None) is None): + cls.__extends__.add_to_schema(schema_entries) + + complex_content = etree.SubElement(complex_type, + "{%s}complexContent" % soaplib.ns_xsd) + extension = etree.SubElement(complex_content, "{%s}extension" + % soaplib.ns_xsd) + extension.set('base', cls.__extends__.get_type_name_ns()) + sequence_parent = extension + + sequence = etree.SubElement(sequence_parent, '{%s}sequence' % + soaplib.ns_xsd) + + for k, v in cls._type_info.items(): + v.add_to_schema(schema_entries) + + member = etree.SubElement(sequence, '{%s}element' % + soaplib.ns_xsd) + member.set('name', k) + + if v.Attributes.min_occurs != 1: # 1 is the default + member.set('minOccurs', str(v.Attributes.min_occurs)) + if v.Attributes.max_occurs != 1: # 1 is the default + member.set('maxOccurs', str(v.Attributes.max_occurs)) + + member.set('type', v.get_type_name_ns()) + if bool(v.Attributes.nillable) == True: + member.set('nillable', 'true') + else: + member.set('nillable', 'false') + + schema_entries.add_complex_type(cls, complex_type) + + # simple node + element = etree.Element('{%s}element' % soaplib.ns_xsd) + element.set('name',cls.get_type_name()) + element.set('type',cls.get_type_name_ns()) + + schema_entries.add_element(cls, element) + + @staticmethod + def produce(namespace, type_name, members): + """ + Lets you create a class programmatically. + """ + + cls_dict = {} + + cls_dict['__namespace__'] = namespace + cls_dict['__type_name__'] = type_name + cls_dict['_type_info'] = TypeInfo(members) + + return ClassSerializerMeta(type_name, (ClassSerializer,), cls_dict) + +class ClassSerializer(ClassSerializerBase): + """ + The general complexType factory. The __call__ method of this class will + return instances, contrary to primivites where the same call will result in + customized duplicates of the original class definition. + Those who'd like to customize the class should use the customize method. + (see soaplib.serializers.base.Base) + """ + + __metaclass__ = ClassSerializerMeta diff -Nru python-soaplib-0.8.1/src/soaplib/serializers/enum.py python-soaplib-0.9.3-alpha3/src/soaplib/serializers/enum.py --- python-soaplib-0.8.1/src/soaplib/serializers/enum.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/serializers/enum.py 2010-08-25 17:33:02.000000000 +0000 @@ -0,0 +1,103 @@ + +from lxml import etree + +from base import SimpleType +from base import nillable_element +from base import nillable_value +from base import string_to_xml + +import soaplib + +_ns_xs = soaplib.ns_xsd + +# adapted from: http://code.activestate.com/recipes/413486/ + +class EnumBase(SimpleType): + __namespace__ = None + + @classmethod + @nillable_value + def to_xml(cls, value, tns, name='retval'): + if name is None: + name = cls.get_type_name() + + return string_to_xml(cls, str(value), tns, name) + + @classmethod + @nillable_element + def from_xml(cls, element): + return getattr(cls, element.text) + +def Enum(*values, **kwargs): + type_name = kwargs.get('type_name', None) + if type_name is None: + raise ValueError("Please specify 'type_name' as a keyword argument") + + assert len(values) > 0, "Empty enums are meaningless" + + maximum = len(values) # to make __invert__ work + + class EnumValue(object): + __slots__ = ('__value') + + def __init__(self, value): + self.__value = value + + def __hash__(self): + return hash(self.__value) + + def __cmp__(self, other): + assert type(self) is type(other), \ + "Only values from the same enum are comparable" + + return cmp(self.__value, other.__value) + + def __invert__(self): + return values[maximum - self.__value] + + def __nonzero__(self): + return bool(self.__value) + + def __repr__(self): + return str(values[self.__value]) + + class EnumType(EnumBase): + __type_name__ = type_name + + def __iter__(self): + return iter(values) + + def __len__(self): + return len(values) + + def __getitem__(self, i): + return values[i] + + def __repr__(self): + return 'Enum' + str(enumerate(values)) + + def __str__(self): + return 'enum ' + str(values) + + @classmethod + def add_to_schema(cls, schema_entries): + if not schema_entries.has_class(cls): + simple_type = etree.Element('{%s}simpleType' % _ns_xs) + simple_type.set('name', cls.get_type_name()) + + restriction = etree.SubElement(simple_type, + '{%s}restriction' % _ns_xs) + restriction.set('base', '%s:string' % + soaplib.get_namespace_prefix(soaplib.ns_xsd)) + + for v in values: + enumeration = etree.SubElement(restriction, + '{%s}enumeration' % _ns_xs) + enumeration.set('value', v) + + schema_entries.add_simple_type(cls, simple_type) + + for i,v in enumerate(values): + setattr(EnumType, v, EnumValue(i)) + + return EnumType diff -Nru python-soaplib-0.8.1/src/soaplib/serializers/exception.py python-soaplib-0.9.3-alpha3/src/soaplib/serializers/exception.py --- python-soaplib-0.8.1/src/soaplib/serializers/exception.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/serializers/exception.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,80 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import soaplib +from lxml import etree +from soaplib.serializers import Base + +_ns_xsi = soaplib.ns_xsi + +class Fault(Exception, Base): + __type_name__ = "Fault" + + def __init__(self, faultcode='Server', faultstring="", detail=""): + self.faultcode = faultcode + self.faultstring = faultstring + self.detail = detail + + @classmethod + def to_xml(cls, value, tns, name=None): + if name is None: + name = cls.get_type_name() + fault = etree.Element("{%s}%s" % (tns,name)) + + etree.SubElement(fault, '{%s}faultcode' % tns).text = value.faultcode + etree.SubElement(fault, '{%s}faultstring' % tns).text = value.faultstring + etree.SubElement(fault, '{%s}detail' % tns).text = value.detail + + return fault + + @classmethod + def from_xml(cls, element): + code = element.find('faultcode').text + string = element.find('faultstring').text + detail_element = element.find('detail') + if detail_element is not None: + if len(detail_element.getchildren()): + detail = etree.tostring(detail_element) + else: + detail = element.find('detail').text + else: + detail = '' + return Fault(faultcode=code, faultstring=string, detail=detail) + + @classmethod + def add_to_schema(cls, schema_dict): + complex_type = etree.Element('complexType') + complex_type.set('name', cls.get_type_name()) + sequenceNode = etree.SubElement(complex_type, 'sequence') + + element = etree.SubElement(sequenceNode, 'element') + element.set('name', 'detail') + element.set('{%s}type' % _ns_xsi, 'xs:string') + + element = etree.SubElement(sequenceNode, 'element') + element.set('name', 'message') + element.set('{%s}type' % _ns_xsi, 'xs:string') + + schema_dict.add_complex_type(cls, complex_type) + + top_level_element = etree.Element('element') + top_level_element.set('name', 'ExceptionFaultType') + top_level_element.set('{%s}type' % _ns_xsi, cls.get_type_name_ns()) + + schema_dict.add_element(cls, top_level_element) diff -Nru python-soaplib-0.8.1/src/soaplib/serializers/__init__.py python-soaplib-0.9.3-alpha3/src/soaplib/serializers/__init__.py --- python-soaplib-0.8.1/src/soaplib/serializers/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/serializers/__init__.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,20 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +from base import * \ No newline at end of file diff -Nru python-soaplib-0.8.1/src/soaplib/serializers/primitive.py python-soaplib-0.9.3-alpha3/src/soaplib/serializers/primitive.py --- python-soaplib-0.8.1/src/soaplib/serializers/primitive.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/serializers/primitive.py 2010-08-25 17:25:56.000000000 +0000 @@ -0,0 +1,363 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import datetime +import decimal +import re +import pytz + +from lxml import etree +from pytz import FixedOffset + +import soaplib +from soaplib.serializers import SimpleType +from soaplib.serializers import nillable_element +from soaplib.serializers import nillable_value +from soaplib.serializers import string_to_xml +from soaplib.util.etreeconv import etree_to_dict +from soaplib.util.etreeconv import dict_to_etree + +string_encoding = 'utf-8' + +_date_pattern = r'(?P\d{4})-(?P\d{2})-(?P\d{2})' +_time_pattern = r'(?P
\d{2}):(?P\d{2}):(?P\d{2})(?P\.\d+)?' +_offset_pattern = r'(?P[+-]\d{2}):(?P\d{2})' +_datetime_pattern = _date_pattern + '[T ]' + _time_pattern + +_local_re = re.compile(_datetime_pattern) +_utc_re = re.compile(_datetime_pattern + 'Z') +_offset_re = re.compile(_datetime_pattern + _offset_pattern) + +_ns_xs = soaplib.ns_xsd +_ns_xsi = soaplib.ns_xsi + +class Any(SimpleType): + __type_name__ = 'anyType' + + @classmethod + @nillable_value + def to_xml(cls, value, tns, name='retval'): + if isinstance(value, str) or isinstance(value, unicode): + value = etree.fromstring(value) + + e = etree.Element('{%s}%s' % (tns,name)) + e.append(value) + + return e + + @classmethod + @nillable_element + def from_xml(cls, element): + children = element.getchildren() + retval = None + + if children: + retval = element.getchildren()[0] + + return retval + +class AnyAsDict(Any): + @classmethod + @nillable_value + def to_xml(cls, value, tns, name='retval'): + e = etree.Element('{%s}%s' % (tns,name)) + dict_to_etree(e, value) + + return e + + @classmethod + @nillable_element + def from_xml(cls, element): + children = element.getchildren() + if children: + ret = etree_to_dict(element) + from pprint import pprint + pprint(ret) + return ret + + return None + +class String(SimpleType): + class Attributes(SimpleType.Attributes): + min_len = 0 + max_len = "unbounded" + pattern = None + + def __new__(cls, * args, ** kwargs): + assert len(args) <= 1 + + retval = SimpleType.__new__(cls, ** kwargs) + + if len(args) == 1: + retval.max_len = args[0] + + return retval + + @classmethod + def is_default(cls): + return (SimpleType.is_default() + and cls.Attributes.min_len == String.Attributes.min_len + and cls.Attributes.max_len == String.Attributes.max_len + and cls.Attributes.pattern == String.Attributes.pattern) + + @classmethod + def add_to_schema(cls, schema_entries): + if not schema_entries.has_class(cls) and not cls.is_default(): + restriction = cls.get_restriction_tag(schema_entries) + + # length + if cls.Attributes.min_len == cls.Attributes.max_len: + length = etree.SubElement(restriction, '{%s}length' % _ns_xs) + length.set('value', str(cls.Attributes.min_len)) + + else: + if cls.Attributes.min_len != String.Attributes.min_len: + min_l = etree.SubElement(restriction, '{%s}minLength' % _ns_xs) + min_l.set('value', str(cls.Attributes.min_len)) + + if cls.Attributes.max_len != String.Attributes.max_len: + max_l = etree.SubElement(restriction, '{%s}maxLength' % _ns_xs) + max_l.set('value', str(cls.Attributes.max_len)) + + # pattern + if cls.Attributes.pattern != String.Attributes.pattern: + pattern = etree.SubElement(restriction, '{%s}pattern' % _ns_xs) + pattern.set('value', cls.Attributes.pattern) + + @classmethod + @nillable_value + def to_xml(cls, value, tns, name='retval'): + if not isinstance(value, unicode): + value = unicode(value, string_encoding) + + return string_to_xml(cls, value, tns, name) + + @classmethod + @nillable_element + def from_xml(cls, element): + u = element.text or "" + try: + u = str(u) + return u.encode(string_encoding) + except: + return u + +class Integer(SimpleType): + @classmethod + @nillable_element + def from_xml(cls, element): + i = element.text + + try: + return int(i) + except: + return long(i) + + @classmethod + @nillable_value + def to_xml(cls, value, tns, name='retval'): + return string_to_xml(cls, str(value), tns, name) + +class Decimal(SimpleType): + @classmethod + @nillable_element + def from_xml(cls, element): + return decimal.Decimal(element.text) + +class Date(SimpleType): + @classmethod + @nillable_value + def to_xml(cls, value, tns, name='retval'): + return string_to_xml(cls, value.isoformat(), tns, name) + + @classmethod + @nillable_element + def from_xml(cls, element): + """expect ISO formatted dates""" + text = element.text + + def parse_date(date_match): + fields = date_match.groupdict(0) + year, month, day = [int(fields[x]) for x in + ("year", "month", "day")] + return datetime.date(year, month, day) + + match = _date_pattern.match(text) + if not match: + raise Exception("Date [%s] not in known format" % text) + + return parse_date(match) + +class DateTime(SimpleType): + __type_name__ = 'dateTime' + + @classmethod + @nillable_value + def to_xml(cls, value, tns, name='retval'): + return string_to_xml(cls, value.isoformat('T'), tns, name) + + @classmethod + @nillable_element + def from_xml(cls, element): + """expect ISO formatted dates""" + + text = element.text + def parse_date(date_match, tz=None): + fields = date_match.groupdict(0) + year, month, day, hr, min, sec = [int(fields[x]) for x in + ("year", "month", "day", "hr", "min", "sec")] + # use of decimal module here (rather than float) might be better + # here, if willing to require python 2.4 or higher + microsec = int(float(fields.get("sec_frac", 0)) * 10 ** 6) + return datetime.datetime(year, month, day, hr, min, sec, microsec, tz) + + match = _utc_re.match(text) + if match: + return parse_date(match, tz=pytz.utc) + + match = _offset_re.match(text) + if match: + tz_hr, tz_min = [int(match.group(x)) for x in "tz_hr", "tz_min"] + return parse_date(match, tz=FixedOffset(tz_hr * 60 + tz_min, {})) + + match = _local_re.match(text) + if not match: + raise Exception("DateTime [%s] not in known format" % text) + + return parse_date(match) + +class Double(SimpleType): + @classmethod + @nillable_element + def from_xml(cls, element): + return float(element.text) + + @classmethod + @nillable_value + def to_xml(cls, value, tns, name='retval'): + return string_to_xml(cls, str(value), tns, name) + +class Float(Double): + pass + +class Boolean(SimpleType): + @classmethod + @nillable_value + def to_xml(cls, value, tns, name='retval'): + return string_to_xml(cls, str(bool(value)).lower(), tns, name) + + @classmethod + @nillable_element + def from_xml(cls, element): + s = element.text + return (s and s.lower()[0] == 't') + +class Array(SimpleType): + serializer = None + __namespace__ = None + + child_min_occurs = 0 + child_max_occurs = "unbounded" + + def __new__(cls, serializer, ** kwargs): + retval = cls.customize( ** kwargs) + + retval.serializer = serializer + + retval.__type_name__ = '%sArray' % retval.serializer.get_type_name() + + return retval + + @classmethod + def resolve_namespace(cls, default_ns): + cls.serializer.resolve_namespace(default_ns) + + if cls.__namespace__ is None: + if cls.serializer.get_namespace() != soaplib.ns_xsd: + cls.__namespace__ = cls.serializer.get_namespace() + else: + cls.__namespace__ = default_ns + + if (cls.__namespace__.startswith('soaplib') or + cls.__namespace__ == '__main__'): + cls.__namespace__ = default_ns + + cls.serializer.resolve_namespace(cls.get_namespace()) + + @classmethod + @nillable_value + def to_xml(cls, values, tns, name='retval'): + retval = etree.Element("{%s}%s" % (tns, name)) + + if values == None: + values = [] + + retval.set('type', "%s" % cls.get_type_name_ns()) + + # so that we see the variable name in the exception + try: + iter(values) + except TypeError: + raise TypeError(values, name) + + for value in values: + retval.append( + cls.serializer.to_xml(value, tns, + cls.serializer.get_type_name())) + + return retval + + @classmethod + @nillable_element + def from_xml(cls, element): + retval = [] + + for child in element.getchildren(): + retval.append(cls.serializer.from_xml(child)) + + return retval + + @classmethod + def add_to_schema(cls, schema_entries): + if not schema_entries.has_class(cls): + cls.serializer.add_to_schema(schema_entries) + + complex_type = etree.Element('{%s}complexType' % _ns_xs) + complex_type.set('name', cls.get_type_name()) + + sequence = etree.SubElement(complex_type, '{%s}sequence' % _ns_xs) + + element = etree.SubElement(sequence, '{%s}element' % _ns_xs) + element.set('name', cls.serializer.get_type_name()) + element.set('type', cls.serializer.get_type_name_ns()) + element.set('minOccurs', str(cls.child_min_occurs)) + element.set('maxOccurs', str(cls.child_max_occurs)) + + schema_entries.add_complex_type(cls, complex_type) + + top_level_element = etree.Element('{%s}element' % _ns_xs) + top_level_element.set('name', cls.get_type_name()) + top_level_element.set('type', cls.get_type_name_ns()) + + schema_entries.add_element(cls, top_level_element) + +# a class that is really a namespace +class Mandatory(object): + String = String(min_len=1, min_occurs=1, nillable=False) + Integer = Integer(min_occurs=1, nillable=False) diff -Nru python-soaplib-0.8.1/src/soaplib/serializers/table.py python-soaplib-0.9.3-alpha3/src/soaplib/serializers/table.py --- python-soaplib-0.8.1/src/soaplib/serializers/table.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/serializers/table.py 2010-08-24 20:46:09.000000000 +0000 @@ -0,0 +1,69 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import sqlalchemy +from sqlalchemy import Column + +from sqlalchemy.ext.declarative import DeclarativeMeta +from soaplib.serializers.clazz import TypeInfo +from soaplib.serializers.clazz import ClassSerializerBase +from soaplib.serializers import primitive as soap + +_type_map = { + sqlalchemy.Text: soap.String, + sqlalchemy.String: soap.String, + sqlalchemy.Unicode: soap.String, + sqlalchemy.UnicodeText: soap.String, + + sqlalchemy.Float: soap.Float, + sqlalchemy.Numeric: soap.Double, + sqlalchemy.Integer: soap.Integer, + sqlalchemy.SmallInteger: soap.Integer, + + sqlalchemy.Boolean: soap.Boolean, + sqlalchemy.DateTime: soap.DateTime, + sqlalchemy.orm.relation: soap.Array, +} + +class TableSerializerMeta(DeclarativeMeta): + def __new__(cls, cls_name, cls_bases, cls_dict): + if cls_dict.get("__type_name__", None) is None: + cls_dict["__type_name__"] = cls_name + + if cls_dict.get("_type_info", None) is None: + cls_dict["_type_info"] = _type_info = TypeInfo() + + for k, v in cls_dict.items(): + if not k.startswith('__'): + if isinstance(v, Column): + if v.type in _type_map: + rpc_type = _type_map[v.type] + elif type(v.type) in _type_map: + rpc_type = _type_map[type(v.type)] + else: + raise Exception("soap_type was not found. maybe " + "_type_map needs a new entry.") + + _type_info[k]=rpc_type + + return DeclarativeMeta.__new__(cls, cls_name, cls_bases, cls_dict) + +class TableSerializer(ClassSerializerBase): + __metaclass__ = TableSerializerMeta + _decl_class_registry={} diff -Nru python-soaplib-0.8.1/src/soaplib/service.py python-soaplib-0.9.3-alpha3/src/soaplib/service.py --- python-soaplib-0.8.1/src/soaplib/service.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/service.py 2010-08-25 19:26:36.000000000 +0000 @@ -0,0 +1,614 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import soaplib +from lxml import etree + +from soaplib.soap import Message +from soaplib.soap import MethodDescriptor +from soaplib.serializers.clazz import TypeInfo +from soaplib.util.odict import odict + +import logging +logger = logging.getLogger(__name__) + +_pref_wsa = soaplib.prefmap[soaplib.ns_wsa] + +def rpc(*params, **kparams): + ''' + This is a method decorator to flag a method as a remote procedure call. It + will behave like a normal python method on a class, and will only behave + differently when the keyword '_method_descriptor' is passed in, returning a + 'MethodDescriptor' object. This decorator does none of the soap/xml + serialization, only flags a method as a soap method. This decorator should + only be used on member methods of an instance of ServiceBase. + ''' + + def explain(f): + def explain_method(*args, **kwargs): + if '_method_descriptor' in kwargs: + # input message + def get_input_message(ns): + _in_message = kparams.get('_in_message', f.func_name) + _in_variable_names = kparams.get('_in_variable_names', {}) + + arg_count = f.func_code.co_argcount + param_names = f.func_code.co_varnames[1:arg_count] + + try: + in_params = TypeInfo() + + for i in range(len(params)): + e0 = _in_variable_names.get(param_names[i], + param_names[i]) + e1 = params[i] + + in_params[e0] = e1 + + except IndexError, e: + raise Exception("%s has parameter numbers mismatching" % + f.func_name) + + message=Message.produce(type_name=_in_message, namespace=ns, + members=in_params) + message.resolve_namespace(ns) + return message + + def get_output_message(ns): + _returns = kparams.get('_returns') + + _out_message = kparams.get('_out_message', '%sResponse' % + f.func_name) + + kparams.get('_out_variable_name') + out_params = TypeInfo() + + if _returns: + if isinstance(_returns, (list, tuple)): + default_names = ['%sResult%d' % (f.func_name, i) + for i in range(len(_returns))] + + _out_variable_names = kparams.get( + '_out_variable_names', default_names) + + assert (len(_returns) == len(_out_variable_names)) + + var_pair = zip(_out_variable_names,_returns) + out_params = TypeInfo(var_pair) + + else: + _out_variable_name = kparams.get( + '_out_variable_name', '%sResult' % f.func_name) + + out_params[_out_variable_name] = _returns + + message=Message.produce(type_name=_out_message,namespace=ns, + members=out_params) + message.resolve_namespace(ns) + return message + + _is_callback = kparams.get('_is_callback', False) + _public_name = kparams.get('_public_name', f.func_name) + _is_async = kparams.get('_is_async', False) + _mtom = kparams.get('_mtom', False) + _in_header = kparams.get('_in_header', None) + _out_header = kparams.get('_out_header', None) + + # the decorator function does not have a reference to the + # class and needs to be passed in + ns = kwargs['clazz'].get_tns() + + in_message = get_input_message(ns) + out_message = get_output_message(ns) + + if not (_in_header is None): + _in_header.resolve_namespace(ns) + if not (_out_header is None): + _out_header.resolve_namespace(ns) + + doc = getattr(f, '__doc__') + descriptor = MethodDescriptor(f.func_name, _public_name, + in_message, out_message, doc, _is_callback, _is_async, + _mtom, _in_header, _out_header) + + return descriptor + + return f(*args, **kwargs) + + explain_method.__doc__ = f.__doc__ + explain_method._is_rpc = True + explain_method.func_name = f.func_name + + return explain_method + + return explain + +class _SchemaInfo(object): + def __init__(self): + self.elements = odict() + self.types = odict() + +class _SchemaEntries(object): + def __init__(self, tns): + self.namespaces = odict() + self.imports = {} + self.tns = tns + + def has_class(self, cls): + retval = False + ns_prefix = cls.get_namespace_prefix() + + if ns_prefix in soaplib.const_nsmap: + retval = True + + else: + type_name = cls.get_type_name() + + if (ns_prefix in self.namespaces) and \ + (type_name in self.namespaces[ns_prefix].types): + retval = True + + return retval + + def get_schema_info(self, prefix): + if prefix in self.namespaces: + schema = self.namespaces[prefix] + else: + schema = self.namespaces[prefix] = _SchemaInfo() + + return schema + + # FIXME: this is an ugly hack. we need proper dependency management + def __check_imports(self, cls, node): + pref_tns = cls.get_namespace_prefix() + pref_own = soaplib.get_namespace_prefix(self.tns) + + def is_valid_import(pref): + return not ( + (pref in soaplib.const_nsmap) or + (pref in (pref_own, pref_tns)) + ) + + if not (pref_tns in self.imports): + self.imports[pref_tns] = set() + + for c in node: + if c.tag == "{%s}complexContent" % soaplib.ns_xsd: + seq = c.getchildren()[0].getchildren()[0] # FIXME: ugly, isn't it? + + extension = c.getchildren()[0] + if extension.tag == '{%s}extension' % soaplib.ns_xsd: + pref = extension.attrib['base'].split(':')[0] + if is_valid_import(pref): + self.imports[pref_tns].add(soaplib.nsmap[pref]) + else: + seq = c + + if seq.tag == '{%s}sequence' % soaplib.ns_xsd: + for e in seq: + pref = e.attrib['type'].split(':')[0] + if is_valid_import(pref): + self.imports[pref_tns].add(soaplib.nsmap[pref]) + + elif seq.tag == '{%s}restriction' % soaplib.ns_xsd: + pref = seq.attrib['base'].split(':')[0] + if is_valid_import(pref): + self.imports[pref_tns].add(soaplib.nsmap[pref]) + + else: + raise Exception("i guess you need to hack some more") + + def add_element(self, cls, node): + schema_info = self.get_schema_info(cls.get_namespace_prefix()) + schema_info.elements[cls.get_type_name()] = node + + def add_simple_type(self, cls, node): + self.__check_imports(cls, node) + schema_info = self.get_schema_info(cls.get_namespace_prefix()) + schema_info.types[cls.get_type_name()] = node + + def add_complex_type(self, cls, node): + self.__check_imports(cls, node) + schema_info = self.get_schema_info(cls.get_namespace_prefix()) + schema_info.types[cls.get_type_name()] = node + + +_public_methods_cache = {} + +class DefinitionBase(object): + ''' + This class serves as the base for all soap services. Subclasses of this + class will use the rpc decorator to flag methods to be exposed via soap. + This class is responsible for generating the wsdl for this service + definition. + + It is a natural abstract base class, because it's of no use without any + method definitions, hence the 'Base' suffix in the name. + ''' + + __tns__ = None + __in_header__ = None + __out_header__ = None + + def __init__(self, environ=None): + self.soap_in_header = None + self.soap_out_header = None + + cls = self.__class__ + if not (cls in _public_methods_cache): + _public_methods_cache[cls] = self.build_public_methods() + + self.public_methods = _public_methods_cache[cls] + + def on_method_call(self, environ, method_name, py_params, soap_params): + ''' + Called BEFORE the service implementing the functionality is called + @param the wsgi environment + @param the method name + @param the body element of the soap request + @param the tuple of python params being passed to the method + @param the soap elements for each params + ''' + pass + + def on_method_return(self, environ, py_results, soap_results, + http_resp_headers): + ''' + Called AFTER the service implementing the functionality is called + @param the wsgi environment + @param the python results from the method + @param the xml serialized results of the method + @param soap response headers as a list of lxml.etree._Element objects + @param http response headers as a dict of strings + ''' + pass + + def on_method_exception(self, environ, exc, fault_xml, fault_str): + ''' + Called when an error occurs durring execution + @param the wsgi environment + @param the exception + @param the response string + ''' + pass + + def call_wrapper(self, call, params): + ''' + Called in place of the original method call. + @param the original method call + @param the arguments to the call + ''' + return call(*params) + + @classmethod + def get_tns(cls): + if not (cls.__tns__ is None): + return cls.__tns__ + + service_name = cls.__name__.split('.')[-1] + + retval = '.'.join((cls.__module__, service_name)) + if cls.__module__ == '__main__': + retval = '.'.join((service_name, service_name)) + + return retval + + def build_public_methods(self): + '''Returns a list of method descriptors for this object''' + + logger.debug('building public methods') + public_methods = [] + + for func_name in dir(self): + if func_name == 'public_methods': + continue + func = getattr(self, func_name) + if callable(func) and hasattr(func, '_is_rpc'): + descriptor = func(_method_descriptor=True, clazz=self.__class__) + public_methods.append(descriptor) + + return public_methods + + def get_method(self, name): + ''' + Returns the metod descriptor based on element name or soap action + ''' + + for method in self.public_methods: + type_name = method.in_message.get_type_name() + if '{%s}%s' % (self.get_tns(), type_name) == name: + return method + + for method in self.public_methods: + if method.public_name == name: + return method + + raise Exception('Method "%s" not found' % name) + + def _has_callbacks(self): + '''Determines if this object has callback methods or not''' + + for method in self.public_methods: + if method.is_callback: + return True + + return False + + def header_objects(self): + return [] + + def get_service_names(self): + ''' + Returns the service name(s) for this service. If this + object has callbacks, then a second service is declared in + the wsdl for those callbacks + ''' + + service_name = self.__class__.__name__.split('.')[-1] + + if self._hasCallbacks(): + return [service_name, '%sCallback' % service_name] + + return [service_name] + + def add_port_type(self, root, service_name, types, url, port_type): + ns_wsdl = soaplib.ns_wsdl + + # FIXME: I don't think this call is working. + cb_port_type = self.__add_callbacks(root, types, service_name, url) + + for method in self.public_methods: + if method.is_callback: + operation = etree.SubElement(cb_port_type, '{%s}operation' + % ns_wsdl) + else: + operation = etree.SubElement(port_type,'{%s}operation' % ns_wsdl) + + operation.set('name', method.name) + + if method.doc is not None: + documentation = etree.SubElement(operation, '{%s}documentation' + % ns_wsdl) + documentation.text = method.doc + + operation.set('parameterOrder', method.in_message.get_type_name()) + + op_input = etree.SubElement(operation, '{%s}input' % ns_wsdl) + op_input.set('name', method.in_message.get_type_name()) + op_input.set('message', method.in_message.get_type_name_ns()) + + if (not method.is_callback) and (not method.is_async): + op_output = etree.SubElement(operation, '{%s}output' % ns_wsdl) + op_output.set('name', method.out_message.get_type_name()) + op_output.set('message', method.out_message.get_type_name_ns()) + + # FIXME: I don't think this is working. + def __add_callbacks(self, root, types, service_name, url): + ns_xsd = soaplib.ns_xsd + ns_wsa = soaplib.ns_wsa + ns_wsdl = soaplib.ns_wsdl + ns_soap = soaplib.ns_soap + + ns_tns = self.get_tns() + pref_tns = soaplib.get_namespace_prefix(ns_tns) + + cb_port_type = None + + # add necessary async headers + # WS-Addressing -> RelatesTo ReplyTo MessageID + # callback porttype + if self._has_callbacks(): + wsa_schema = etree.SubElement(types, "{%s}schema" % ns_xsd) + wsa_schema.set("targetNamespace", '%sCallback' % ns_tns) + wsa_schema.set("elementFormDefault", "qualified") + + import_ = etree.SubElement(wsa_schema, "{%s}import" % ns_xsd) + import_.set("namespace", ns_wsa) + import_.set("schemaLocation", ns_wsa) + + relt_message = etree.SubElement(root, '{%s}message' % ns_wsdl) + relt_message.set('name', 'RelatesToHeader') + relt_part = etree.SubElement(relt_message, '{%s}part' % ns_wsdl) + relt_part.set('name', 'RelatesTo') + relt_part.set('element', '%s:RelatesTo' % _pref_wsa) + + reply_message = etree.SubElement(root, '{%s}message' % ns_wsdl) + reply_message.set('name', 'ReplyToHeader') + reply_part = etree.SubElement(reply_message, '{%s}part' % ns_wsdl) + reply_part.set('name', 'ReplyTo') + reply_part.set('element', '%s:ReplyTo' % _pref_wsa) + + id_header = etree.SubElement(root, '{%s}message' % ns_wsdl) + id_header.set('name', 'MessageIDHeader') + id_part = etree.SubElement(id_header, '{%s}part' % ns_wsdl) + id_part.set('name', 'MessageID') + id_part.set('element', '%s:MessageID' % _pref_wsa) + + # make portTypes + cb_port_type = etree.SubElement(root, '{%s}portType' % ns_wsdl) + cb_port_type.set('name', '%sCallback' % service_name) + + cb_service_name = '%sCallback' % service_name + + cb_service = etree.SubElement(root, '{%s}service' % ns_wsdl) + cb_service.set('name', cb_service_name) + + cb_wsdl_port = etree.SubElement(cb_service, '{%s}port' % ns_wsdl) + cb_wsdl_port.set('name', cb_service_name) + cb_wsdl_port.set('binding', '%s:%s' % (pref_tns, cb_service_name)) + + cb_address = etree.SubElement(cb_wsdl_port, '{%s}address' + % ns_soap) + cb_address.set('location', url) + + return cb_port_type + + def add_schema(self, schema_entries): + ''' + A private method for adding the appropriate entries + to the schema for the types in the specified methods. + + @param the schema node to add the schema elements to. if it is None, + the schema nodes are returned inside a dictionary + @param the schema node dictinary, where keys are prefixes of the schema + stored schema node + ''' + + if schema_entries is None: + schema_entries = _SchemaEntries(self.get_tns()) + + if self.__in_header__ != None: + self.__in_header__.resolve_namespace(self.get_tns()) + self.__in_header__.add_to_schema(schema_entries) + + if self.__out_header__ != None: + self.__out_header__.resolve_namespace(self.get_tns()) + self.__out_header__.add_to_schema(schema_entries) + + for method in self.public_methods: + method.in_message.add_to_schema(schema_entries) + method.out_message.add_to_schema(schema_entries) + + if method.in_header is None: + method.in_header = self.__in_header__ + else: + method.in_header.add_to_schema(schema_entries) + + if method.out_header is None: + method.out_header = self.__out_header__ + else: + method.out_header.add_to_schema(schema_entries) + + return schema_entries + + def __add_message_for_object(self, root, messages, obj): + if obj != None and not (obj.get_type_name() in messages): + messages.add(obj.get_type_name()) + + message = etree.SubElement(root, '{%s}message' % soaplib.ns_wsdl) + message.set('name', obj.get_type_name()) + + part = etree.SubElement(message, '{%s}part' % soaplib.ns_wsdl) + part.set('name', obj.get_type_name()) + part.set('element', obj.get_type_name_ns()) + + def add_messages_for_methods(self, root, messages): + ''' + A private method for adding message elements to the wsdl + @param the the root element of the wsdl + ''' + + for method in self.public_methods: + self.__add_message_for_object(root, messages, method.in_message) + self.__add_message_for_object(root, messages, method.out_message) + self.__add_message_for_object(root, messages, method.in_header) + self.__add_message_for_object(root, messages, method.out_header) + + + def add_bindings_for_methods(self, root, service_name, types, url, binding, cb_binding=None): + ''' + A private method for adding bindings to the wsdl + + @param the root element of the wsdl + @param the name of this service + ''' + + ns_wsdl = soaplib.ns_wsdl + ns_soap = soaplib.ns_soap + pref_tns = soaplib.get_namespace_prefix(self.get_tns()) + + if self._has_callbacks(): + if cb_binding is None: + cb_binding = etree.SubElement(root, '{%s}binding' % ns_wsdl) + cb_binding.set('name', '%sCallback' % service_name) + cb_binding.set('type', 'typens:%sCallback' % service_name) + + soap_binding = etree.SubElement(cb_binding, '{%s}binding' % ns_soap) + soap_binding.set('transport', 'http://schemas.xmlsoap.org/soap/http') + + for method in self.public_methods: + operation = etree.Element('{%s}operation' % ns_wsdl) + operation.set('name', method.name) + + soap_operation = etree.SubElement(operation, '{%s}operation' % + ns_soap) + soap_operation.set('soapAction', method.public_name) + soap_operation.set('style', 'document') + + # get input + input = etree.SubElement(operation, '{%s}input' % ns_wsdl) + input.set('name', method.in_message.get_type_name()) + + soap_body = etree.SubElement(input, '{%s}body' % ns_soap) + soap_body.set('use', 'literal') + + # get input soap header + in_header = method.in_header + if in_header is None: + in_header = self.__in_header__ + + if not (in_header is None): + soap_header = etree.SubElement(input, '{%s}header' % ns_soap) + soap_header.set('use', 'literal') + soap_header.set('message', in_header.get_type_name_ns()) + soap_header.set('part', in_header.get_type_name()) + + if not (method.is_async or method.is_callback): + output = etree.SubElement(operation, '{%s}output' % ns_wsdl) + output.set('name', method.out_message.get_type_name()) + + soap_body = etree.SubElement(output, '{%s}body' % ns_soap) + soap_body.set('use', 'literal') + + # get input soap header + out_header = method.in_header + if out_header is None: + out_header = self.__in_header__ + + if not (out_header is None): + soap_header = etree.SubElement(output, '{%s}header' % ns_soap) + soap_header.set('use', 'literal') + soap_header.set('message', out_header.get_type_name_ns()) + soap_header.set('part', out_header.get_type_name()) + + + if method.is_callback: + relates_to = etree.SubElement(input, '{%s}header' % ns_soap) + + relates_to.set('message', '%s:RelatesToHeader' % pref_tns) + relates_to.set('part', 'RelatesTo') + relates_to.set('use', 'literal') + + cb_binding.append(operation) + + else: + if method.is_async: + rt_header = etree.SubElement(input,'{%s}header' % ns_soap) + rt_header.set('message', '%s:ReplyToHeader' % pref_tns) + rt_header.set('part', 'ReplyTo') + rt_header.set('use', 'literal') + + mid_header = etree.SubElement(input, '{%s}header'% ns_soap) + mid_header.set('message', '%s:MessageIDHeader' % pref_tns) + mid_header.set('part', 'MessageID') + mid_header.set('use', 'literal') + + binding.append(operation) + + return cb_binding diff -Nru python-soaplib-0.8.1/src/soaplib/soap.py python-soaplib-0.9.3-alpha3/src/soaplib/soap.py --- python-soaplib-0.8.1/src/soaplib/soap.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/soap.py 2010-08-23 13:46:29.000000000 +0000 @@ -0,0 +1,407 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import logging +logger = logging.getLogger(__name__) +from lxml import etree + +from base64 import b64encode +from urllib import unquote + +# import email data format related stuff +try: + # python >= 2.5 + from email.mime.multipart import MIMEMultipart + from email.mime.application import MIMEApplication + from email.encoders import encode_7or8bit +except ImportError: + # python 2.4 + from email.MIMENonMultipart import MIMENonMultipart + from email.MIMEMultipart import MIMEMultipart + from email.Encoders import encode_7or8bit + +from email import message_from_string + +# import soaplib stuff +from soaplib.serializers.binary import Attachment +from soaplib.serializers.clazz import ClassSerializer + +import soaplib + +class Message(ClassSerializer): + pass + +class MethodDescriptor(object): + ''' + This class represents the method signature of a soap method, + and is returned by the soapdocument, or rpc decorators. + ''' + + def __init__(self, name, public_name, in_message, out_message, doc, + is_callback=False, is_async=False, mtom=False, in_header=None, + out_header=None): + + self.name = name + self.public_name = public_name + self.in_message = in_message + self.out_message = out_message + self.doc = doc + self.is_callback = is_callback + self.is_async = is_async + self.mtom = mtom + self.in_header = in_header + self.out_header = out_header + +def from_soap(xml_string, http_charset): + ''' + Parses the xml string into the header and payload + ''' + try: + root, xmlids = etree.XMLID(xml_string.decode(http_charset)) + except ValueError,e: + logger.debug('%s -- falling back to str decoding.' % (e)) + root, xmlids = etree.XMLID(xml_string) + + if xmlids: + resolve_hrefs(root, xmlids) + + header_envelope = root.xpath('e:Header', namespaces={'e': soaplib.ns_soap_env}) + body_envelope = root.xpath('e:Body', namespaces={'e': soaplib.ns_soap_env}) + + body=None + if len(body_envelope) > 0 and len(body_envelope[0]) > 0: + body = body_envelope[0].getchildren()[0] + + header=None + if len(header_envelope) > 0 and len(header_envelope[0]) > 0: + header = header_envelope[0].getchildren()[0] + + return body, header + +# see http://www.w3.org/TR/2000/NOTE-SOAP-20000508/ +# section 5.2.1 for an example of how the id and href attributes are used. +def resolve_hrefs(element, xmlids): + for e in element: + if e.get('id'): + continue # don't need to resolve this element + + elif e.get('href'): + resolved_element = xmlids[e.get('href').replace('#', '')] + if resolved_element is None: + continue + resolve_hrefs(resolved_element, xmlids) + + # copies the attributes + [e.set(k, v) for k, v in resolved_element.items()] + + # copies the children + [e.append(child) for child in resolved_element.getchildren()] + + # copies the text + e.text = resolved_element.text + + else: + resolve_hrefs(e, xmlids) + + return element + +def make_soap_envelope(header, body): + ''' + This method takes the results from a soap method call, and wraps them + in the appropriate soap envelope with any specified headers + + @param the message of the soap envelope, either an element or + a list of elements + @param any header elements to be included in the soap response + @returns the envelope element + ''' + envelope = etree.Element('{%s}Envelope' % soaplib.ns_soap_env, nsmap=soaplib.nsmap) + if not (header is None): + soap_header = etree.SubElement(envelope, '{%s}Header' % soaplib.ns_soap_env) + soap_header.append(header) + + soap_body = etree.SubElement(envelope, '{%s}Body' % soaplib.ns_soap_env) + if body != None: + soap_body.append(body) + + return envelope + +def join_attachment(href_id, envelope, payload, prefix=True): + ''' + Helper function for swa_to_soap. + + Places the data from an attachment back into a SOAP message, replacing + its xop:Include element or href. + + @param id content-id or content-location of attachment + @param prefix Set this to true if id is content-id or false if it is + content-location. It prefixes a "cid:" to the href value. + @param envelope soap envelope string to be operated on + @param payload attachment data + + @return tuple of length 2 with the new message and the + number of replacements made + ''' + + def replacing(parent, node, payload, numreplaces): + if node.tag == '{%s}Include' % soaplib.ns_xop: + attrib = node.attrib.get('href') + if not attrib is None: + if unquote(attrib) == href_id: + parent.remove(node) + parent.text = payload + numreplaces += 1 + else: + for child in node: + numreplaces = replacing(node, child, payload, numreplaces) + + return numreplaces + + # grab the XML element of the message in the SOAP body + soaptree = etree.fromstring(envelope) + soapbody = soaptree.find("{%s}Body" % soaplib.ns_soap_env) + + message = None + for child in list(soapbody): + if child.tag != "{%s}Fault" % soaplib.ns_soap_env: + message = child + break + + numreplaces = 0 + idprefix = '' + + if prefix == True: + idprefix = "cid:" + href_id = "%s%s" % (idprefix, href_id, ) + + # Make replacement. + for param in message: + # Look for Include subelement. + for sub in param: + numreplaces = replacing(param, sub, payload, numreplaces) + + if numreplaces < 1: + attrib = param.attrib.get('href') + if not attrib is None: + if unquote(attrib) == href_id: + del(param.attrib['href']) + param.text = payload + numreplaces += 1 + + return (etree.tostring(soaptree), numreplaces) + +def collapse_swa(content_type, envelope): + ''' + Translates an SwA multipart/related message into an + application/soap+xml message. + + References: + SwA http://www.w3.org/TR/SOAP-attachments + XOP http://www.w3.org/TR/xop10/ + MTOM http://www.w3.org/TR/soap12-mtom/ + http://www.w3.org/Submission/soap11mtom10/ + + @param content_type value of the Content-Type header field, parsed by + cgi.parse_header() function + @param envelope body of the HTTP message, a soap envelope + @return appication/soap+xml version of the given HTTP body + ''' + + # convert multipart messages back to pure SOAP + mime_type = content_type[0] + + if 'multipart/related' not in mime_type: + return envelope + + charset = content_type[1].get('charset', None) + if charset is None: + charset='ascii' + + # parse the body into an email.Message object + msg_string = [ + "MIME-Version: 1.0", + "Content-Type: %s; charset=%s" % (mime_type, charset), + "", + envelope + ] + + msg = message_from_string('\r\n'.join(msg_string)) # our message + + soapmsg = None + root = msg.get_param('start') + + # walk through sections, reconstructing pure SOAP + for part in msg.walk(): + # skip the multipart container section + if part.get_content_maintype() == 'multipart': + continue + + # detect main soap section + if (part.get('Content-ID') and part.get('Content-ID') == root) or \ + (root == None and part == msg.get_payload()[0]): + soapmsg = part.get_payload() + continue + + # binary packages + cte = part.get("Content-Transfer-Encoding") + + payload = None + if cte != 'base64': + payload = b64encode(part.get_payload()) + else: + payload = part.get_payload() + + cid = part.get("Content-ID").strip("<>") + cloc = part.get("Content-Location") + numreplaces = None + + # Check for Content-ID and make replacement + if cid: + soapmsg, numreplaces = join_attachment(cid, soapmsg, payload) + + # Check for Content-Location and make replacement + if cloc and not cid and not numreplaces: + soapmsg, numreplaces = join_attachment(cloc, soapmsg, payload, False) + + return soapmsg + +def apply_mtom(headers, envelope, params, paramvals): + ''' + Apply MTOM to a SOAP envelope, separating attachments into a + MIME multipart message. + + References: + XOP http://www.w3.org/TR/xop10/ + MTOM http://www.w3.org/TR/soap12-mtom/ + http://www.w3.org/Submission/soap11mtom10/ + + @param headers Headers dictionary of the SOAP message that would + originally be sent. + @param envelope SOAP envelope string that would have originally been sent. + @param params params attribute from the Message object used for the SOAP + @param paramvals values of the params, passed to Message.to_xml + @return tuple of length 2 with dictionary of headers and + string of body that can be sent with HTTPConnection + ''' + + # grab the XML element of the message in the SOAP body + soaptree = etree.fromstring(envelope) + soapbody = soaptree.find("{%s}Body" % soaplib.ns_soap_env) + + message = None + for child in list(soapbody): + if child.tag != "%sFault" % (soaplib.ns_soap_env, ): + message = child + break + + # Get additional parameters from original Content-Type + ctarray = [] + for n, v in headers.items(): + if n.lower() == 'content-type': + ctarray = v.split(';') + break + roottype = ctarray[0].strip() + rootparams = {} + for ctparam in ctarray[1:]: + n, v = ctparam.strip().split('=') + rootparams[n] = v.strip("\"'") + + # Set up initial MIME parts. + mtompkg = MIMEMultipart('related', + boundary='?//<><>soaplib_MIME_boundary<>') + rootpkg = None + try: + rootpkg = MIMEApplication(envelope, 'xop+xml', encode_7or8bit) + except NameError: + rootpkg = MIMENonMultipart("application", "xop+xml") + rootpkg.set_payload(envelope) + encode_7or8bit(rootpkg) + + # Set up multipart headers. + del(mtompkg['mime-version']) + mtompkg.set_param('start-info', roottype) + mtompkg.set_param('start', '') + if 'SOAPAction' in headers: + mtompkg.add_header('SOAPAction', headers.get('SOAPAction')) + + # Set up root SOAP part headers. + del(rootpkg['mime-version']) + + rootpkg.add_header('Content-ID', '') + + for n, v in rootparams.items(): + rootpkg.set_param(n, v) + + rootpkg.set_param('type', roottype) + + mtompkg.attach(rootpkg) + + # Extract attachments from SOAP envelope. + for i in range(len(params)): + name, typ = params[i] + + if typ == Attachment: + id = "soaplibAttachment_%s" % (len(mtompkg.get_payload()), ) + + param = message[i] + param.text = "" + + incl = etree.SubElement(param, "{%s}Include" % soaplib.ns_xop) + incl.attrib["href"] = "cid:%s" % id + + if paramvals[i].fileName and not paramvals[i].data: + paramvals[i].load_from_file() + + data = paramvals[i].data + attachment = None + + try: + attachment = MIMEApplication(data, _encoder=encode_7or8bit) + + except NameError: + attachment = MIMENonMultipart("application", "octet-stream") + attachment.set_payload(data) + encode_7or8bit(attachment) + + del(attachment['mime-version']) + + attachment.add_header('Content-ID', '<%s>' % (id, )) + mtompkg.attach(attachment) + + # Update SOAP envelope. + rootpkg.set_payload(etree.tostring(soaptree)) + + # extract body string from MIMEMultipart message + bound = '--%s' % (mtompkg.get_boundary(), ) + marray = mtompkg.as_string().split(bound) + mtombody = bound + mtombody += bound.join(marray[1:]) + + # set Content-Length + mtompkg.add_header("Content-Length", str(len(mtombody))) + + # extract dictionary of headers from MIMEMultipart message + mtomheaders = {} + for name, value in mtompkg.items(): + mtomheaders[name] = value + + if len(mtompkg.get_payload()) <= 1: + return (headers, envelope) + + return (mtomheaders, mtombody) diff -Nru python-soaplib-0.8.1/src/soaplib/test/__init__.py python-soaplib-0.9.3-alpha3/src/soaplib/test/__init__.py --- python-soaplib-0.8.1/src/soaplib/test/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/__init__.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,34 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import unittest + +import serializers +import test_soap +import test_service + +def suite(): + suite = serializers.suite() + suite.addTests(test_soap.suite()) + suite.addTests(test_service.suite()) + + return suite + +if __name__== '__main__': + unittest.TextTestRunner().run(suite()) diff -Nru python-soaplib-0.8.1/src/soaplib/test/interop/server/basic.py python-soaplib-0.9.3-alpha3/src/soaplib/test/interop/server/basic.py --- python-soaplib-0.8.1/src/soaplib/test/interop/server/basic.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/interop/server/basic.py 2010-08-25 17:23:56.000000000 +0000 @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import logging +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger('soaplib.wsgi') +logger.setLevel(logging.DEBUG) + +from soaplib.test.interop.server._service import application + +if __name__ == '__main__': + try: + from wsgiref.simple_server import make_server + from wsgiref.validate import validator + server = make_server('0.0.0.0', 9753, validator(application)) + logger.info('Starting interop server at %s:%s.' % ('0.0.0.0', 9753)) + logger.info('WSDL is at: /?wsdl') + server.serve_forever() + + except ImportError: + print "Error: example server code requires Python >= 2.5" diff -Nru python-soaplib-0.8.1/src/soaplib/test/interop/server/_service.py python-soaplib-0.9.3-alpha3/src/soaplib/test/interop/server/_service.py --- python-soaplib-0.8.1/src/soaplib/test/interop/server/_service.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/interop/server/_service.py 2010-08-25 17:14:39.000000000 +0000 @@ -0,0 +1,244 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +from soaplib.serializers.binary import Attachment +from soaplib.serializers.clazz import ClassSerializer +from soaplib.serializers.enum import Enum + +from soaplib.serializers.primitive import Any +from soaplib.serializers.primitive import AnyAsDict +from soaplib.serializers.primitive import Array +from soaplib.serializers.primitive import Boolean +from soaplib.serializers.primitive import DateTime +from soaplib.serializers.primitive import Float +from soaplib.serializers.primitive import Integer +from soaplib.serializers.primitive import String +from soaplib.serializers.primitive import Double + +from soaplib import service +from soaplib.service import rpc +from soaplib.wsgi import ValidatingApplication + +from datetime import datetime + +import logging +logger = logging.getLogger(__name__) + +class SimpleClass(ClassSerializer): + i = Integer + s = String + +class OtherClass(ClassSerializer): + dt = DateTime + d = Double + b = Boolean + +class NestedClass(ClassSerializer): + __namespace__ = "punk.tunk" + + simple = Array(SimpleClass) + s = String + i = Integer + f = Float + other = OtherClass + +class NonNillableClass(ClassSerializer): + __namespace__ = "hunk.sunk" + + nillable = False + min_occurs = 1 + + dt = DateTime(min_occurs=1, nillable=False) + i = Integer(nillable=False) + s = String(min_len=1, nillable=False) + +class ExtensionClass(NestedClass): + __namespace__ = "bar" + + p = NonNillableClass + l = DateTime + q = Integer + +DaysOfWeekEnum = Enum( + 'Monday', + 'Tuesday', + 'Wednesday', + 'Friday', + 'Saturday', + 'Sunday', + type_name = 'DaysOfWeekEnum' +) + +class InHeader(ClassSerializer): + s=String + i=Integer + +class OutHeader(ClassSerializer): + dt=DateTime + f=Float + +class InteropServiceWithHeader(service.DefinitionBase): + __in_header__ = InHeader + __out_header__ = OutHeader + + @rpc(_returns=InHeader) + def echo_in_header(self): + return self.soap_in_header + + @rpc(_returns=OutHeader) + def send_out_header(self): + self.soap_out_header = OutHeader() + self.soap_out_header.dt = datetime(year=2000, month=01, day=01) + self.soap_out_header.f = 3.141592653 + + return self.soap_out_header + +class InteropPrimitive(service.DefinitionBase): + @rpc(Any, _returns=Any) + def echo_any(self, xml): + return xml + + @rpc(AnyAsDict, _returns=AnyAsDict) + def echo_any_as_dict(self, xml_as_dict): + return xml_as_dict + + @rpc(Integer, _returns=Integer) + def echo_integer(self, i): + return i + + @rpc(String, _returns=String) + def echo_string(self, s): + return s + + @rpc(DateTime, _returns=DateTime) + def echo_datetime(self, dt): + return dt + + @rpc(Float, _returns=Float) + def echo_float(self, f): + return f + + @rpc(Double, _returns=Double) + def echo_double(self, f): + return f + + @rpc(Boolean, _returns=Boolean) + def echo_boolean(self, b): + return b + + @rpc(DaysOfWeekEnum, _returns=DaysOfWeekEnum) + def echo_enum(self, day): + return day + +class InteropArray(service.DefinitionBase): + @rpc(Array(Integer), _returns=Array(Integer)) + def echo_integer_array(self, ia): + return ia + + @rpc(Array(String), _returns=Array(String)) + def echo_string_array(self, sa): + return sa + + @rpc(Array(DateTime), _returns=Array(DateTime)) + def echo_date_time_array(self, dta): + return dta + + @rpc(Array(Float), _returns=Array(Float)) + def echo_float_array(self, fa): + return fa + + @rpc(Array(Double), _returns=Array(Double)) + def echo_double_array(self, da): + return da + + @rpc(Array(Boolean), _returns=Array(Boolean)) + def echo_boolean_array(self, ba): + return ba + + @rpc(Array(Boolean), _returns=Array(Array(Boolean))) + def echo_array_in_array(self, baa): + return baa + +class InteropClass(service.DefinitionBase): + @rpc(SimpleClass, _returns=SimpleClass) + def echo_simple_class(self, sc): + return sc + + @rpc(Array(SimpleClass), _returns=Array(SimpleClass)) + def echo_simple_class_array(self, sca): + return sca + + @rpc(NestedClass, _returns=NestedClass) + def echo_nested_class(self, nc): + return nc + + @rpc(Array(NestedClass), _returns=Array(NestedClass)) + def echo_nested_class_array(self, nca): + return nca + + @rpc(ExtensionClass, _returns=ExtensionClass) + def echo_extension_class(self, nc): + return nc + + @rpc(Attachment, _returns=Attachment) + def echo_attachment(self, a): + return a + + @rpc(Array(Attachment), _returns=Array(Attachment)) + def echo_attachment_array(self, aa): + return aa + +class InteropMisc(service.DefinitionBase): + @rpc() + def huge_number(_returns=Integer): + return 2**int(1e5) + + @rpc() + def long_string(_returns=String): + return len('0123456789abcdef' * 16384) + + @rpc() + def test_empty(self): + pass + + @rpc(String, Integer, DateTime) + def multi_param(self, s, i, dt): + pass + + @rpc(_returns=String) + def return_only(self): + return 'howdy' + + @rpc(NonNillableClass, _returns=String) + def non_nillable(self, n): + return "OK" + + @rpc(String, _returns=String, _public_name="do_something") + def do_something_else(self, s): + return s + +services = [ + InteropPrimitive, + InteropArray, + InteropClass, + InteropMisc, + InteropServiceWithHeader, +] + +application = ValidatingApplication(services, tns=__name__) diff -Nru python-soaplib-0.8.1/src/soaplib/test/interop/server/static.py python-soaplib-0.9.3-alpha3/src/soaplib/test/interop/server/static.py --- python-soaplib-0.8.1/src/soaplib/test/interop/server/static.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/interop/server/static.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import logging +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger('soaplib.wsgi') +logger.setLevel(logging.DEBUG) + +from twisted.python import log + +from soaplib.test.interop.server._service import application +from soaplib.util.server import run_twisted + +port = 9754 +url = 'app' + +def main(argv): + observer = log.PythonLoggingObserver('twisted') + log.startLoggingWithObserver(observer.emit,setStdout=False) + + return run_twisted( [ (application, url) ], port ) + +if __name__ == '__main__': + import sys + sys.exit(main(sys.argv)) diff -Nru python-soaplib-0.8.1/src/soaplib/test/interop/test_suds.py python-soaplib-0.9.3-alpha3/src/soaplib/test/interop/test_suds.py --- python-soaplib-0.8.1/src/soaplib/test/interop/test_suds.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/interop/test_suds.py 2010-08-23 13:33:26.000000000 +0000 @@ -0,0 +1,206 @@ +#!/usr/bin/env python +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import unittest + +from suds.client import Client +from suds import WebFault +from datetime import datetime + +class TestSuds(unittest.TestCase): + def setUp(self): + self.client = Client("http://localhost:9753/?wsdl", cache=None) + self.ns = "soaplib.test.interop.server._service" + + def test_enum(self): + DaysOfWeekEnum = self.client.factory.create("DaysOfWeekEnum") + + val = DaysOfWeekEnum.Monday + ret = self.client.service.echo_enum(val) + + assert val == ret + + def test_validation(self): + non_nillable_class = self.client.factory.create("NonNillableClass") + non_nillable_class.i = 6 + non_nillable_class.s = None + + try: + ret = self.client.service.non_nillable(non_nillable_class) + raise Exception("must fail") + except WebFault, e: + pass + + def test_echo_integer_array(self): + ia = self.client.factory.create('integerArray') + ia.integer.extend([1,2,3,4,5]) + self.client.service.echo_integer_array(ia) + + def test_echo_in_header(self): + in_header = self.client.factory.create('InHeader') + in_header.s = 'a' + in_header.i = 3 + + self.client.set_options(soapheaders=in_header) + ret = self.client.service.echo_in_header() + self.client.set_options(soapheaders=None) + + print ret + + self.assertEquals(in_header.s, ret.s) + self.assertEquals(in_header.i, ret.i) + + def test_send_out_header(self): + #self.soap_out_header.dt = datetime(year=2000, month=01, day=01) + #self.soap_out_header.f = 3.141592653 + + ret = self.client.service.send_out_header() + + print ret + raise Exception("test something!") + + def test_echo_string(self): + test_string = "OK" + ret = self.client.service.echo_string(test_string) + + self.assertEquals(ret, test_string) + + def __get_xml_test_val(self): + return { + "test_sub": { + "test_subsub1": { + "test_subsubsub1" : ["subsubsub1 value"] + }, + "test_subsub2": ["subsub2 value 1", "subsub2 value 2"], + "test_subsub3": [ + { + "test_subsub3sub1": "subsub3sub1 value" + }, + { + "test_subsub3sub2": "subsub3sub2 value" + }, + ], + "test_subsub4": None, + "test_subsub5": ["x"], + } + } + + def test_any(self): + val=self.__get_xml_test_val() + ret = self.client.service.echo_any(val) + + self.assertEquals(ret, val) + + def test_any_as_dict(self): + val=self.__get_xml_test_val() + ret = self.client.service.echo_any_as_dict(val) + + self.assertEquals(ret, val) + + def test_echo_simple_class(self): + service_name = "echo_nested_class"; + val = self.client.factory.create("{%s}NestedClass" % self.ns); + + val.i = 45 + val.s = "asd" + val.f = 12.34 + + val.simple = self.client.factory.create("{%s}SimpleClassArray" % self.ns) + + val = self.client.factory.create("{%s}SimpleClass" % self.ns) + + val.i = 45 + val.s = "asd" + + ret = self.client.service.echo_simple_class(val) + + def test_echo_nested_class(self): + service_name = "echo_nested_class"; + val = self.client.factory.create("{%s}NestedClass" % self.ns); + + val.i = 45 + val.s = "asd" + val.f = 12.34 + + val.simple = self.client.factory.create("{%s}SimpleClassArray" % self.ns) + + val.simple.SimpleClass.append(self.client.factory.create( + "{%s}SimpleClass" % self.ns)) + val.simple.SimpleClass.append(self.client.factory.create( + "{%s}SimpleClass" % self.ns)) + + val.simple.SimpleClass[0].i = 45 + val.simple.SimpleClass[0].s = "asd" + val.simple.SimpleClass[1].i = 12 + val.simple.SimpleClass[1].s = "qwe" + + val.other = self.client.factory.create("{%s}OtherClass" % self.ns); + val.other.dt = datetime.now() + val.other.d = 123.456 + val.other.b = True + + ret = self.client.service.echo_nested_class(val) + print ret + raise Exception("test something! :)") + # TODO: write asserts + + def test_echo_extension_class(self): + service_name = "echo_extension_class"; + val = self.client.factory.create("{%s}ExtensionClass" % self.ns); + + val.i = 45 + val.s = "asd" + val.f = 12.34 + + val.simple = self.client.factory.create("{%s}SimpleClassArray" % self.ns) + + val.simple.SimpleClass.append(self.client.factory.create( + "{%s}SimpleClass" % self.ns)) + val.simple.SimpleClass.append(self.client.factory.create( + "{%s}SimpleClass" % self.ns)) + + val.simple.SimpleClass[0].i = 45 + val.simple.SimpleClass[0].s = "asd" + val.simple.SimpleClass[1].i = 12 + val.simple.SimpleClass[1].s = "qwe" + + val.other = self.client.factory.create("{%s}OtherClass" % self.ns); + val.other.dt = datetime.now() + val.other.d = 123.456 + val.other.b = True + + val.p = self.client.factory.create("{%s}NonNillableClass" % self.ns); + val.p.dt = datetime(2010,06,02) + val.p.i = 123 + val.p.s = "punk" + + val.l = datetime(2010,07,02) + val.q = 5 + + ret = self.client.service.echo_extension_class(val) + print ret + raise Exception("test something! :)") + # TODO: write asserts + +def suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(TestSuds) + +if __name__== '__main__': + unittest.TextTestRunner().run(suite()) diff -Nru python-soaplib-0.8.1/src/soaplib/test/interop/test_wsi.py python-soaplib-0.9.3-alpha3/src/soaplib/test/interop/test_wsi.py --- python-soaplib-0.8.1/src/soaplib/test/interop/test_wsi.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/interop/test_wsi.py 2010-08-23 20:59:54.000000000 +0000 @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# +# WS-I interoperability test http://www.ws-i.org/deliverables/workinggroup.aspx?wg=testingtools +# latest download: http://www.ws-i.org/Testing/Tools/2005/06/WSI_Test_Java_Final_1.1.zip +# +# before launching it, you should download the zip file and unpack it in this directory +# this should create the wsi-test-tools directory +# +# adapted from http://thestewscope.wordpress.com/2008/08/19/ruby-wrapper-for-ws-i-analyzer-tools/ +# from Luca Dariz +# + +import os +import string +from lxml import etree + +CONFIG_FILE = 'config.xml' +SOAPLIB_TEST_NS = 'soaplib.test.interop.server._service' +SOAPLIB_TEST_SERVICE = 'ValidatingApplication' +SOAPLIB_TEST_PORT = 'ValidatingApplication' +SOAPLIB_REPORT_FILE = 'wsi-report-soaplib.xml' + + +WSI_ANALYZER_CONFIG_TEMPLATE=string.Template("""\ + + + +false + + + + +${ASSERTIONS_FILE} + + ${PORT_NAME} + ${WSDL_URI} + +""") + +#This must be changed to point to the physical root of the wsi-installation +WSI_HOME_TAG = "WSI_HOME" +WSI_HOME_VAL = "wsi-test-tools" +WSI_JAVA_HOME_TAG = "WSI_JAVA_HOME" +WSI_JAVA_HOME_VAL = WSI_HOME_VAL+"/java" +WSI_JAVA_OPTS_TAG = "WSI_JAVA_OPTS" +WSI_JAVA_OPTS_VAL = " -Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser" +WSI_TEST_ASSERTIONS_FILE = WSI_HOME_VAL+"/common/profiles/SSBP10_BP11_TAD.xml" +WSI_STYLESHEET_FILE = WSI_HOME_VAL+"/common/xsl/report.xsl" +WSI_EXECUTION_COMMAND = "java ${WSI_JAVA_OPTS} -Dwsi.home=${WSI_HOME} -cp ${WSI_CP}\ + org.wsi.test.analyzer.BasicProfileAnalyzer -config " + +WSIClasspath=[ + WSI_JAVA_HOME_VAL+"/lib/wsi-test-tools.jar", + WSI_JAVA_HOME_VAL+"/lib", + WSI_JAVA_HOME_VAL+"/lib/xercesImpl.jar", + WSI_JAVA_HOME_VAL+"/lib/xmlParserAPIs.jar", + WSI_JAVA_HOME_VAL+"/lib/wsdl4j.jar", + WSI_JAVA_HOME_VAL+"/lib/uddi4j.jar", + WSI_JAVA_HOME_VAL+"/lib/axis.jar", + WSI_JAVA_HOME_VAL+"/lib/jaxrpc.jar", + WSI_JAVA_HOME_VAL+"/lib/saaj.jar", + WSI_JAVA_HOME_VAL+"/lib/commons-discovery.jar", + WSI_JAVA_HOME_VAL+"/lib/commons-logging.jar" +] +WSI_CLASSPATH_TAG = "WSI_CP" +WSI_CLASSPATH_VAL = ':'.join(WSIClasspath) + + +def configure_env(): + os.environ[WSI_HOME_TAG] = WSI_HOME_VAL + os.environ[WSI_JAVA_HOME_TAG] = WSI_JAVA_HOME_VAL + os.environ[WSI_JAVA_OPTS_TAG] = WSI_JAVA_OPTS_VAL + os.environ[WSI_CLASSPATH_TAG] = WSI_CLASSPATH_VAL + +def create_config(wsdl_uri, config_file): + print "Creating config for wsdl at %s ...\n" %wsdl_uri + # extract target elements + service = 'ValidatingApplication' + port = 'ValidatingApplication' + # for wsdl service declarations: + # create config(service, port) + vars = {'REPORT_FILE':SOAPLIB_REPORT_FILE, + 'STYLESHEET_FILE':WSI_STYLESHEET_FILE, + 'ASSERTIONS_FILE':WSI_TEST_ASSERTIONS_FILE, + 'SERVICE_NAME':SOAPLIB_TEST_SERVICE, + 'WSDL_NAMESPACE':SOAPLIB_TEST_NS, + 'PORT_NAME':SOAPLIB_TEST_PORT, + 'WSDL_URI':wsdl_uri} + config = WSI_ANALYZER_CONFIG_TEMPLATE.substitute(vars) + f = open(config_file, 'w') + f.write(config) + f.close() + +def analyze_wsdl(config_file): + # execute ws-i tests + # don't execute Analyzer.sh directly since it needs bash + os.system(WSI_EXECUTION_COMMAND + config_file) + + # parse result + e = etree.parse(SOAPLIB_REPORT_FILE).getroot() + summary = etree.ETXPath('{%s}summary' %e.nsmap['wsi-report'])(e) + if summary: + # retrieve overall result of the test + result = summary[0].get('result') + if result == 'failed': + outs = etree.ETXPath('{%s}artifact' %(e.nsmap['wsi-report'],))(e) + + # filter for the object describing the wsdl test + desc = [o for o in outs if o.get('type') == 'description'][0] + + # loop over every group test + for entry in desc.iterchildren(): + # loop over every single test + for test in entry.iterchildren(): + # simply print the error if there is one + # an html can be generated using files in wsi-test-tools/common/xsl + if test.get('result') == 'failed': + fail_msg = etree.ETXPath('{%s}failureMessage' %e.nsmap['wsi-report'])(test) + fail_det = etree.ETXPath('{%s}failureDetail' %e.nsmap['wsi-report'])(test) + if fail_msg: + print '\nFAILURE in test %s\n' %test.get('id') + print fail_msg[0].text + if fail_det: + print '\nFAILURE MSG\n' + print fail_det[0].text + +if __name__ == '__main__': + configure_env() + create_config('http://localhost:9753/?wsdl', CONFIG_FILE) + analyze_wsdl(CONFIG_FILE) diff -Nru python-soaplib-0.8.1/src/soaplib/test/serializers/__init__.py python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/__init__.py --- python-soaplib-0.8.1/src/soaplib/test/serializers/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/__init__.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,36 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import unittest + +import test_binary +import test_clazz +import test_primitive + +def suite(): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromTestCase(test_primitive.TestPrimitive) + suite.addTests(test_clazz.suite()) + suite.addTests(test_binary.suite()) + + return suite + +if __name__== '__main__': + unittest.TextTestRunner().run(suite()) diff -Nru python-soaplib-0.8.1/src/soaplib/test/serializers/test_binary.py python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/test_binary.py --- python-soaplib-0.8.1/src/soaplib/test/serializers/test_binary.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/test_binary.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import base64 +import os +import shutil +import unittest +from tempfile import mkstemp + +import soaplib +from soaplib.serializers.binary import Attachment + +ns_test = 'test_namespace' + +class TestBinary(unittest.TestCase): + def setUp(self): + os.mkdir('binaryDir') + + fd, self.tmpfile = mkstemp('', '', 'binaryDir') + os.close(fd) + f = open(self.tmpfile, 'w') + for i in range(0, 1000): + f.write('All work and no play makes jack a dull boy\r\n') + f.flush() + f.close() + + def tearDown(self): + shutil.rmtree('binaryDir') + + def test_to_xml_data(self): + f = open(self.tmpfile) + data = f.read() + f.close() + a = Attachment() + a.data = data + element = Attachment.to_xml(a, ns_test) + encoded_data = base64.encodestring(data) + self.assertNotEquals(element.text, None) + self.assertEquals(element.text, encoded_data) + + def test_to_xml_file(self): + a = Attachment() + a.file_name = self.tmpfile + f = open(self.tmpfile, 'rb') + data = f.read() + f.close() + element = Attachment.to_xml(a, ns_test) + encoded_data = base64.encodestring(data) + self.assertNotEquals(element.text, None) + self.assertEquals(element.text, encoded_data) + + def test_to_from_xml_file(self): + a = Attachment() + a.file_name = self.tmpfile + element = Attachment.to_xml(a, ns_test) + + data = Attachment.from_xml(element).data + + f = open(self.tmpfile, 'rb') + fdata = f.read() + f.close() + + self.assertEquals(data, fdata) + + def test_exception(self): + try: + Attachment.to_xml(Attachment(), ns_test) + except: + self.assertTrue(True) + else: + self.assertFalse(True) + + def test_from_xml(self): + f = open(self.tmpfile) + data = f.read() + f.close() + + a = Attachment() + a.data = data + element = Attachment.to_xml(a, ns_test) + a2 = Attachment.from_xml(element) + + self.assertEquals(data, a2.data) + + def test_add_to_schema(self): + schema = {} + Attachment.add_to_schema(schema) + self.assertEquals(0, len(schema.keys())) + + def test_get_datatype(self): + dt = Attachment.get_type_name() + self.assertEquals('base64Binary', dt) + + dt = Attachment.get_namespace() + assert soaplib.nsmap['xs'] == dt + +def suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(TestBinary) + +if __name__== '__main__': + unittest.TextTestRunner().run(suite()) diff -Nru python-soaplib-0.8.1/src/soaplib/test/serializers/test_clazz.py python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/test_clazz.py --- python-soaplib-0.8.1/src/soaplib/test/serializers/test_clazz.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/test_clazz.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,230 @@ +#!/usr/bin/env python +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import datetime +import unittest + +from soaplib.serializers.clazz import ClassSerializer + +from soaplib.serializers.primitive import Array +from soaplib.serializers.primitive import DateTime +from soaplib.serializers.primitive import Float +from soaplib.serializers.primitive import Integer +from soaplib.serializers.primitive import String + +ns_test = 'test_namespace' + +class Address(ClassSerializer): + street = String + city = String + zip = Integer + since = DateTime + lattitude = Float + longitude = Float + +Address.resolve_namespace(__name__) + +class Person(ClassSerializer): + name = String + birthdate = DateTime + age = Integer + addresses = Array(Address) + titles = Array(String) + +Person.resolve_namespace(__name__) + +class Employee(Person): + employee_id = Integer + salary = Float + +Employee.resolve_namespace(__name__) + +class Level2(ClassSerializer): + arg1 = String + arg2 = Float + +Level2.resolve_namespace(__name__) + +class Level3(ClassSerializer): + arg1 = Integer + +Level3.resolve_namespace(__name__) + +class Level4(ClassSerializer): + arg1 = String + +Level4.resolve_namespace(__name__) + +class Level1(ClassSerializer): + level2 = Level2 + level3 = Array(Level3) + level4 = Array(Level4) + +Level1.resolve_namespace(__name__) + +class TestClassSerializer(unittest.TestCase): + def test_simple_class(self): + a = Address() + a.street = '123 happy way' + a.city = 'badtown' + a.zip = 32 + a.lattitude = 4.3 + a.longitude = 88.0 + + element = Address.to_xml(a, ns_test) + self.assertEquals(6, len(element.getchildren())) + + r = Address.from_xml(element) + + self.assertEquals(a.street, r.street) + self.assertEquals(a.city, r.city) + self.assertEquals(a.zip, r.zip) + self.assertEquals(a.lattitude, r.lattitude) + self.assertEquals(a.longitude, r.longitude) + self.assertEquals(a.since, r.since) + + def test_nested_class(self): # FIXME: this test is incomplete + p = Person() + element = Person.to_xml(p, ns_test) + + self.assertEquals(None, p.name) + self.assertEquals(None, p.birthdate) + self.assertEquals(None, p.age) + self.assertEquals(None, p.addresses) + + def test_class_array(self): + peeps = [] + names = ['bob', 'jim', 'peabody', 'mumblesleves'] + for name in names: + a = Person() + a.name = name + a.birthdate = datetime.datetime(1979, 1, 1) + a.age = 27 + peeps.append(a) + + serializer = Array(Person) + serializer.resolve_namespace(__name__) + + element = serializer.to_xml(peeps, ns_test) + + self.assertEquals(4, len(element.getchildren())) + + peeps2 = serializer.from_xml(element) + for i in range(0, 4): + self.assertEquals(peeps2[i].name, names[i]) + self.assertEquals(peeps2[i].birthdate, + datetime.datetime(1979, 1, 1)) + + def test_class_nested_array(self): + peeps = [] + names = ['bob', 'jim', 'peabody', 'mumblesleves'] + + for name in names: + a = Person() + a.name = name + a.birthdate = datetime.datetime(1979, 1, 1) + a.age = 27 + a.addresses = [] + + for i in range(0, 25): + addr = Address() + addr.street = '555 downtown' + addr.city = 'funkytown' + a.addresses.append(addr) + peeps.append(a) + + serializer = Array(Person) + serializer.resolve_namespace(__name__) + element = serializer.to_xml(peeps, ns_test) + + self.assertEquals(4, len(element.getchildren())) + + peeps2 = serializer.from_xml(element) + for peep in peeps2: + self.assertEquals(27, peep.age) + self.assertEquals(25, len(peep.addresses)) + self.assertEquals('funkytown', peep.addresses[18].city) + + def test_complex_class(self): + l = Level1() + l.level2 = Level2() + l.level2.arg1 = 'abcd' + l.level2.arg2 = 1.444 + l.level3 = [] + l.level4 = [] + + for i in range(0, 100): + a = Level3() + a.arg1 = i + l.level3.append(a) + + for i in range(0, 4): + a = Level4() + a.arg1 = str(i) + l.level4.append(a) + + element = Level1.to_xml(l, ns_test) + l1 = Level1.from_xml(element) + + self.assertEquals(l1.level2.arg1, l.level2.arg1) + self.assertEquals(l1.level2.arg2, l.level2.arg2) + self.assertEquals(len(l1.level4), len(l.level4)) + self.assertEquals(100, len(l.level3)) + + def test_customize(self): + class Base(ClassSerializer): + class Attributes(ClassSerializer.Attributes): + prop1=3 + prop2=6 + + Base2 = Base.customize(prop1=4) + + self.assertNotEquals(Base.Attributes.prop1, Base2.Attributes.prop1) + self.assertEquals(Base.Attributes.prop2, Base2.Attributes.prop2) + + class Derived(Base): + class Attributes(Base.Attributes): + prop3 = 9 + prop4 = 12 + + Derived2 = Derived.customize(prop1=5, prop3=12) + + self.assertEquals(Base.Attributes.prop1, 3) + self.assertEquals(Base2.Attributes.prop1, 4) + + self.assertEquals(Derived.Attributes.prop1, 3) + self.assertEquals(Derived2.Attributes.prop1, 5) + + self.assertNotEquals(Derived.Attributes.prop3, Derived2.Attributes.prop3) + self.assertEquals(Derived.Attributes.prop4, Derived2.Attributes.prop4) + + Derived3 = Derived.customize(prop3=12) + Base.prop1 = 4 + + # changes made to bases propagate, unless overridden + self.assertEquals(Derived.Attributes.prop1, Base.Attributes.prop1) + self.assertNotEquals(Derived2.Attributes.prop1, Base.Attributes.prop1) + self.assertEquals(Derived3.Attributes.prop1, Base.Attributes.prop1) + +def suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(TestClassSerializer) + +if __name__== '__main__': + unittest.TextTestRunner().run(suite()) diff -Nru python-soaplib-0.8.1/src/soaplib/test/serializers/test_enum.py python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/test_enum.py --- python-soaplib-0.8.1/src/soaplib/test/serializers/test_enum.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/test_enum.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import unittest +import soaplib +_ns_xs = soaplib.nsmap['xs'] +_ns_xsi = soaplib.nsmap['xsi'] + +from soaplib.wsgi import Application +from soaplib.service import DefinitionBase +from soaplib.service import rpc + +from soaplib.serializers.enum import Enum + +from lxml import etree + +vals = [ + 'Monday', + 'Tuesday', + 'Wednesday', + 'Friday', + 'Saturday', + 'Sunday', +] + +DaysOfWeekEnum = Enum( + 'Monday', + 'Tuesday', + 'Wednesday', + 'Friday', + 'Saturday', + 'Sunday', + type_name = 'DaysOfWeekEnum' +) + +class TestService(DefinitionBase): + @rpc(DaysOfWeekEnum, _returns=DaysOfWeekEnum) + def remote_call(self, day): + return DaysOfWeekEnum.Sunday + +class TestEnum(unittest.TestCase): + def test_wsdl(self): + wsdl = Application([TestService],'tns').get_wsdl('punk') + elt = etree.fromstring(wsdl) + simple_type = elt.xpath('//xs:simpleType', namespaces=soaplib.nsmap)[0] + + print etree.tostring(elt, pretty_print=True) + print simple_type + + self.assertEquals(simple_type.attrib['name'], 'DaysOfWeekEnum') + self.assertEquals(simple_type[0].tag, "{%s}restriction" % soaplib.ns_xsd) + self.assertEquals([e.attrib['value'] for e in simple_type[0]], vals) + + def test_serialize(self): + DaysOfWeekEnum.resolve_namespace('punk') + mo = DaysOfWeekEnum.Monday + print repr(mo) + + elt = DaysOfWeekEnum.to_xml(mo, 'test_namespace') + ret = DaysOfWeekEnum.from_xml(elt) + + self.assertEquals(mo, ret) + +def suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(TestEnum) + +if __name__== '__main__': + unittest.TextTestRunner().run(suite()) diff -Nru python-soaplib-0.8.1/src/soaplib/test/serializers/test_include.py python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/test_include.py --- python-soaplib-0.8.1/src/soaplib/test/serializers/test_include.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/test_include.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import unittest +from urllib import quote_plus + +from lxml import etree + +import soaplib + +from soaplib.serializers.clazz import ClassSerializer +from soaplib.serializers.primitive import Integer +from soaplib.serializers.primitive import String +from soaplib.soap import join_attachment + +# Service Classes +class DownloadPartFileResult(ClassSerializer): + ErrorCode = Integer + ErrorMessage = String + Data = String + +# Tests +class TestInclude(unittest.TestCase): + def test_join_attachment(self): + href_id="http://tempuri.org/1/634133419330914808" + payload="ANJNSLJNDYBC SFDJNIREMX:CMKSAJN" + envelope = ''' + + + + + 0 + + + + + + + + + ''' % quote_plus(href_id) + + (joinedmsg, numreplaces) = join_attachment(href_id, envelope, payload) + + soaptree = etree.fromstring(joinedmsg) + + body = soaptree.find("{%s}Body" % soaplib.ns_soap_env) + response = body.getchildren()[0] + result = response.getchildren()[0] + r = DownloadPartFileResult.from_xml(result) + + self.assertEquals(payload, r.Data) + +def suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(TestInclude) + +if __name__== '__main__': + unittest.TextTestRunner().run(suite()) diff -Nru python-soaplib-0.8.1/src/soaplib/test/serializers/test_primitive.py python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/test_primitive.py --- python-soaplib-0.8.1/src/soaplib/test/serializers/test_primitive.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/serializers/test_primitive.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import datetime +import unittest + +from lxml import etree + +import soaplib + +from soaplib.serializers.primitive import Array +from soaplib.serializers.primitive import Boolean +from soaplib.serializers.primitive import DateTime +from soaplib.serializers.primitive import Float +from soaplib.serializers.primitive import Integer +from soaplib.serializers.base import Null +from soaplib.serializers.primitive import String + +ns_test = 'test_namespace' + +class TestPrimitive(unittest.TestCase): + def test_string(self): + s = String() + element = String.to_xml('value', ns_test) + self.assertEquals(element.text, 'value') + value = String.from_xml(element) + self.assertEquals(value, 'value') + + def test_datetime(self): + d = DateTime() + n = datetime.datetime.now() + element = DateTime.to_xml(n, ns_test) + self.assertEquals(element.text, n.isoformat()) + dt = DateTime.from_xml(element) + self.assertEquals(n, dt) + + def test_utcdatetime(self): + datestring = '2007-05-15T13:40:44Z' + e = etree.Element('test') + e.text = datestring + + dt = DateTime.from_xml(e) + + self.assertEquals(dt.year, 2007) + self.assertEquals(dt.month, 5) + self.assertEquals(dt.day, 15) + + datestring = '2007-05-15T13:40:44.003Z' + e = etree.Element('test') + e.text = datestring + + dt = DateTime.from_xml(e) + + self.assertEquals(dt.year, 2007) + self.assertEquals(dt.month, 5) + self.assertEquals(dt.day, 15) + + def test_integer(self): + i = 12 + integer = Integer() + element = Integer.to_xml(i, ns_test) + self.assertEquals(element.text, '12') + self.assertEquals('xs:integer', element.get('{%s}type' % soaplib.ns_xsi)) + value = integer.from_xml(element) + self.assertEquals(value, i) + + def test_large_integer(self): + i = 128375873458473 + integer = Integer() + element = Integer.to_xml(i, ns_test) + self.assertEquals(element.text, '128375873458473') + self.assertEquals('xs:integer', element.get('{%s}type' % soaplib.ns_xsi)) + value = integer.from_xml(element) + self.assertEquals(value, i) + + def test_float(self): + f = 1.22255645 + + element = Float.to_xml(f, ns_test) + self.assertEquals(element.text, '1.22255645') + self.assertEquals('xs:float', element.get('{%s}type' % soaplib.ns_xsi)) + + f2 = Float.from_xml(element) + self.assertEquals(f2, f) + + def test_array(self): + serializer = Array(String) + serializer.resolve_namespace("zbank") + + values = ['a', 'b', 'c', 'd', 'e', 'f'] + + element = serializer.to_xml(values, ns_test) + self.assertEquals(len(values), len(element.getchildren())) + + values2 = serializer.from_xml(element) + self.assertEquals(values[3], values2[3]) + + def test_array_empty(self): + serializer = Array(String) + serializer.resolve_namespace("zbank") + + values = [] + + element = serializer.to_xml(values, ns_test) + self.assertEquals(len(values), len(element.getchildren())) + + values2 = serializer.from_xml(element) + self.assertEquals(len(values2), 0) + + def test_unicode(self): + s = u'\x34\x55\x65\x34' + self.assertEquals(4, len(s)) + element = String.to_xml(s, 'test_ns') + value = String.from_xml(element) + self.assertEquals(value, s) + + def test_null(self): + element = Null.to_xml('doesnt matter', ns_test) + self.assertTrue( bool(element.get('{%s}nil' % soaplib.ns_xsi)) ) + value = Null.from_xml(element) + self.assertEquals(None, value) + + def test_boolean(self): + b = Boolean.to_xml(True, ns_test) + self.assertEquals('true', b.text) + + b = Boolean.to_xml(0, ns_test) + self.assertEquals('false', b.text) + + b = Boolean.to_xml(1, ns_test) + self.assertEquals('true', b.text) + + b = Boolean.from_xml(b) + self.assertEquals(b, True) + + b = Boolean.to_xml(False, ns_test) + self.assertEquals('false', b.text) + + b = Boolean.from_xml(b) + self.assertEquals(b, False) + + b = Boolean.to_xml(False, ns_test) + self.assertEquals('xs:boolean', b.get('{%s}type' % soaplib.ns_xsi)) + + b = Boolean.to_xml(None, ns_test) + self.assertEquals('true', b.get('{%s}nil' % soaplib.ns_xsi)) + + b = Boolean.from_xml(b) + self.assertEquals(b, None) + +def suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(TestPrimitive) + +if __name__== '__main__': + unittest.TextTestRunner().run(suite()) diff -Nru python-soaplib-0.8.1/src/soaplib/test/test_service.py python-soaplib-0.9.3-alpha3/src/soaplib/test/test_service.py --- python-soaplib-0.8.1/src/soaplib/test/test_service.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/test_service.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,169 @@ +#!/usr/bin/env python +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import datetime +import unittest + +from lxml import etree + +from soaplib.serializers.clazz import ClassSerializer +from soaplib.serializers.primitive import Array +from soaplib.serializers.primitive import DateTime +from soaplib.serializers.primitive import Float +from soaplib.serializers.primitive import Integer +from soaplib.serializers.primitive import String + +from soaplib import service +from soaplib import wsgi +from soaplib.service import rpc + +class Address(ClassSerializer): + __namespace__ = "TestService" + + street = String + city = String + zip = Integer + since = DateTime + laditude = Float + longitude = Float + +class Person(ClassSerializer): + __namespace__ = "TestService" + + name = String + birthdate = DateTime + age = Integer + addresses = Array(Address) + titles = Array(String) + +class Request(ClassSerializer): + __namespace__ = "TestService" + + param1 = String + param2 = Integer + +class Response(ClassSerializer): + __namespace__ = "TestService" + + param1 = Float + +class TypeNS1(ClassSerializer): + __namespace__ = "TestService.NS1" + + s = String + i = Integer + +class TypeNS2(ClassSerializer): + __namespace__ = "TestService.NS2" + + d = DateTime + f = Float + +class MultipleNamespaceService(service.DefinitionBase): + @rpc(TypeNS1, TypeNS2) + def a(self, t1, t2): + return "OK" + +class MultipleNamespaceValidatingService(MultipleNamespaceService): + def __init__(self): + MultipleNamespaceService.__init__(self) + + self.validating_service = True + +class TestService(service.DefinitionBase): + @rpc(String, _returns=String) + def aa(self, s): + return s + + @rpc(String, Integer, _returns=DateTime) + def a(self, s, i): + return datetime.datetime.now() + + @rpc(Person, String, Address, _returns=Address) + def b(self, p, s, a): + return Address() + + @rpc(Person, isAsync=True) + def d(self, Person): + pass + + @rpc(Person, isCallback=True) + def e(self, Person): + pass + + @rpc(String, String, String, _returns=String, + _in_variable_names={'_from': 'from', '_self': 'self', + '_import': 'import'}, + _out_variable_name="return") + def f(self, _from, _self, _import): + return '1234' + +class MultipleReturnService(service.DefinitionBase): + @rpc(String, _returns=(String, String, String)) + def multi(self, s): + return s, 'a', 'b' + +class Test(unittest.TestCase): + ''' + Most of the service tests are excersized through the interop tests + ''' + + def setUp(self): + self.app = wsgi.Application([TestService], 'tns') + self.srv = TestService() + self._wsdl = self.app.get_wsdl('') + self.wsdl = etree.fromstring(self._wsdl) + + def test_portypes(self): + porttype = self.wsdl.find('{http://schemas.xmlsoap.org/wsdl/}portType') + self.assertEquals( + len(self.srv.public_methods), len(porttype.getchildren())) + + def test_override_param_names(self): + for n in ['self', 'import', 'return', 'from']: + self.assertTrue(n in self._wsdl, '"%s" not in self._wsdl' % n) + + def test_multiple_return(self): + app = wsgi.Application([MultipleReturnService], 'tns') + app.get_wsdl('') + srv = MultipleReturnService() + message = srv.public_methods[0].out_message() + + self.assertEquals(len(message._type_info), 3) + + sent_xml = message.to_xml( ('a','b','c'), srv.get_tns() ) + + print etree.tostring(sent_xml, pretty_print=True) + response_data = message.from_xml(sent_xml) + + self.assertEquals(len(response_data), 3) + self.assertEqual(response_data[0], 'a') + self.assertEqual(response_data[1], 'b') + self.assertEqual(response_data[2], 'c') + + def test_multiple_ns(self): + svc = wsgi.Application([MultipleNamespaceService], 'tns') + wsdl = svc.get_wsdl("URL") + +def suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__== '__main__': + unittest.TextTestRunner().run(suite()) diff -Nru python-soaplib-0.8.1/src/soaplib/test/test_soap.py python-soaplib-0.9.3-alpha3/src/soaplib/test/test_soap.py --- python-soaplib-0.8.1/src/soaplib/test/test_soap.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/test/test_soap.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,230 @@ +#!/usr/bin/env python +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import unittest + +from lxml import etree + +from soaplib.serializers.clazz import ClassSerializer + +from soaplib.serializers.exception import Fault +from soaplib.serializers.primitive import Array +from soaplib.serializers.primitive import DateTime +from soaplib.serializers.primitive import Float +from soaplib.serializers.primitive import Integer +from soaplib.serializers.primitive import String + +from soaplib.soap import Message +from soaplib.soap import from_soap +from soaplib.soap import make_soap_envelope + +class Address(ClassSerializer): + street = String + city = String + zip = Integer + since = DateTime + laditude = Float + longitude = Float + +class Person(ClassSerializer): + name = String + birthdate = DateTime + age = Integer + addresses = Array(Address) + titles = Array(String) + +class TestSoap(unittest.TestCase): + def test_simple_message(self): + m = Message.produce( + namespace=None, + type_name='myMessage', + members={'s': String, 'i': Integer} + ) + m.resolve_namespace('test') + + m_inst = m(s="a", i=43) + e = m.to_xml(m_inst,m.get_namespace()) + + self.assertEquals(e.tag, '{%s}myMessage' % m.get_namespace()) + + self.assertEquals(e.find('{%s}s' % m.get_namespace()).text, 'a') + self.assertEquals(e.find('{%s}i' % m.get_namespace()).text, '43') + + values = m.from_xml(e) + + self.assertEquals('a', values.s) + self.assertEquals(43, values.i) + + def test_href(self): + # the template. Start at pos 0, some servers complain if + # xml tag is not in the first line. + a = ''' + + + + + + + + + + + somemachine + someuser + + + machine2 + user2 + + +''' + + payload, header = from_soap(a, 'utf8') + # quick and dirty test href reconstruction + self.assertEquals(len(payload.getchildren()[0].getchildren()), 2) + + def test_namespaces(self): + m = Message.produce( + namespace="some_namespace", + type_name='myMessage', + members={'s': String, 'i': Integer}, + ) + + mi = m() + mi.s = 'a' + + e = m.to_xml(mi,m.get_namespace()) + self.assertEquals(e.tag, '{some_namespace}myMessage') + + def test_class_to_xml(self): + m = Message.produce( + namespace=None, + type_name='myMessage', + members={'p': Person} + ) + + m.resolve_namespace("punk") + + m.p = Person() + m.p.name = 'steve-o' + m.p.age = 2 + m.p.addresses = [] + + element = m.to_xml(m,m.get_namespace()) + + self.assertEquals(element.tag, '{%s}myMessage' % m.get_namespace()) + self.assertEquals(element.getchildren()[0].find('{%s}name' % m.get_namespace()).text, + 'steve-o') + self.assertEquals(element.getchildren()[0].find('{%s}age' % m.get_namespace()).text, '2') + self.assertEquals( + len(element.getchildren()[0].find('{%s}addresses' % m.get_namespace()).getchildren()), 0) + + p1 = m.from_xml(element)[0] + + self.assertEquals(p1.name, m.p.name) + self.assertEquals(p1.age, m.p.age) + self.assertEquals(p1.addresses, []) + + def test_to_xml_nested(self): + m = Message.produce( + namespace=None, + type_name='myMessage', + members={'p':Person} + ) + + m.resolve_namespace("m") + + p = Person() + p.name = 'steve-o' + p.age = 2 + p.addresses = [] + + for i in range(0, 100): + a = Address() + a.street = '123 happy way' + a.zip = i + a.laditude = '45.22' + a.longitude = '444.234' + p.addresses.append(a) + + m_inst = m(p=p) + + element = m.to_xml(m_inst,m.get_namespace()) + print etree.tostring(element, pretty_print=True) + self.assertEquals('{%s}myMessage' % m.get_namespace(), element.tag) + + addresses = element.getchildren()[0].find('{%s}addresses' % m.get_namespace()).getchildren() + self.assertEquals(100, len(addresses)) + self.assertEquals('0', addresses[0].find('{%s}zip' % m.get_namespace()).text) + + def test_soap_envelope(self): + m = Message.produce( + namespace=None, + type_name='myMessage', + members={'p': Person} + ) + env = make_soap_envelope(m.to_xml(Person(),m.get_namespace())) + + self.assertTrue(env.tag.endswith('Envelope')) + self.assertTrue(env.getchildren()[0].tag.endswith('Body')) + + m = Message.produce( + namespace=None, + type_name='myMessage', + members={'p': Person} + ) + env = make_soap_envelope(m.to_xml(Person(),m.get_namespace()), + header_elements=[etree.Element('header1'), etree.Element('header2')]) + + env = etree.fromstring(etree.tostring(env)) + + self.assertTrue(env.getchildren()[0].tag.endswith('Header')) + self.assertEquals(len(env.getchildren()[0].getchildren()), 2) + self.assertTrue(env.getchildren()[1].tag.endswith('Body')) + + def test_soap_fault(self): + ns_test = "test_namespace" + + fault = Fault("code", 'something happened', 'detail') + fault_str = etree.tostring(make_soap_envelope(Fault.to_xml(fault,ns_test)), pretty_print=True) + print fault_str + fault = etree.fromstring(fault_str) + + self.assertTrue(fault.getchildren()[0].tag.endswith, 'Body') + self.assertTrue( + fault.getchildren()[0].getchildren()[0].tag.endswith('Fault')) + f = fault.getchildren()[0].getchildren()[0] + + print etree.tostring(f,pretty_print=True) + + self.assertEquals(f.find('{%s}faultstring' % ns_test).text, 'something happened') + self.assertEquals(f.find('{%s}faultcode' % ns_test).text, 'code') + self.assertEquals(f.find('{%s}detail' % ns_test).text, 'detail') + +def suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(TestSoap) + +if __name__== '__main__': + unittest.TextTestRunner().run(suite()) diff -Nru python-soaplib-0.8.1/src/soaplib/util/etreeconv.py python-soaplib-0.9.3-alpha3/src/soaplib/util/etreeconv.py --- python-soaplib-0.8.1/src/soaplib/util/etreeconv.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/util/etreeconv.py 2010-08-25 12:12:00.000000000 +0000 @@ -0,0 +1,62 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +from lxml import etree + +from soaplib.util.odict import odict + +def root_dict_to_etree(d): + assert len(d) == 1 + + retval = etree.Element(d.keys()[0]) + dict_to_etree(retval, d.values()[0]) + + return retval + +def dict_to_etree(parent, d): + """the dict values are either dicts or iterables""" + + for k, v in d.items(): + if len(v) == 0: + etree.SubElement(parent,k) + + elif isinstance(v, dict) or isinstance(v, odict): + child = etree.SubElement(parent,k) + dict_to_etree(child,v) + + else: + for e in v: + child=etree.SubElement(parent,k) + if isinstance(e, dict) or isinstance(e, odict): + dict_to_etree(child,e) + else: + child.text=str(e) + +def etree_to_dict(element, iterable=(list,list.append)): + if (element.text is None) or element.text.isspace(): + retval = odict() + for elt in element: + if not (elt.tag in retval): + retval[elt.tag] = iterable[0]() + iterable[1](retval[elt.tag], etree_to_dict(elt, iterable)) + + else: + retval = element.text + + return retval diff -Nru python-soaplib-0.8.1/src/soaplib/util/__init__.py python-soaplib-0.9.3-alpha3/src/soaplib/util/__init__.py --- python-soaplib-0.8.1/src/soaplib/util/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/util/__init__.py 2010-08-11 11:07:34.000000000 +0000 @@ -0,0 +1,131 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import sys +import urllib +import soaplib +import urllib + +from lxml import etree + +def create_relates_to_header(relatesTo, attrs={}): + '''Creates a 'relatesTo' header for async callbacks''' + relatesToElement = etree.Element( + '{%s}RelatesTo' % soaplib.ns_wsa) + for k, v in attrs.items(): + relatesToElement.set(k, v) + relatesToElement.text = relatesTo + return relatesToElement + + +def create_callback_info_headers(message_id, reply_to): + '''Creates MessageId and ReplyTo headers for initiating an + async function''' + message_id = etree.Element('{%s}MessageID' % soaplib.ns_wsa) + message_id.text = message_id + + reply_to = etree.Element('{%s}ReplyTo' % soaplib.ns_wsa) + address = etree.SubElement(reply_to, '{%s}Address' % soaplib.ns_wsa) + address.text = reply_to + + return message_id, reply_to + +def get_callback_info(request): + ''' + Retrieves the messageId and replyToAddress from the message header. + This is used for async calls. + ''' + message_id = None + reply_to_address = None + header = request.soap_req_header + + if header: + headers = header.getchildren() + for header in headers: + if header.tag.lower().endswith("messageid"): + message_id = header.text + + if header.tag.lower().find("replyto") != -1: + replyToElems = header.getchildren() + + for replyTo in replyToElems: + if replyTo.tag.lower().endswith("address"): + reply_to_address = replyTo.text + + return message_id, reply_to_address + +def get_relates_to_info(request): + '''Retrieves the relatesTo header. This is used for callbacks''' + header = request.soap_req_header + if header: + headers = header.getchildren() + for header in headers: + if header.tag.lower().find('relatesto') != -1: + return header.text + +def split_url(url): + '''Splits a url into (uri_scheme, host[:port], path)''' + scheme, remainder = urllib.splittype(url) + host, path = urllib.splithost(remainder) + return scheme.lower(), host, path + +def reconstruct_url(environ): + ''' + Rebuilds the calling url from values found in the + environment. + + This algorithm was found via PEP 333, the wsgi spec and + contributed by Ian Bicking. + ''' + + url = environ['wsgi.url_scheme'] + '://' + + if environ.get('HTTP_HOST'): + url += environ['HTTP_HOST'] + + else: + url += environ['SERVER_NAME'] + + if environ['wsgi.url_scheme'] == 'https': + if environ['SERVER_PORT'] != '443': + url += ':' + environ['SERVER_PORT'] + + else: + if environ['SERVER_PORT'] != '80': + url += ':' + environ['SERVER_PORT'] + + if (urllib.quote(environ.get('SCRIPT_NAME', '')) == '/' and + urllib.quote(environ.get('PATH_INFO', ''))[0:1] == '/'): + #skip this if it is only a slash + pass + + elif urllib.quote(environ.get('SCRIPT_NAME', ''))[0:2] == '//': + url += urllib.quote(environ.get('SCRIPT_NAME', ''))[1:] + + else: + url += urllib.quote(environ.get('SCRIPT_NAME', '')) + + url += urllib.quote(environ.get('PATH_INFO', '')) + if environ.get('QUERY_STRING'): + url += '?' + environ['QUERY_STRING'] + + return url + +def check_pyversion(*minversion): + return sys.version_info[:3] >= minversion diff -Nru python-soaplib-0.8.1/src/soaplib/util/odict.py python-soaplib-0.9.3-alpha3/src/soaplib/util/odict.py --- python-soaplib-0.8.1/src/soaplib/util/odict.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/util/odict.py 2010-08-23 11:33:20.000000000 +0000 @@ -0,0 +1,104 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +class odict(object): + """ + Sort of an ordered dictionary implementation. + """ + + class Empty(object): + pass + + def __init__(self, data=[]): + if isinstance(data, self.__class__): + self.__list = list(data.__list) + self.__dict = dict(data.__dict) + + else: + self.__list = [] + self.__dict = {} + + self.update(data) + + def __getitem__(self, key): + if isinstance(key, int): + return self.__dict[self.__list[key]] + else: + return self.__dict[key] + + def __setitem__(self, key, val): + if isinstance(key, int): + self.__dict[self.__list[key]] = val + + else: + if not (key in self.__dict): + self.__list.append(key) + self.__dict[key] = val + + assert len(self.__list) == len(self.__dict), (repr(self.__list), + repr(self.__dict)) + + def __contains__(self, what): + return (what in self.__dict) + + def __repr__(self): + return "{%s}" % ','.join(["%r: %r" % (k,v) for k,v in self.items()]) + + def __str__(self): + return repr(self) + + def __len__(self): + assert len(self.__list) == len(self.__dict) + + return len(self.__list) + + def __iter__(self): + return iter(self.__list) + + def items(self): + for k in self.__list: + yield k, self.__dict[k] + + def keys(self): + return self.__list + + def update(self, data): + if isinstance(data, dict): + data = data.items() + + for k,v in data: + self[k] = v + + def values(self): + for l in self.__list: + yield self.__dict[l] + + def get(self, key, default=Empty): + if key in self.__dict: + return self[key] + + else: + if default is odict.Empty: + raise KeyError(key) + else: + return default + + def append(self, t): + k, v = t + self[k] = v diff -Nru python-soaplib-0.8.1/src/soaplib/util/server.py python-soaplib-0.9.3-alpha3/src/soaplib/util/server.py --- python-soaplib-0.8.1/src/soaplib/util/server.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/util/server.py 2010-08-08 20:54:52.000000000 +0000 @@ -0,0 +1,49 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import os +import logging +logger = logging.getLogger(__name__) + +import twisted.web.server +import twisted.web.static +from twisted.web.wsgi import WSGIResource +from twisted.internet import reactor + +def run_twisted(apps, port): + """ + takes a list of tuples containing application, url pairs, and a port to + to listen to. + """ + + static_dir = os.path.abspath(".") + logging.info("registering static folder %r on /" % static_dir) + root = twisted.web.static.File(static_dir) + + for app,url in apps: + resource = WSGIResource(reactor, reactor, app) + logging.info("registering %r on /%s" % (app, url)) + root.putChild(url, resource) + + site = twisted.web.server.Site(root) + + reactor.listenTCP(port, site) + logging.info("listening on: 0.0.0.0:%d" % port) + + return reactor.run() diff -Nru python-soaplib-0.8.1/src/soaplib/wsgi.py python-soaplib-0.9.3-alpha3/src/soaplib/wsgi.py --- python-soaplib-0.8.1/src/soaplib/wsgi.py 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib/wsgi.py 2010-08-25 17:31:44.000000000 +0000 @@ -0,0 +1,661 @@ + +# +# soaplib - Copyright (C) Soaplib contributors. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import cgi +import logging +import shutil +import tempfile +import traceback + +logger = logging.getLogger(__name__) + +from lxml import etree + +import soaplib + +from soaplib.serializers.exception import Fault +from soaplib.serializers.primitive import string_encoding + +from soaplib.soap import apply_mtom +from soaplib.soap import collapse_swa +from soaplib.soap import from_soap +from soaplib.soap import make_soap_envelope +from soaplib.util import reconstruct_url +from soaplib.util.etreeconv import etree_to_dict + +HTTP_500 = '500 Internal server error' +HTTP_200 = '200 OK' +HTTP_405 = '405 Method Not Allowed' + +class ValidationError(Fault): + pass + +class Application(object): + def __init__(self, services, tns, _with_partnerlink=False): + ''' + @param A ServiceBase subclass that defines the exposed services. + ''' + + self.services = services + self.call_routes = {} + + self.__wsdl = None + self.__public_methods = {} + self.schema = None + self.tns = tns + self._with_plink = _with_partnerlink + + self.build_schema() + + def get_tns(self): + if not (self.tns is None): + return self.tns + + service_name = self.__class__.__name__.split('.')[-1] + + retval = '.'.join((self.__class__.__module__, service_name)) + if self.__class__.__module__ == '__main__': + retval = '.'.join((service_name, service_name)) + + if retval.startswith('soaplib'): + retval = self.services[0].get_tns() + + return retval + + def __get_schema_node(self, pref, schema_nodes, types): + # create schema node + if not (pref in schema_nodes): + if types is None: + schema = etree.Element("{%s}schema" % soaplib.ns_xsd, + nsmap=soaplib.nsmap) + else: + schema = etree.SubElement(types, "{%s}schema" % soaplib.ns_xsd) + + schema.set("targetNamespace", soaplib.nsmap[pref]) + schema.set("elementFormDefault", "qualified") + + schema_nodes[pref] = schema + + else: + schema = schema_nodes[pref] + + return schema + + def __build_schema_nodes(self, schema_entries, types=None): + schema_nodes = {} + + for pref in schema_entries.namespaces: + schema = self.__get_schema_node(pref, schema_nodes, types) + + # append import tags + for namespace in schema_entries.imports[pref]: + import_ = etree.SubElement(schema, "{%s}import" % soaplib.ns_xsd) + import_.set("namespace", namespace) + if types is None: + import_.set('schemaLocation', "%s.xsd" % + soaplib.get_namespace_prefix(namespace)) + + # append element tags + for node in schema_entries.namespaces[pref].elements.values(): + schema.append(node) + + # append simpleType and complexType tags + for node in schema_entries.namespaces[pref].types.values(): + schema.append(node) + + return schema_nodes + + def build_schema(self, types=None): + if types is None: + # populate call routes + for s in self.services: + s.__tns__ = self.get_tns() + inst = self.get_service(s) + + for method in inst.public_methods: + method_name = "{%s}%s" % (self.get_tns(), method.name) + + if method_name in self.call_routes: + o = self.call_routes[method_name] + raise Exception("%s.%s.%s overwrites %s.%s.%s" % + (s.__module__, s.__name__, method.name, + o.__module__, o.__name__, method.name)) + + else: + logger.debug('adding method %r' % method_name) + self.call_routes[method_name] = s + self.call_routes[method.name] = s + + # populate types + schema_entries = None + for s in self.services: + inst = self.get_service(s) + schema_entries = inst.add_schema(schema_entries) + + schema_nodes = self.__build_schema_nodes(schema_entries, types) + + return schema_nodes + + def get_service_class(self, method_name): + return self.call_routes[method_name] + + def get_service(self, service, http_req_env=None): + return service(http_req_env) + + def get_schema(self): + if self.schema is None: + return self.build_schema() + else: + return self.schema + + def get_wsdl(self, url): + if self.__wsdl is None: + return self.__build_wsdl(url) + else: + return self.__wsdl + + def __is_wsdl_request(self, req_env): + # Get the wsdl for the service. Assume path_info matches pattern: + # /stuff/stuff/stuff/serviceName.wsdl or + # /stuff/stuff/stuff/serviceName/?wsdl + + return ( + req_env['REQUEST_METHOD'].lower() == 'get' + and ( + req_env['QUERY_STRING'].endswith('wsdl') + or req_env['PATH_INFO'].endswith('wsdl') + ) + ) + + def __build_wsdl(self, url): + ns_wsdl = soaplib.ns_wsdl + ns_soap = soaplib.ns_soap + ns_plink = soaplib.ns_plink + + ns_tns = self.get_tns() + pref_tns = 'tns' + soaplib.set_namespace_prefix(ns_tns, pref_tns) + + # FIXME: doesn't look so robust + url = url.replace('.wsdl', '') + + # TODO: we may want to customize service_name. + service_name = self.__class__.__name__.split('.')[-1] + + # create wsdl root node + root = etree.Element("{%s}definitions" % ns_wsdl, nsmap=soaplib.nsmap) + root.set('targetNamespace', ns_tns) + root.set('name', service_name) + + # create types node + types = etree.SubElement(root, "{%s}types" % ns_wsdl) + + self.build_schema(types) + messages = set() + + for s in self.services: + s=self.get_service(s,None) + + s.add_messages_for_methods(root, messages) + + if self._with_plink: + # create plink node + plink = etree.SubElement(root, '{%s}partnerLinkType' % ns_plink) + plink.set('name', service_name) + self.add_partner_link(root, service_name, types, url, plink) + + # create service node + service = etree.SubElement(root, '{%s}service' % ns_wsdl) + service.set('name', service_name) + self.add_service(root, service_name, types, url, service) + + # create portType node + port_type = etree.SubElement(root, '{%s}portType' % ns_wsdl) + port_type.set('name', service_name) + + # create binding nodes + binding = etree.SubElement(root, '{%s}binding' % ns_wsdl) + binding.set('name', service_name) + binding.set('type', '%s:%s'% (pref_tns, service_name)) + + soap_binding = etree.SubElement(binding, '{%s}binding' % ns_soap) + soap_binding.set('style', 'document') + soap_binding.set('transport', 'http://schemas.xmlsoap.org/soap/http') + + cb_binding = None + + for s in self.services: + s=self.get_service(s) + s.add_port_type(root, service_name, types, url, port_type) + cb_binding = s.add_bindings_for_methods(root, service_name, types, + url, binding, cb_binding) + + self.__wsdl = etree.tostring(root, xml_declaration=True, encoding="UTF-8", pretty_print=True) + + return self.__wsdl + + def add_service(self, root, service_name, types, url, service): + ns_wsdl = soaplib.ns_wsdl + ns_soap = soaplib.ns_soap + ns_tns = self.get_tns() + pref_tns = soaplib.get_namespace_prefix(ns_tns) + + wsdl_port = etree.SubElement(service, '{%s}port' % ns_wsdl) + wsdl_port.set('name', service_name) + wsdl_port.set('binding', '%s:%s' % (pref_tns, service_name)) + + addr = etree.SubElement(wsdl_port, '{%s}address' % ns_soap) + addr.set('location', url) + + def __handle_wsdl_request(self, req_env, start_response, url): + http_resp_headers = {'Content-Type': 'text/xml'} + + try: + self.get_wsdl(url) + self.on_wsdl(req_env, self.__wsdl) # implementation hook + + http_resp_headers['Content-Length'] = str(len(self.__wsdl)) + start_response(HTTP_200, http_resp_headers.items()) + + return [self.__wsdl] + + except Exception, e: + # implementation hook + logger.error(traceback.format_exc()) + + tns = self.get_tns() + fault_xml = Fault.to_xml(Fault(str(e)), tns) + fault_str = etree.tostring(fault_xml, + xml_declaration=True, encoding=string_encoding) + if logger.level == logging.DEBUG: + logger.debug(etree.tostring(etree.fromstring(fault_str),pretty_print=True)) + + self.on_wsdl_exception(req_env, e, fault_xml) + + http_resp_headers['Content-length'] = str(len(fault_str)) + start_response(HTTP_500, http_resp_headers.items()) + + return [fault_str] + + def __decode_soap_request(self, http_env, http_payload): + # decode body using information in the http header + # + # fyi, here's what the parse_header function returns: + # >>> import cgi; cgi.parse_header("text/xml; charset=utf-8") + # ('text/xml', {'charset': 'utf-8'}) + content_type = cgi.parse_header(http_env.get("CONTENT_TYPE")) + charset = content_type[1].get('charset',None) + if charset is None: + charset = 'ascii' + + http_payload = collapse_swa(content_type, http_payload) + + # deserialize the body of the message + req_payload, req_header = from_soap(http_payload, charset) + + return req_header, req_payload + + def validate_request(self, payload): + pass + + def __get_method_name(self, http_req_env, soap_req_payload): + retval = None + + if soap_req_payload is not None: + retval = soap_req_payload.tag + logger.debug("\033[92mMethod name from xml tag: %r\033[0m" % retval) + else: + # check HTTP_SOAPACTION + retval = http_req_env.get("HTTP_SOAPACTION") + + if retval is not None: + if retval.startswith('"') and retval.endswith('"'): + retval = retval[1:-1] + + if retval.find('/') >0: + retvals = retval.split('/') + retval = '{%s}%s' % (retvals[0], retvals[1]) + + logger.debug("\033[92m" + "Method name from HTTP_SOAPACTION: %r" + "\033[0m" % retval) + + return retval + + def add_partner_link(self, root, service_name, types, url, plink): + ns_plink = soaplib.ns_plink + ns_tns = self.get_tns() + pref_tns = soaplib.get_namespace_prefix(ns_tns) + + role = etree.SubElement(plink, '{%s}role' % ns_plink) + role.set('name', service_name) + + plink_port_type = etree.SubElement(role, '{%s}portType' % ns_plink) + plink_port_type.set('name', '%s:%s' % (pref_tns,service_name)) + + if self._has_callbacks(): + role = etree.SubElement(plink, '{%s}role' % ns_plink) + role.set('name', '%sCallback' % service_name) + + plink_port_type = etree.SubElement(role, '{%s}portType' % ns_plink) + plink_port_type.set('name', '%s:%sCallback' % + (pref_tns,service_name)) + + def _has_callbacks(self): + retval = False + + for s in self.services: + if self.get_service(s)._has_callbacks(): + return True + + return retval + + def __handle_soap_request(self, req_env, start_response, url): + http_resp_headers = { + 'Content-Type': 'text/xml', + 'Content-Length': '0', + } + method_name = None + + try: + # implementation hook + self.on_call(req_env) + + if req_env['REQUEST_METHOD'].lower() != 'post': + http_resp_headers['Allow'] = 'POST' + start_response(HTTP_405, http_resp_headers.items()) + return [''] + + input = req_env.get('wsgi.input') + length = req_env.get("CONTENT_LENGTH") + body = input.read(int(length)) + + try: + service = None + soap_req_header, soap_req_payload = self.__decode_soap_request( + req_env, body) + self.validate_request(soap_req_payload) + + method_name = self.__get_method_name(req_env, soap_req_payload) + if method_name is None: + resp = "Could not get method name!" + http_resp_headers['Content-Length'] = str(len(resp)) + start_response(HTTP_500, http_resp_headers.items()) + return [resp] + + service_class = self.get_service_class(method_name) + service = self.get_service(service_class, req_env) + + finally: + # for performance reasons, we don't want the following to run + # in production even if we don't see the results. + if logger.level == logging.DEBUG: + try: + logger.debug(etree.tostring(etree.fromstring(body), + pretty_print=True)) + except: + logger.debug(body) + raise + + # retrieve the method descriptor + descriptor = service.get_method(method_name) + func = getattr(service, descriptor.name) + + # decode header object + if soap_req_header is not None and len(soap_req_header) > 0: + service.soap_in_header = descriptor.in_header.from_xml(soap_req_header) + + # decode method arguments + if soap_req_payload is not None and len(soap_req_payload) > 0: + params = descriptor.in_message.from_xml(soap_req_payload) + else: + params = [None] * len(descriptor.in_message._type_info) + + # implementation hook + service.on_method_call(req_env, method_name, params, body) + + # call the method + result_raw = service.call_wrapper(func, params) + + # create result message + result_message = descriptor.out_message() + + # assign raw result to its wrapper, result_message + out_type = descriptor.out_message._type_info + + if len(out_type) > 0: + assert len(out_type) == 1 + + attr_name = descriptor.out_message._type_info.keys()[0] + setattr(result_message, attr_name, result_raw) + + # transform the results into an element + # only expect a single element + soap_resp_body = None + if not (descriptor.is_async or descriptor.is_callback): + soap_resp_body = descriptor.out_message.to_xml(result_message, + self.get_tns()) + soap_resp_header = None + if not (descriptor.out_header is None or service.soap_out_header is None): + soap_resp_header = descriptor.out_header.to_xml( + service.soap_out_header, self.get_tns(), + descriptor.out_header.get_type_name()) + + # implementation hook + service.on_method_return(req_env, result_raw, soap_resp_body, + http_resp_headers) + + # construct the soap response, and serialize it + envelope = make_soap_envelope(soap_resp_header, soap_resp_body) + results_str = etree.tostring(envelope, xml_declaration=True, + encoding=string_encoding) + + if descriptor.mtom: + http_resp_headers, results_str = apply_mtom(http_resp_headers, + results_str,descriptor.out_message._type_info,[result_raw]) + + # implementation hook + self.on_return(req_env, http_resp_headers, results_str) + + # initiate the response + http_resp_headers['Content-Length'] = str(len(results_str)) + start_response(HTTP_200, http_resp_headers.items()) + + if logger.level == logging.DEBUG: + logger.debug('\033[91m'+ "Response" + '\033[0m') + logger.debug(etree.tostring(envelope, xml_declaration=True, + pretty_print=True)) + + # return the serialized results + return [results_str] + + # The user issued a Fault, so handle it just like an exception! + except Fault, e: + stacktrace=traceback.format_exc() + logger.error(stacktrace) + + # FIXME: There's no way to alter soap response headers for the user. + + soap_resp_header = None + soap_resp_body = Fault.to_xml(e, self.get_tns()) + + fault_xml = make_soap_envelope(soap_resp_header, soap_resp_body) + fault_str = etree.tostring(fault_xml, xml_declaration=True, + encoding=string_encoding) + if logger.level == logging.DEBUG: + logger.debug(etree.tostring(etree.fromstring(fault_str), + pretty_print=True)) + + # implementation hook + if not (service is None): + service.on_method_exception(req_env, e, fault_xml, fault_str) + self.on_exception(req_env,e,fault_str) + + # initiate the response + http_resp_headers['Content-Length'] = str(len(fault_str)) + start_response(HTTP_500, http_resp_headers.items()) + + return [fault_str] + + except Exception, e: + # capture stacktrace + stacktrace=traceback.format_exc() + + # psycopg specific + if hasattr(e,'statement') and hasattr(e,'params'): + e.statement="" + e.params={} + + faultstring = str(e) + + if method_name: + faultcode = '%sFault' % method_name + else: + faultcode = 'Server' + + detail = ' ' + logger.error(stacktrace) + + fault = Fault(faultcode, faultstring, detail) + + soap_resp_header = None + soap_resp_body = Fault.to_xml(fault, self.get_tns()) + + fault_xml = make_soap_envelope(soap_resp_header, soap_resp_body) + fault_str = etree.tostring(fault_xml, xml_declaration=True, + encoding=string_encoding) + if logger.level == logging.DEBUG: + logger.debug(etree.tostring(etree.fromstring(fault_str), + pretty_print=True)) + + # implementation hook + if not (service is None): + service.on_method_exception(req_env, e, fault_xml, fault_str) + self.on_exception(req_env,e,fault_str) + + # initiate the response + http_resp_headers['Content-Length'] = str(len(fault_str)) + start_response(HTTP_500, http_resp_headers.items()) + + return [fault_str] + + def __call__(self, req_env, start_response, wsgi_url=None): + ''' + This method conforms to the WSGI spec for callable wsgi applications + (PEP 333). It looks in environ['wsgi.input'] for a fully formed soap + request envelope, will deserialize the request parameters and call the + method on the object returned by the get_handler() method. + + @param the http environment + @param a callable that begins the response message + @param the optional url + @returns the string representation of the soap call + ''' + + url = wsgi_url + if url is None: + url = reconstruct_url(req_env).split('.wsdl')[0] + + if self.__is_wsdl_request(req_env): + return self.__handle_wsdl_request(req_env, start_response, url) + else: + return self.__handle_soap_request(req_env, start_response, url) + + def on_call(self, environ): + ''' + This is the first method called when this WSGI app is invoked + @param the wsgi environment + ''' + pass + + def on_wsdl(self, environ, wsdl): + ''' + This is called when a wsdl is requested + + @param the wsgi environment + @param the wsdl string + ''' + pass + + def on_wsdl_exception(self, environ, exc, resp): + ''' + Called when an exception occurs durring wsdl generation + + @param the wsgi environment + @param exc the exception + @param the fault response string + ''' + pass + + def on_return(self, environ, http_headers, return_str): + ''' + Called before the application returns + + @param the wsgi environment + @param http response headers as dict + @param return string of the soap request + ''' + pass + + def on_exception(self, environ, fault, fault_str): + ''' + Called when the app throws an exception. (might be inside or outside the + service call. + + @param the wsgi environment + @param the fault + @param string of the fault + ''' + pass + +class ValidatingApplication(Application): + def build_schema(self, types=None): + schema_nodes = Application.build_schema(self, types) + + if types is None: + logger.debug("generating schema...") + tmp_dir_name = tempfile.mkdtemp() + + # serialize nodes to files + for k,v in schema_nodes.items(): + file_name = '%s/%s.xsd' % (tmp_dir_name, k) + f = open(file_name, 'w') + etree.ElementTree(v).write(f, pretty_print=True) + f.close() + logger.debug("writing %r for ns %s" % (file_name, soaplib.nsmap[k])) + + pref_tns = soaplib.get_namespace_prefix(self.get_tns()) + f = open('%s/%s.xsd' % (tmp_dir_name, pref_tns), 'r') + + logger.debug("building schema...") + self.schema = etree.XMLSchema(etree.parse(f)) + + logger.debug("schema %r built, cleaning up..." % self.schema) + f.close() + shutil.rmtree(tmp_dir_name) + logger.debug("removed %r" % tmp_dir_name) + + return self.schema + + def validate_request(self, payload): + schema = self.schema + ret = schema.validate(payload) + logger.debug("validation result: %s" % str(ret)) + if ret == False: + raise ValidationError(schema.error_log.last_error.message) diff -Nru python-soaplib-0.8.1/src/soaplib.egg-info/dependency_links.txt python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/dependency_links.txt --- python-soaplib-0.8.1/src/soaplib.egg-info/dependency_links.txt 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/dependency_links.txt 2010-08-25 19:27:49.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru python-soaplib-0.8.1/src/soaplib.egg-info/not-zip-safe python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/not-zip-safe --- python-soaplib-0.8.1/src/soaplib.egg-info/not-zip-safe 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/not-zip-safe 2010-07-26 16:26:47.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru python-soaplib-0.8.1/src/soaplib.egg-info/PKG-INFO python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/PKG-INFO --- python-soaplib-0.8.1/src/soaplib.egg-info/PKG-INFO 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/PKG-INFO 2010-08-25 19:27:49.000000000 +0000 @@ -0,0 +1,25 @@ +Metadata-Version: 1.0 +Name: soaplib +Version: 0.9.3-alpha3 +Summary: A simple library for writing soap web services +Home-page: http://github.com/arskom/soaplib +Author: Burak Arslan +Author-email: burak-soaplib@arskom.com.tr +License: LGPL +Description: This is a simple, easily extendible soap library that provides several useful + tools for creating and publishing soap web services in python. This package + features on-demand wsdl generation for the published services, a + wsgi-compliant web application, support for complex class structures, binary + attachments, simple framework for creating additional serialization mechanisms + and a client library. + + This prokect now uses lxml as it's XML API, providing full namespace support. + +Keywords: soap,wsdl,wsgi +Platform: UNKNOWN +Classifier: Programming Language :: Python +Classifier: Operating System :: OS Independent +Classifier: Natural Language :: English +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content diff -Nru python-soaplib-0.8.1/src/soaplib.egg-info/requires.txt python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/requires.txt --- python-soaplib-0.8.1/src/soaplib.egg-info/requires.txt 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/requires.txt 2010-08-25 19:27:49.000000000 +0000 @@ -0,0 +1,2 @@ +pytz +lxml>=2.2.1 \ No newline at end of file diff -Nru python-soaplib-0.8.1/src/soaplib.egg-info/SOURCES.txt python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/SOURCES.txt --- python-soaplib-0.8.1/src/soaplib.egg-info/SOURCES.txt 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/SOURCES.txt 2010-08-25 19:27:50.000000000 +0000 @@ -0,0 +1,40 @@ +setup.cfg +setup.py +src/soaplib/__init__.py +src/soaplib/service.py +src/soaplib/soap.py +src/soaplib/wsgi.py +src/soaplib.egg-info/PKG-INFO +src/soaplib.egg-info/SOURCES.txt +src/soaplib.egg-info/dependency_links.txt +src/soaplib.egg-info/not-zip-safe +src/soaplib.egg-info/requires.txt +src/soaplib.egg-info/top_level.txt +src/soaplib/serializers/__init__.py +src/soaplib/serializers/base.py +src/soaplib/serializers/binary.py +src/soaplib/serializers/clazz.py +src/soaplib/serializers/enum.py +src/soaplib/serializers/exception.py +src/soaplib/serializers/primitive.py +src/soaplib/serializers/table.py +src/soaplib/test/__init__.py +src/soaplib/test/test_service.py +src/soaplib/test/test_soap.py +src/soaplib/test/interop/__init__.py +src/soaplib/test/interop/test_suds.py +src/soaplib/test/interop/test_wsi.py +src/soaplib/test/interop/server/__init__.py +src/soaplib/test/interop/server/_service.py +src/soaplib/test/interop/server/basic.py +src/soaplib/test/interop/server/static.py +src/soaplib/test/serializers/__init__.py +src/soaplib/test/serializers/test_binary.py +src/soaplib/test/serializers/test_clazz.py +src/soaplib/test/serializers/test_enum.py +src/soaplib/test/serializers/test_include.py +src/soaplib/test/serializers/test_primitive.py +src/soaplib/util/__init__.py +src/soaplib/util/etreeconv.py +src/soaplib/util/odict.py +src/soaplib/util/server.py \ No newline at end of file diff -Nru python-soaplib-0.8.1/src/soaplib.egg-info/top_level.txt python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/top_level.txt --- python-soaplib-0.8.1/src/soaplib.egg-info/top_level.txt 1970-01-01 00:00:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/src/soaplib.egg-info/top_level.txt 2010-08-25 19:27:49.000000000 +0000 @@ -0,0 +1 @@ +soaplib diff -Nru python-soaplib-0.8.1/tests/client_test.py python-soaplib-0.9.3-alpha3/tests/client_test.py --- python-soaplib-0.8.1/tests/client_test.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/tests/client_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,190 +0,0 @@ -import unittest -import datetime -from soaplib.etimport import ElementTree - -from soaplib.serializers.primitive import * -from soaplib.serializers.clazz import * -from soaplib.service import * -from soaplib.wsgi_soap import * -from soaplib.client import * -from soaplib.util import * -from soaplib.soap import * - -from threading import Thread -try: - from wsgiref.simple_server import make_server -except ImportError: - raise Exception("UnitTests require Python >= 2.5") - -class Address(ClassSerializer): - class types: - street = String - city = String - zip = Integer - since = DateTime - laditude = Float - longitude = Float - -class Person(ClassSerializer): - class types: - name = String - birthdate = DateTime - age = Integer - addresses = Array(Address) - titles = Array(String) - -class Request(ClassSerializer): - class types: - param1 = String - param2 = Integer - -class Response(ClassSerializer): - class types: - param1 = Float - -class TestService(SimpleWSGISoapApp): - - @soapmethod(String, Integer, _returns=DateTime) - def a(self, s, i): - return datetime.datetime(1901,12,15) - - @soapmethod(Person, String, Integer, _returns=Address) - def b(self, p,s,i): - a = Address() - a.zip = 4444 - a.street = 'wsgi way' - a.laditude = 123.3 - - return a - - @soapmethod(Person, _isAsync=True) - def d(self, person): - pass - - @soapmethod(Person, _isCallback=True) - def e(self, person): - pass - - @soapmethod() - def fault(self): - raise Exception('Testing faults') - -class test(unittest.TestCase): - - def setUp(self): - self.server = make_server('127.0.0.1', 9191, TestService()) - self.server.allow_reuse_address = True - Thread(target=self.server.serve_forever).start() - - def tearDown(self): - self.server.shutdown() - del self.server - - def test_simple(self): - inMessage = Message('a',[('s',String),('i',Integer)]) - outMessage = Message('aResponse',[('retval',DateTime)]) - - desc = MethodDescriptor('a','a',inMessage,outMessage,'') - - client = SimpleSoapClient('127.0.0.1:9191','/',desc) - results = client('abc',54) - self.assertEquals(results,datetime.datetime(1901,12,15)) - - def test_nested(self): - inMessage = Message('b',[('p',Person),('s',String),('i',Integer)]) - outMessage = Message('bResponse',[('retval',Address)]) - - desc = MethodDescriptor('b','b',inMessage,outMessage,'') - - client = SimpleSoapClient('127.0.0.1:9191','/',desc) - p = Person() - p.name = 'wilson' - p.addresses = [] - for i in range(0,123): - a = Address() - a.zip = i - p.addresses.append(a) - res = client(p,'abc',123) - self.assertEquals(res.longitude,None) - self.assertEquals(res.zip,4444) - self.assertEquals(res.street,'wsgi way') - - def test_async(self): - inMessage = Message('d',[('person',Person)]) - outMessage = Message('dResponse',[]) - - desc = MethodDescriptor('d','d',inMessage,outMessage,'') - - client = SimpleSoapClient('127.0.0.1:9191','/',desc) - p = Person() - p.name = 'wilson' - r = client(p) - self.assertEquals(r,None) - - def test_fault(self): - inMessage = Message('fault',[]) - outMessage = Message('faultResponse',[]) - desc = MethodDescriptor('fault','fault',inMessage,outMessage,'') - - client = SimpleSoapClient('127.0.0.1:9191','/',desc) - try: - client() - except Fault, f: - self.assertEquals(f.faultcode,'faultFault') - self.assertEquals(f.faultstring,'Testing faults') - self.assertTrue(f.detail.find('client_test.py') > -1) - else: - raise - - def _test_callback(self): - inputs = [ParameterDescriptor('person',Person)] - - client = SimpleSoapClient('127.0.0.1:9191','/','e',inputs,None) - p = Person() - p.name = 'wilson' - r = client(p) - self.assertEquals(r,None) - - def test_service_client(self): - client = ServiceClient('127.0.0.1:9191','/',TestService()) - - r = client.a('bobo',23) - self.assertEquals(r,datetime.datetime(1901, 12, 15)) - - p = Person() - p.name = 'wilson' - p.addresses = [] - for i in range(0,123): - a = Address() - a.zip = i - p.addresses.append(a) - res = client.b(p,'abc',123) - self.assertEquals(res.longitude,None) - self.assertEquals(res.zip,4444) - self.assertEquals(res.street,'wsgi way') - - request = Request() - request.param1 = 'asdf' - - p = Person() - p.name = 'wilson' - r = client.d(p) - self.assertEquals(r,None) - - p = Person() - p.name = 'wilson' - r = client.e(p) - self.assertEquals(r,None) - -def test_suite(): - #debug(True) - loader = unittest.TestLoader() - #log_debug(True) - return loader.loadTestsFromTestCase(test) - -if __name__== '__main__': - unittest.TextTestRunner().run(test_suite()) - - - - diff -Nru python-soaplib-0.8.1/tests/__init__.py python-soaplib-0.9.3-alpha3/tests/__init__.py --- python-soaplib-0.8.1/tests/__init__.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -import unittest - -import serializers -import client_test -import soap_test -import service_test - -def test_suite(): - suite = serializers.test_suite() - #suite.addTests(client_test.test_suite()) - suite.addTests(soap_test.test_suite()) - suite.addTests(service_test.test_suite()) - return suite - -if __name__== '__main__': - unittest.TextTestRunner().run(test_suite()) \ No newline at end of file diff -Nru python-soaplib-0.8.1/tests/interop_service.py python-soaplib-0.9.3-alpha3/tests/interop_service.py --- python-soaplib-0.8.1/tests/interop_service.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/tests/interop_service.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -from soaplib.service import soapmethod -from soaplib.wsgi_soap import SimpleWSGISoapApp, log_debug, log_exceptions -from soaplib.serializers.primitive import String, Integer, DateTime, Float, Boolean, Array, Any -from soaplib.serializers.clazz import ClassSerializer -from soaplib.serializers.binary import Attachment - -class SimpleClass(ClassSerializer): - class types: - i = Integer - s = String - -class OtherClass(ClassSerializer): - class types: - dt = DateTime - f = Float - b = Boolean - -class NestedClass(ClassSerializer): - class types: - simple = Array(SimpleClass) - s = String - i = Integer - f = Float - other = OtherClass - -class InteropService(SimpleWSGISoapApp): - - # basic primitives - @soapmethod(Integer, _returns=Integer) - def echoInteger(self, i): - return i - - @soapmethod(String, _returns=String) - def echoString(self,s): - return s - - @soapmethod(DateTime, _returns=DateTime) - def echoDateTime(self,dt): - return dt - - @soapmethod(Float,_returns=Float) - def echoFloat(self,f): - return f - - @soapmethod(Boolean,_returns=Boolean) - def echoBoolean(self,b): - return b - - # lists of primitives - @soapmethod(Array(Integer), _returns=Array(Integer)) - def echoIntegerArray(self,ia): - return ia - - @soapmethod(Array(String), _returns=Array(String)) - def echoStringArray(self, sa): - return sa - - @soapmethod(Array(DateTime), _returns=Array(DateTime)) - def echoDateTimeArray(self,dta): - return dta - - @soapmethod(Array(Float),_returns=Array(Float)) - def echoFloatArray(self,fa): - return fa - - @soapmethod(Array(Boolean),_returns=Array(Boolean)) - def echoBooleanArray(self,ba): - return ba - - # classses - @soapmethod(SimpleClass,_returns=SimpleClass) - def echoSimpleClass(self,sc): - return sc - - @soapmethod(Array(SimpleClass),_returns=Array(SimpleClass)) - def echoSimpleClassArray(self,sca): - return sca - - @soapmethod(NestedClass,_returns=NestedClass) - def echoNestedClass(self,nc): - return nc - - @soapmethod(Array(NestedClass),_returns=Array(NestedClass)) - def echoNestedClassArray(self,nca): - return nca - - @soapmethod(Attachment,_returns=Attachment) - def echoAttachment(self,a): - return a - - @soapmethod(Array(Attachment),_returns=Array(Attachment)) - def echoAttachmentArray(self,aa): - return aa - - @soapmethod() - def testEmpty(self): - # new - pass - - @soapmethod(String,Integer,DateTime) - def multiParam(self,s,i,dt): - # new - pass - - @soapmethod(_returns=String) - def returnOnly(self): - # new - return 'howdy' - - @soapmethod(String,_returns=String,_soapAction="http://sample.org/webservices/doSomething") - def doSomethingElse(self,s): - return s - -if __name__ == '__main__': - try: - from wsgiref.simple_server import make_server - log_debug(True) - log_exceptions(True) - server = make_server('127.0.0.1',9753, InteropService()) - print 'Starting interop server at -- %s:%s' % ('127.0.0.1',9753) - server.serve_forever() - except ImportError: - print "Error: example server code requires Python >= 2.5" - diff -Nru python-soaplib-0.8.1/tests/serializers/binary_test.py python-soaplib-0.9.3-alpha3/tests/serializers/binary_test.py --- python-soaplib-0.8.1/tests/serializers/binary_test.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/tests/serializers/binary_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -import unittest -import shutil -import base64 -from soaplib.serializers.binary import * -from soaplib.xml import NamespaceLookup -from tempfile import mkstemp -import os, shutil - - -class test(unittest.TestCase): - - def setUp(self): - os.mkdir('binaryDir') - - fd, self.tmpfile = mkstemp('','','binaryDir') - os.close(fd) - f = open(self.tmpfile,'w') - for i in range(0,1000): - f.write('All work and no play makes jack a dull boy\r\n') - f.flush() - f.close() - - def tearDown(self): - shutil.rmtree('binaryDir') - - def test_to_xml_data(self): - f = open(self.tmpfile) - data = f.read() - f.close() - - a = Attachment() - a.data = data - element = Attachment.to_xml(a) - - encoded_data = base64.encodestring(data) - - self.assertNotEquals(element.text,None) - self.assertEquals(element.text,encoded_data) - - def test_to_xml_file(self): - a = Attachment() - a.fileName = self.tmpfile - - f = open(self.tmpfile,'rb') - data = f.read() - f.close() - - element = Attachment.to_xml(a) - encoded_data = base64.encodestring(data) - - self.assertNotEquals(element.text,None) - self.assertEquals(element.text,encoded_data) - - def test_to_from_xml_file(self): - a = Attachment() - a.fileName = self.tmpfile - - - element = Attachment.to_xml(a) - data = Attachment.from_xml(element).data - - - f = open(self.tmpfile,'rb') - fdata = f.read() - f.close() - - self.assertEquals(data, fdata) - - - def test_exception(self): - try: - Attachment.to_xml(Attachment()) - except: - self.assertTrue(True) - else: - self.assertFalse(True) - - def test_from_xml(self): - f = open(self.tmpfile) - data = f.read() - f.close() - - a = Attachment() - a.data = data - element = Attachment.to_xml(a) - - a2 = Attachment.from_xml(element) - self.assertEquals(data,a2.data) - - def test_add_to_schema(self): - schema = {} - Attachment.add_to_schema(schema, NamespaceLookup()) - self.assertEquals(0,len(schema.keys())) - - def test_get_datatype(self): - dt = Attachment.get_datatype() - self.assertEquals('base64Binary', dt) - dt = Attachment.get_datatype(ns) - - self.assertEquals(ns.get('xs') + 'base64Binary', dt) - - -def test_suite(): - loader = unittest.TestLoader() - return loader.loadTestsFromTestCase(test) - -if __name__== '__main__': - unittest.TextTestRunner().run(test_suite()) - - - diff -Nru python-soaplib-0.8.1/tests/serializers/clazz_test.py python-soaplib-0.9.3-alpha3/tests/serializers/clazz_test.py --- python-soaplib-0.8.1/tests/serializers/clazz_test.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/tests/serializers/clazz_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,218 +0,0 @@ -import unittest -import datetime -from soaplib.xml import NamespaceLookup -from soaplib.serializers.primitive import * -from soaplib.serializers.clazz import * - - -########################################################## -# Simple Classes -########################################################## - -class Address(ClassSerializer): - class types: - street = String - city = String - zip = Integer - since = DateTime - laditude = Float - longitude = Float - -class Person(ClassSerializer): - class types: - name = String - birthdate = DateTime - age = Integer - addresses = Array(Address) - titles = Array(String) - -class ClassWithRepeatingData(ClassSerializer): - class types: - people = Repeating(Person) - -########################################################## -# Complex Classes -########################################################## - - -class Level2(ClassSerializer): - class types: - arg1 = String - arg2 = Float - -class Level3(ClassSerializer): - class types: - arg1 = Integer - -class Level4(ClassSerializer): - class types: - arg1 = String - -class Level1(ClassSerializer): - class types: - level2 = Level2 - level3 = Array(Level3) - level4 = Array(Level4) - - - - -class test(unittest.TestCase): - - def test_simple_class(self): - a = Address() - a.street = '123 happy way' - a.city = 'badtown' - a.zip = 32 - a.laditude = 4.3 - a.longitude = 88.0 - - element = Address.to_xml(a) - self.assertEquals(6,len(element.getchildren())) - - r = Address.from_xml(element) - - self.assertEquals(a.street,r.street) - self.assertEquals(a.city,r.city) - self.assertEquals(a.zip,r.zip) - self.assertEquals(a.laditude,r.laditude) - self.assertEquals(a.longitude,r.longitude) - self.assertEquals(a.since,r.since) - - def test_nested_class(self): - p = Person() - element = Person.to_xml(p) - - self.assertEquals(None,p.name) - self.assertEquals(None,p.birthdate) - self.assertEquals(None,p.age) - self.assertEquals(None,p.addresses) - - p2 = Person() - - def test_class_array(self): - - peeps = [] - names = ['bob','jim','peabody','mumblesleves'] - for name in names: - a = Person() - a.name = name - a.birthdate = datetime.datetime(1979,1,1) - a.age = 27 - peeps.append(a) - - serializer = Array(Person) - element = serializer.to_xml(peeps) - - self.assertEquals(4,len(element.getchildren())) - - peeps2 = serializer.from_xml(element) - for i in range(0,4): - self.assertEquals(peeps[i].name,names[i]) - self.assertEquals(peeps[i].birthdate,datetime.datetime(1979,1,1)) - - def test_class_nested_array(self): - peeps = [] - names = ['bob','jim','peabody','mumblesleves'] - for name in names: - a = Person() - a.name = name - a.birthdate = datetime.datetime(1979,1,1) - a.age = 27 - a.addresses = [] - - for i in range(0,25): - addr = Address() - addr.street = '555 downtown' - addr.city = 'funkytown' - a.addresses.append(addr) - - peeps.append(a) - - - serializer = Array(Person) - element = serializer.to_xml(peeps) - - self.assertEquals(4,len(element.getchildren())) - - peeps2 = serializer.from_xml(element) - for peep in peeps2: - self.assertEquals(27,peep.age) - self.assertEquals(25,len(peep.addresses)) - self.assertEquals('funkytown',peep.addresses[18].city) - - - def test_complex_class(self): - l = Level1() - - l.level2 = Level2() - l.level2.arg1 = 'abcd' - l.level2.arg2 = 1.444 - - - l.level3 = [] - l.level4 = [] - - for i in range(0,100): - a = Level3() - a.arg1 = i - l.level3.append(a) - - for i in range(0,4): - a = Level4() - a.arg1 = str(i) - l.level4.append(a) - - element = Level1.to_xml(l) - - l1 = Level1.from_xml(element) - - self.assertEquals(l1.level2.arg1,l.level2.arg1) - self.assertEquals(l1.level2.arg2,l.level2.arg2) - self.assertEquals(len(l1.level4),len(l.level4)) - self.assertEquals(100,len(l.level3)) - - def test_schema(self): - a = {} - Person.add_to_schema(a, NamespaceLookup()) - #self.assertEquals(8,len(a)) - self.assertTrue(a.has_key(ns.get('tns') + "Person")) - self.assertTrue(a.has_key(ns.get('tns') + "Address")) - self.assertTrue(a.has_key(ns.get('tns') + "AddressArray")) - - def test_repeating(self): - - peeps = [] - names = ['bob','jim','peabody','mumblesleves'] - for name in names: - a = Person() - a.name = name - a.birthdate = datetime.datetime(1979,1,1) - a.age = 27 - a.addresses = [] - - for i in range(0,25): - addr = Address() - addr.street = '555 downtown' - addr.city = 'funkytown' - a.addresses.append(addr) - - peeps.append(a) - - rpt = ClassWithRepeatingData() - rpt.people = peeps - - e = ClassWithRepeatingData.to_xml(rpt) - rpt2 = ClassWithRepeatingData.from_xml(e) - - self.assertEquals(len(rpt2.people),len(rpt.people)) - - -def test_suite(): - loader = unittest.TestLoader() - return loader.loadTestsFromTestCase(test) - -if __name__== '__main__': - unittest.TextTestRunner().run(test_suite()) - - \ No newline at end of file diff -Nru python-soaplib-0.8.1/tests/serializers/__init__.py python-soaplib-0.9.3-alpha3/tests/serializers/__init__.py --- python-soaplib-0.8.1/tests/serializers/__init__.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/tests/serializers/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -import unittest - -import primitive_test -import clazz_test -import binary_test - -def test_suite(): - loader = unittest.TestLoader() - suite = loader.loadTestsFromTestCase(primitive_test.test) - suite.addTests(clazz_test.test_suite()) - suite.addTests(binary_test.test_suite()) - return suite - -if __name__== '__main__': - unittest.TextTestRunner().run(test_suite()) \ No newline at end of file diff -Nru python-soaplib-0.8.1/tests/serializers/primitive_test.py python-soaplib-0.9.3-alpha3/tests/serializers/primitive_test.py --- python-soaplib-0.8.1/tests/serializers/primitive_test.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/tests/serializers/primitive_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,131 +0,0 @@ -import unittest -import datetime -from soaplib.etimport import ElementTree -from soaplib.xml import ns, create_xml_element -from soaplib.serializers.primitive import * - -class test(unittest.TestCase): - - def test_string(self): - s = String() - element = String.to_xml('value') - self.assertEquals(element.text,'value') - value = String.from_xml(element) - self.assertEquals(value,'value') - - def test_datetime(self): - d = DateTime() - n = datetime.datetime.now() - element = DateTime.to_xml(n) - self.assertEquals(element.text,n.isoformat()) - dt = DateTime.from_xml(element) - self.assertEquals(n,dt) - - def test_utcdatetime(self): - datestring = '2007-05-15T13:40:44Z' - e = create_xml_element('test', ns) - e.text = datestring - - dt = DateTime.from_xml(e) - - self.assertEquals(dt.year,2007) - self.assertEquals(dt.month,5) - self.assertEquals(dt.day,15) - - datestring = '2007-05-15T13:40:44.003Z' - e = create_xml_element('test', ns) - e.text = datestring - - dt = DateTime.from_xml(e) - - self.assertEquals(dt.year,2007) - self.assertEquals(dt.month,5) - self.assertEquals(dt.day,15) - - - def test_integer(self): - i = 12 - integer = Integer() - element = Integer.to_xml(i) - self.assertEquals(element.text,'12') - self.assertEquals('xs:int', element.get(ns.get('xsi') + 'type')) - value = integer.from_xml(element) - self.assertEquals(value,i) - - def test_float(self): - f = 1.22255645 - element = Float.to_xml(f) - self.assertEquals(element.text,'1.22255645') - self.assertEquals('xs:float', element.get(ns.get('xsi') + 'type')) - f2 = Float.from_xml(element) - self.assertEquals(f2,f) - - def test_array(self): - serializer = Array(String) - values = ['a','b','c','d','e','f'] - element = serializer.to_xml(values) - self.assertEquals(len(values),len(element.getchildren())) - values2 = serializer.from_xml(element) - self.assertEquals(values[3],values2[3]) - - def test_unicode(self): - s = u'\x34\x55\x65\x34' - self.assertEquals(4,len(s)) - element = String.to_xml(s) - value = String.from_xml(element) - self.assertEquals(value,s) - - def test_null(self): - element = Null.to_xml('doesnt matter') - self.assertEquals('1',element.get(ns.get('xs') + 'null')) - value = Null.from_xml(element) - self.assertEquals(None,value) - - def test_boolean(self): - b = Boolean.to_xml(True) - self.assertEquals('true',b.text) - - b = Boolean.to_xml(0) - self.assertEquals('false',b.text) - - b = Boolean.to_xml(1) - self.assertEquals('true',b.text) - - b = Boolean.from_xml(b) - self.assertEquals(b,True) - - b = Boolean.to_xml(False) - self.assertEquals('false',b.text) - - b = Boolean.from_xml(b) - self.assertEquals(b,False) - - b = Boolean.to_xml(False) - self.assertEquals('xs:boolean', b.get(ns.get('xsi') + 'type')) - - b = Boolean.to_xml(None) - self.assertEquals('1', b.get(ns.get('xs') + 'null')) - - b = Boolean.from_xml(b) - self.assertEquals(b,None) - - def test_repeating(self): - serializer = Repeating(String) - - data = ["a","b","c","d"] - - elements = serializer.to_xml(data) - self.assertEquals(len(elements),4) - - newdata = serializer.from_xml(*elements) - - self.assertEquals(data,newdata) - - -def test_suite(): - loader = unittest.TestLoader() - return loader.loadTestsFromTestCase(test) - -if __name__== '__main__': - unittest.TextTestRunner().run(test_suite()) - diff -Nru python-soaplib-0.8.1/tests/service_test.py python-soaplib-0.9.3-alpha3/tests/service_test.py --- python-soaplib-0.8.1/tests/service_test.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/tests/service_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,101 +0,0 @@ -import unittest -import datetime -from soaplib.etimport import ElementTree - -from soaplib.serializers.primitive import * -from soaplib.serializers.clazz import * -from soaplib.service import * -from soaplib.wsgi_soap import * - -class Address(ClassSerializer): - class types: - street = String - city = String - zip = Integer - since = DateTime - laditude = Float - longitude = Float - -class Person(ClassSerializer): - class types: - name = String - birthdate = DateTime - age = Integer - addresses = Array(Address) - titles = Array(String) - - -class Request(ClassSerializer): - class types: - param1 = String - param2 = Integer - -class Response(ClassSerializer): - class types: - param1 = Float - -class TestService(SoapServiceBase): - - @soapmethod(String,_returns=String) - def aa(self,s): - return s - - @soapmethod(String, Integer, _returns=DateTime) - def a(self, s, i): - return datetime.datetime.now() - - @soapmethod(Person, String, Address, _returns=Address) - def b(self, p,s,a): - return Address() - - @soapmethod(Person, isAsync=True) - def d(self, Person): - pass - - @soapmethod(Person, isCallback=True) - def e(self, Person): - pass - - @soapmethod(String, String, String, _returns=String, - _inputVariableNames={'_from':'from','_self':'self','_import':'import'}, - _outVariableName="return") - def f(self,_from, _self, _import): - return '1234' - -class OverrideNamespaceService(SimpleWSGISoapApp): - __tns__ = "http://someservice.com/override" - - @soapmethod(String,_returns=String) - def mymethod(self,s): - return s - -class test(unittest.TestCase): - ''' - Most of the service tests are excersized through the interop tests - ''' - - def setUp(self): - self.service = TestService() - self._wsdl = self.service.wsdl('') - self.wsdl = ElementTree.fromstring(self._wsdl) - - def test_portypes(self): - porttype = self.wsdl.find('{http://schemas.xmlsoap.org/wsdl/}portType') - self.assertEquals(len(self.service._soap_methods),len(porttype.getchildren())) - - def test_override_default_names(self): - wsdl = ElementTree.fromstring(OverrideNamespaceService().wsdl('')) - self.assertEquals(wsdl.get('targetNamespace'),"http://someservice.com/override") - - def test_override_param_names(self): - for n in ['self','import','return','from']: - self.assertTrue(n in self._wsdl) - - -def test_suite(): - loader = unittest.TestLoader() - return loader.loadTestsFromTestCase(test) - -if __name__== '__main__': - unittest.TextTestRunner().run(test_suite()) - diff -Nru python-soaplib-0.8.1/tests/soap_test.py python-soaplib-0.9.3-alpha3/tests/soap_test.py --- python-soaplib-0.8.1/tests/soap_test.py 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/tests/soap_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,188 +0,0 @@ -import unittest -import datetime -from soaplib.etimport import ElementTree as et - -from soaplib.serializers.primitive import Integer, String, Repeating -from soaplib.serializers.clazz import ClassSerializer -from soaplib.serializers.primitive import String, Integer, DateTime, Float, Array -from soaplib.soap import Message, MethodDescriptor, make_soap_envelope, make_soap_fault, from_soap - -class Address(ClassSerializer): - class types: - street = String - city = String - zip = Integer - since = DateTime - laditude = Float - longitude = Float - -class Person(ClassSerializer): - class types: - name = String - birthdate = DateTime - age = Integer - addresses = Array(Address) - titles = Array(String) - - -class test(unittest.TestCase): - - def test_simple_message(self): - - m = Message('myMessage',[('s',String),('i',Integer)]) - e = m.to_xml('a',43) - - self.assertEquals(e.tag,'myMessage') - - self.assertEquals(e.getchildren()[0].tag,'s') - self.assertEquals(e.getchildren()[1].tag,'i') - - self.assertEquals(e.getchildren()[0].text,'a') - self.assertEquals(e.getchildren()[1].text,'43') - - values = m.from_xml(e) - - self.assertEquals('a',values[0]) - self.assertEquals(43,values[1]) - - def test_href(self): - - a = ''' - - - - - - - - - - - somemachine - someuser - - - machine2 - user2 - - - - ''' - payload,header = from_soap(a) - self.assertEquals(len(payload.getchildren()[0].getchildren()),2) # quick and dirty test href reconstruction - - def test_namespaces(self): - m = Message('{some_namespace}myMessage',[('s',String),('i',Integer)]) - e = m.to_xml('a',43) - self.assertEquals(e.tag,'{some_namespace}myMessage') - - m1 = Message('myMessage',[('{some_namespace}s',String),('i',Integer)],ns='some_namespace') - e2 = m1.to_xml('a',43) - self.assertEquals(e2.nsmap[None],'some_namespace') - - def test_class_to_xml(self): - m = Message('myMessage',[('p',Person)]) - - p = Person() - p.name = 'steve-o' - p.age = 2 - - element = m.to_xml(p) - - self.assertEquals(element.tag,'myMessage') - self.assertEquals(element.getchildren()[0].find('name').text,'steve-o') - self.assertEquals(element.getchildren()[0].find('age').text,'2') - self.assertEquals(len(element.getchildren()[0].find('addresses').getchildren()),0) - - p1 = m.from_xml(element)[0] - - self.assertEquals(p1.name,p.name) - self.assertEquals(p1.age,p.age) - self.assertEquals(p1.addresses,[]) - - - def test_to_xml_nested(self): - - m = Message('myMessage',[('p',Person)]) - - p = Person() - p.name = 'steve-o' - p.age = 2 - p.addresses = [] - - for i in range(0,1000): - a = Address() - a.street = '123 happy way' - a.zip = i - a.laditude = '45.22' - a.longitude = '444.234' - p.addresses.append(a) - - element = m.to_xml(p) - - self.assertEquals('myMessage',element.tag) - addresses = element.getchildren()[0].find('addresses').getchildren() - self.assertEquals(1000,len(addresses)) - self.assertEquals('0',addresses[0].find('zip').text) - - def test_soap_envelope(self): - m = Message('myMessage',[('p',Person)]) - env = make_soap_envelope(m.to_xml(Person())) - - self.assertTrue(env.tag.endswith('Envelope')) - self.assertTrue(env.getchildren()[0].tag.endswith('Body')) - - m = Message('myMessage',[('p',Person)]) - env = make_soap_envelope(m.to_xml(Person()), header_elements=[et.Element('header1'),et.Element('header2')]) - - env = et.fromstring(et.tostring(env)) - - self.assertTrue(env.getchildren()[0].tag.endswith('Header')) - self.assertEquals(len(env.getchildren()[0].getchildren()),2) - self.assertTrue(env.getchildren()[1].tag.endswith('Body')) - - def test_soap_fault(self): - fault = make_soap_fault('something happened') - fault = et.fromstring(et.tostring(fault)) - - self.assertTrue(fault.getchildren()[0].tag.endswith,'Body') - self.assertTrue(fault.getchildren()[0].getchildren()[0].tag.endswith('Fault')) - f = fault.getchildren()[0].getchildren()[0] - - self.assertEquals(f.find('faultstring').text,'something happened') - self.assertEquals(f.find('faultcode').text,'Server') - self.assertEquals(f.find('detail').text,None) - - fault = make_soap_fault('something happened','DatabaseError','error on line 12') - - fault = et.fromstring(et.tostring(fault)) - - f = fault.getchildren()[0].getchildren()[0] - self.assertEquals(f.find('faultstring').text,'something happened') - self.assertEquals(f.find('faultcode').text,'DatabaseError') - self.assertEquals(f.find('detail').text,'error on line 12') - - def test_message_repeating(self): - m = Message('myMessage',[('p',Repeating(String))]) - method = m.to_xml(["a","b","c","d"]) - self.assertEquals(len(method.getchildren()),4) - - data = m.from_xml(method) - - self.assertEquals(data,[["a","b","c","d"]]) - - - -def test_suite(): - loader = unittest.TestLoader() - return loader.loadTestsFromTestCase(test) - -if __name__== '__main__': - unittest.TextTestRunner().run(test_suite()) - - \ No newline at end of file diff -Nru python-soaplib-0.8.1/TODO.txt python-soaplib-0.9.3-alpha3/TODO.txt --- python-soaplib-0.8.1/TODO.txt 2009-07-14 10:33:00.000000000 +0000 +++ python-soaplib-0.9.3-alpha3/TODO.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -Code to sort out after namespace map addition: - -Message.to_xml() -ClassSerializerMeta.namespace - -create_xml_element - pass nsmap -_add_messages_for_methods / _add_bindings_for_methods : create_xml_element (no nsmap) \ No newline at end of file