├── .project ├── .pydevproject ├── LICENSE ├── README ├── makefile ├── python-suds.spec ├── sdist ├── setup.cfg ├── setup.py ├── suds ├── __init__.py ├── bindings │ ├── __init__.py │ ├── binding.py │ ├── document.py │ ├── multiref.py │ └── rpc.py ├── builder.py ├── cache.py ├── client.py ├── metrics.py ├── mx │ ├── __init__.py │ ├── appender.py │ ├── basic.py │ ├── core.py │ ├── encoded.py │ ├── literal.py │ └── typer.py ├── options.py ├── properties.py ├── reader.py ├── resolver.py ├── sax │ ├── __init__.py │ ├── attribute.py │ ├── date.py │ ├── document.py │ ├── element.py │ ├── enc.py │ ├── parser.py │ └── text.py ├── servicedefinition.py ├── serviceproxy.py ├── soaparray.py ├── store.py ├── sudsobject.py ├── transport │ ├── __init__.py │ ├── http.py │ ├── https.py │ └── options.py ├── umx │ ├── __init__.py │ ├── attrlist.py │ ├── basic.py │ ├── core.py │ ├── encoded.py │ └── typed.py ├── wsdl.py ├── wsse.py └── xsd │ ├── __init__.py │ ├── deplist.py │ ├── doctor.py │ ├── query.py │ ├── schema.py │ ├── sxbase.py │ ├── sxbasic.py │ └── sxbuiltin.py └── tests ├── __init__.py ├── axis1.py ├── axis2.py ├── builtin.py ├── jasper.py ├── public.py ├── rhq.py └── saxenc.py /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | suds 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | python 2.6 6 | 7 | /suds 8 | /suds/suds 9 | /suds/tests 10 | 11 | 12 | 13 | Default 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | # 17 | 18 | PKG = python-suds 19 | SPEC = $(PKG).spec 20 | SETUP = setup.py 21 | DOCTAR = suds-docs.tar.gz 22 | FEDORAPEOPLE = jortel@fedorapeople.org 23 | 24 | all : rpm docs 25 | 26 | dist : clean 27 | mkdir dist 28 | ./sdist 29 | ./sdist python 30 | 31 | rpm : dist 32 | cp dist/$(PKG)*.gz /usr/src/redhat/SOURCES 33 | rpmbuild -ba $(SPEC) 34 | cp /usr/src/redhat/RPMS/noarch/$(PKG)*.rpm dist 35 | cp /usr/src/redhat/SRPMS/$(PKG)*.rpm dist 36 | rpmlint -i dist/$(PKG)*.rpm 37 | 38 | release : rpm rdocs 39 | scp dist/python*.tar.gz fedorahosted.org:suds 40 | scp dist/python*.rpm fedorahosted.org:suds 41 | 42 | register : 43 | python setup.py sdist bdist_egg register upload 44 | 45 | rdocs : docs 46 | scp /tmp/$(DOCTAR) $(FEDORAPEOPLE): 47 | ssh $(FEDORAPEOPLE) 'cd public_html/suds; rm -rf doc; tar xmzvf ~/$(DOCTAR)' 48 | 49 | docs : 50 | rm -rf doc 51 | rm -f /tmp/$(DOCTAR) 52 | epydoc -vo doc `find suds -name "*.py"` 53 | tar czvf /tmp/$(DOCTAR) doc 54 | 55 | pdf : 56 | epydoc -vo doc --pdf `find suds -name \*.py` 57 | mv doc/api.pdf doc/sudsapi.pdf 58 | 59 | clean : 60 | rm -rf dist 61 | rm -rf build 62 | rm -rf doc 63 | rm -rf *.egg-info 64 | rm -rf /usr/src/redhat/BUILD/$(PKG)* 65 | rm -rf /usr/src/redhat/RPMS/noarch/$(PKG)* 66 | rm -rf /usr/src/redhat/SOURCES/$(PKG)* 67 | rm -rf /usr/src/redhat/SRPMS/$(PKG)* 68 | find . -name "*.pyc" -exec rm -f {} \; 69 | find . -name "*~" -exec rm -f {} \; 70 | 71 | .PHONY : clean register docs pdf 72 | -------------------------------------------------------------------------------- /python-suds.spec: -------------------------------------------------------------------------------- 1 | %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} 2 | 3 | Summary: A python SOAP client 4 | Name: python-suds 5 | Version: 0.4 6 | Release: 1%{?dist} 7 | Source0: https://fedorahosted.org/releases/s/u/%{name}/%{name}-%{version}.tar.gz 8 | License: LGPLv3+ 9 | Group: Development/Libraries 10 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot 11 | BuildArch: noarch 12 | Requires: python >= 2.4 13 | BuildRequires: python-setuptools-devel 14 | Url: https://fedorahosted.org/suds 15 | 16 | %description 17 | The suds project is a python soap web services client lib. Suds leverages 18 | python meta programming to provide an intuative API for consuming web 19 | services. Objectification of types defined in the WSDL is provided 20 | without class generation. Programmers rarely need to read the WSDL since 21 | services and WSDL based objects can be easily inspected. Supports 22 | pluggable soap bindings. 23 | 24 | %prep 25 | %setup -q 26 | 27 | %build 28 | python setup.py sdist 29 | 30 | %install 31 | rm -rf $RPM_BUILD_ROOT 32 | python setup.py install --optimize=1 --root=$RPM_BUILD_ROOT 33 | 34 | %clean 35 | rm -rf $RPM_BUILD_ROOT 36 | 37 | %files 38 | %defattr(-,root,root,-) 39 | %{python_sitelib}/suds*.egg-info 40 | %dir %{python_sitelib}/suds 41 | %dir %{python_sitelib}/suds/bindings 42 | %dir %{python_sitelib}/suds/sax 43 | %dir %{python_sitelib}/suds/xsd 44 | %dir %{python_sitelib}/suds/mx 45 | %dir %{python_sitelib}/suds/umx 46 | %dir %{python_sitelib}/suds/transport 47 | %{python_sitelib}/suds/*.py* 48 | %{python_sitelib}/suds/bindings/*.py* 49 | %{python_sitelib}/suds/sax/*.py* 50 | %{python_sitelib}/suds/xsd/*.py* 51 | %{python_sitelib}/suds/mx/*.py* 52 | %{python_sitelib}/suds/umx/*.py* 53 | %{python_sitelib}/suds/transport/*.py* 54 | 55 | %doc README LICENSE 56 | 57 | %changelog 58 | * Thu Dec 17 2009 jortel - 0.4-1 59 | - 0.4 60 | 61 | * Thu Dec 17 2009 jortel - 0.3.9-1 62 | - Bumped python requires to 2.4 63 | - Replaced stream-based caching in the transport package with document-based caching. 64 | - Caches pickled Document objects instead of XML text. 2x Faster! 65 | - No more SAX parsing exceptions on damaged or incomplete cached files. 66 | - Cached WSDL objects. Entire Definitions object including contained Schema object cached via pickle. 67 | - Copy of soap encoding schema packaged with suds. 68 | - Refactor Transports to use ProxyHandler instead of urllib2.Request.set_proxy(). 69 | - Added WSSE enhancements and support. See: Timestamp token. 70 | - Fixed Tickets: #256, #291, #294, #295, #296 71 | 72 | * Wed Dec 9 2009 jortel - 0.3.8-1 73 | - Includeds Windows NTLM Transport. 74 | - Add missing self.messages in Client.clone(). 75 | - Changed default behavior for WSDL PartElement to be optional. 76 | - Add support for services/ports defined without
element in WSDL. 77 | - Fix sax.attribute.Element.attrib() to find by name only when ns is not specified; renamed to Element.getAttribute(). 78 | - Update HttpTransport to pass timeout parameter to urllib2 open() methods when supported by urllib2. 79 | - Add null class to pass explicit NULL values for parameters and optional elements. 80 | - Soap encoded array (soap-enc:Array) enhancement for rpc/encoded. 81 | Arrays passed as python arrays - works like document/literal now. 82 | No more using the factory to create the Array. 83 | Automatically includes arrayType attribute. Eg: soap-enc:arrayType="Array[2]". 84 | Reintroduced ability to pass complex (objects) using python dict instead of suds object via factory. 85 | - Fixed tickets: #84, #261, #262, #263, #265, #266, #278, #280, #282. 86 | 87 | * Thu Oct 16 2009 jortel - 0.3.7-1 88 | - Better soap header support 89 | - Added new transport HttpAuthenticated for active (not passive) basic authentication. 90 | - New options (prefixes, timeout, retxml) 91 | - WSDL processing enhancements. 92 | - Expanded builtin XSD type support. 93 | - Fixed 94 | - Better XML date/datetime conversion. 95 | - Client.clone() method added for lightweight copy of client object. 96 | - XSD processing fixes/enhancements. 97 | - Better by support. 98 | - Performance enhancements. 99 | - Fixed tickets: #65, #232, #233, #235, #241, #242, #244, #247, #254, #254, #256, #257, #258 100 | 101 | * Sun Jul 26 2009 Fedora Release Engineering - 0.3.6-2 102 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild 103 | 104 | * Wed May 1 2009 jortel - 0.3.6-1 105 | - Change hard coded /tmp/suds to tempfile.gettempdir() and create suds/ on demand. 106 | - Fix return type for Any.get_attribute(). 107 | - Update http caching to ignore file:// urls. 108 | - Better logging of messages when only the reply is injected. 109 | - Fix XInteger and XFloat types to translate returned arrays properly. 110 | - Fix xs:import schema with same namespace. 111 | - Update parser to not load external references and add Import.bind() for XMLSchema.xsd location. 112 | - Add schema doctor - used to patch XSDs at runtime. (See Options.doctor) 113 | - Fix deprecation warnings in python 2.6. 114 | - Add behavior for @default defined on . 115 | - Change @xsi:type value to always be qualified for doc/literal. 116 | - Add Option.xstq option to control when @xsi:type is qualified. 117 | - Fixed Tickets: #64, #129, #205, #206, #217, #221, #222, #224, #225, #228, #229, #230 118 | 119 | * Wed Feb 25 2009 jortel - 0.3.5-1 120 | - Adds http caching. Default is (1) day. 121 | - Removed checking fc version in spec since no longer building < fc9. 122 | - Updated makefile to roll tarball with tar.sh. 123 | - Moved bare/wrapped determination to wsdl for document/literal. 124 | - Refactored Transport to provide better visibility into http headers. 125 | - Fixed Tickets: #207, #207, #209, #210, #212, #214, #215 126 | 127 | * Mon Dec 08 2008 jortel - 0.3.4-1 128 | - Static (automatic) Import.bind('http://schemas.xmlsoap.org/soap/encoding/') 129 | - Basic ws-security with {{{UsernameToken}}} and clear-text password only. 130 | - Add support for ''sparse'' soap headers via passing dictionary 131 | - Add support for arbitrary user defined soap headers 132 | - Fixes service operations with multiple soap header entries. 133 | - Schema loading and dereferencing algorithm enhancements. 134 | - Nested soap multirefs fixed. 135 | - Better (true) support for elementFormDefault="unqualified" provides more accurate namespaing. 136 | - WSDL part types no longer default to WSDL targetNamespace. 137 | - Fixed Tickets: #4, #6, #21, #32, #62, #66, #71, #72, #114, #155, #201. 138 | 139 | * Wed Dec 04 2008 jortel - 0.3.3-2 140 | - Rebuild for Python 2.6 141 | 142 | * Wed Dec 04 2008 jortel - 0.3.3-1 143 | - No longer installs (tests) package. 144 | - Implements API-3 proposal 145 | Pluggable transport 146 | Keyword method arguments 147 | Baisc http authentication in default transport 148 | - Add namespace prefix normalization in soap message. 149 | - Better soap message pruning of empty nodes. 150 | - Fixed Tickets: #51 - #60. 151 | 152 | * Sat Nov 29 2008 Ignacio Vazquez-Abrams - 0.3.2-2 153 | - Rebuild for Python 2.6 154 | 155 | * Fri Nov 06 2008 jortel - 0.3.2-1 156 | - Add SOAP MultiRef support 157 | - Add support for new schema tags: 158 | 159 | 160 | 161 | 162 | - Added support for new xs <--> python type conversions: 163 | xs:int 164 | xs:long 165 | xs:float 166 | xs:double 167 | - Revise marshaller and binding to further sharpen the namespacing of nodes produced. 168 | - Infinite recursion fixed in ''xsd'' package dereference() during schema loading. 169 | - Add support for of schema files into the wsdl root . 170 | - Fix double encoding of (&) 171 | - Add Client API: 172 | setheaders() - Same as keyword but works for all invocations. 173 | addprefix() - Mapping of namespace prefixes. 174 | setlocation() - Override the location in the wsdl. 175 | setproxy() - Same as proxy keyword but for all invocations. 176 | - Add proper namespace prefix for soap headers. 177 | - Fixed Tickets: #5, #12, #34, #37, #40, #44, #45, #46, #48, #49, #50, #51 178 | 179 | * Fri Nov 03 2008 jortel - 0.3.1-5 180 | - Add LICENSE to %%doc. 181 | 182 | * Fri Oct 28 2008 jortel - 0.3.1-4 183 | - Changes acc. #466496 Comment #8 184 | 185 | * Fri Oct 27 2008 jortel - 0.3.1-3 186 | - Add "rm -rf $RPM_BUILD_ROOT" to install 187 | 188 | * Fri Oct 16 2008 jortel - 0.3.1-2 189 | - Changes acc. #466496 Comment #1 190 | 191 | * Fri Oct 10 2008 jortel - 0.3.1-1 192 | - Extends the support for multi-port services introduced earlier. This addition, 193 | provides for multiple services to define the *same* method and suds will 194 | handle it properly. See section 'SERVICES WITH MULTIPLE PORTS:' 195 | - Add support for multi-document document/literal soap binding style. 196 | See section 'MULTI-DOCUMENT Docuemnt/Literal:' 197 | - Add support for (xs:group, xs:attributeGroup) tags. 198 | - Add Client.last_sent() and Client.last_received(). 199 | -------------------------------------------------------------------------------- /sdist: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the (LGPL) GNU Lesser General Public License as 4 | # published by the Free Software Foundation; either version 3 of the 5 | # License, or (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU Library Lesser General Public License for more details at 11 | # ( http://www.gnu.org/licenses/lgpl.html ). 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 16 | # written by: Jeff Ortel ( jortel@redhat.com ) 17 | 18 | product="suds" 19 | version=`python -c "import $product; print $product.__version__"` 20 | 21 | if [ $1 ] 22 | then 23 | product="$1-$product" 24 | fi 25 | 26 | tarfile=$product-$version.tar.gz 27 | 28 | rm -rf build 29 | files=`find . -regex ".*\.\(py\|spec\|cfg\)"` 30 | files+=" 31 | makefile 32 | LICENSE 33 | README" 34 | 35 | wrapper="$product-$version" 36 | 37 | echo $product 38 | echo $version 39 | echo $files 40 | echo $wrapper 41 | 42 | mkdir -p build/$wrapper 43 | tar cvf build/stage.tar $files 44 | cd build/$wrapper 45 | tar xvf ../stage.tar 46 | cd ../ 47 | tar czvf ../dist/$tarfile $wrapper 48 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [install] 2 | optimize = 1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # This program is free software; you can redistribute it and/or modify 4 | # it under the terms of the (LGPL) GNU Lesser General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Library Lesser General Public License for more details at 12 | # ( http://www.gnu.org/licenses/lgpl.html ). 13 | # 14 | # You should have received a copy of the GNU Lesser General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | # written by: Jeff Ortel ( jortel@redhat.com ) 18 | 19 | import sys 20 | import suds 21 | from setuptools import setup, find_packages 22 | 23 | setup( 24 | name="suds", 25 | version=suds.__version__, 26 | description="Lightweight SOAP client", 27 | author="Jeff Ortel", 28 | author_email="jortel@redhat.com", 29 | maintainer="Jeff Ortel", 30 | maintainer_email="jortel@redhat.com", 31 | packages=find_packages(exclude=['tests']), 32 | url="https://fedorahosted.org/suds", 33 | ) 34 | -------------------------------------------------------------------------------- /suds/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Suds is a lightweight SOAP python client that provides a 19 | service proxy for Web Services. 20 | """ 21 | 22 | import os 23 | import sys 24 | 25 | # 26 | # Project properties 27 | # 28 | 29 | __version__ = '0.4' 30 | __build__="(beta) R663-20100303" 31 | 32 | # 33 | # Exceptions 34 | # 35 | 36 | class MethodNotFound(Exception): 37 | def __init__(self, name): 38 | Exception.__init__(self, "Method not found: '%s'" % name) 39 | 40 | class PortNotFound(Exception): 41 | def __init__(self, name): 42 | Exception.__init__(self, "Port not found: '%s'" % name) 43 | 44 | class ServiceNotFound(Exception): 45 | def __init__(self, name): 46 | Exception.__init__(self, "Service not found: '%s'" % name) 47 | 48 | class TypeNotFound(Exception): 49 | def __init__(self, name): 50 | Exception.__init__(self, "Type not found: '%s'" % tostr(name)) 51 | 52 | class BuildError(Exception): 53 | msg = \ 54 | """ 55 | An error occured while building a instance of (%s). As a result 56 | the object you requested could not be constructed. It is recommended 57 | that you construct the type manually using a Suds object. 58 | Please open a ticket with a description of this error. 59 | Reason: %s 60 | """ 61 | def __init__(self, name, exception): 62 | Exception.__init__(self, BuildError.msg % (name, exception)) 63 | 64 | class SoapHeadersNotPermitted(Exception): 65 | msg = \ 66 | """ 67 | Method (%s) was invoked with SOAP headers. The WSDL does not 68 | define SOAP headers for this method. Retry without the soapheaders 69 | keyword argument. 70 | """ 71 | def __init__(self, name): 72 | Exception.__init__(self, self.msg % name) 73 | 74 | class WebFault(Exception): 75 | def __init__(self, fault, document): 76 | if hasattr(fault, 'faultstring'): 77 | Exception.__init__(self, "Server raised fault: '%s'" % fault.faultstring) 78 | self.fault = fault 79 | self.document = document 80 | 81 | # 82 | # Logging 83 | # 84 | 85 | class Repr: 86 | def __init__(self, x): 87 | self.x = x 88 | def __str__(self): 89 | return repr(self.x) 90 | 91 | # 92 | # Utility 93 | # 94 | 95 | def tostr(object, encoding=None): 96 | """ get a unicode safe string representation of an object """ 97 | if isinstance(object, basestring): 98 | if encoding is None: 99 | return object 100 | else: 101 | return object.encode(encoding) 102 | if isinstance(object, tuple): 103 | s = ['('] 104 | for item in object: 105 | if isinstance(item, basestring): 106 | s.append(item) 107 | else: 108 | s.append(tostr(item)) 109 | s.append(', ') 110 | s.append(')') 111 | return ''.join(s) 112 | if isinstance(object, list): 113 | s = ['['] 114 | for item in object: 115 | if isinstance(item, basestring): 116 | s.append(item) 117 | else: 118 | s.append(tostr(item)) 119 | s.append(', ') 120 | s.append(']') 121 | return ''.join(s) 122 | if isinstance(object, dict): 123 | s = ['{'] 124 | for item in object.items(): 125 | if isinstance(item[0], basestring): 126 | s.append(item[0]) 127 | else: 128 | s.append(tostr(item[0])) 129 | s.append(' = ') 130 | if isinstance(item[1], basestring): 131 | s.append(item[1]) 132 | else: 133 | s.append(tostr(item[1])) 134 | s.append(', ') 135 | s.append('}') 136 | return ''.join(s) 137 | try: 138 | return unicode(object) 139 | except: 140 | return str(object) 141 | 142 | class null: 143 | """ 144 | The I{null} object. 145 | Used to pass NULL for optional XML nodes. 146 | """ 147 | pass 148 | 149 | def objid(obj): 150 | return obj.__class__.__name__\ 151 | +':'+hex(id(obj)) 152 | 153 | 154 | import client 155 | -------------------------------------------------------------------------------- /suds/bindings/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides modules containing classes to support Web Services (SOAP) 19 | bindings. 20 | """ -------------------------------------------------------------------------------- /suds/bindings/document.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides classes for the (WS) SOAP I{document/literal}. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.bindings.binding import Binding 24 | from suds.sax.element import Element 25 | 26 | log = getLogger(__name__) 27 | 28 | 29 | class Document(Binding): 30 | """ 31 | The document/literal style. Literal is the only (@use) supported 32 | since document/encoded is pretty much dead. 33 | Although the soap specification supports multiple documents within the soap 34 | , it is very uncommon. As such, suds presents an I{RPC} view of 35 | service methods defined with a single document parameter. This is done so 36 | that the user can pass individual parameters instead of one, single document. 37 | To support the complete specification, service methods defined with multiple documents 38 | (multiple message parts), must present a I{document} view for that method. 39 | """ 40 | 41 | def bodycontent(self, method, args, kwargs): 42 | # 43 | # The I{wrapped} vs I{bare} style is detected in 2 ways. 44 | # If there is 2+ parts in the message then it is I{bare}. 45 | # If there is only (1) part and that part resolves to a builtin then 46 | # it is I{bare}. Otherwise, it is I{wrapped}. 47 | # 48 | if not len(method.soap.input.body.parts): 49 | return () 50 | wrapped = method.soap.input.body.wrapped 51 | if wrapped: 52 | pts = self.bodypart_types(method) 53 | root = self.document(pts[0]) 54 | else: 55 | root = [] 56 | n = 0 57 | for pd in self.param_defs(method): 58 | if n < len(args): 59 | value = args[n] 60 | else: 61 | value = kwargs.get(pd[0]) 62 | n += 1 63 | p = self.mkparam(method, pd, value) 64 | if p is None: 65 | continue 66 | if not wrapped: 67 | ns = pd[1].namespace('ns0') 68 | p.setPrefix(ns[0], ns[1]) 69 | root.append(p) 70 | return root 71 | 72 | def replycontent(self, method, body): 73 | wrapped = method.soap.output.body.wrapped 74 | if wrapped: 75 | return body[0].children 76 | else: 77 | return body.children 78 | 79 | def document(self, wrapper): 80 | """ 81 | Get the document root. For I{document/literal}, this is the 82 | name of the wrapper element qualifed by the schema tns. 83 | @param wrapper: The method name. 84 | @type wrapper: L{xsd.sxbase.SchemaObject} 85 | @return: A root element. 86 | @rtype: L{Element} 87 | """ 88 | tag = wrapper[1].name 89 | ns = wrapper[1].namespace('ns0') 90 | d = Element(tag, ns=ns) 91 | return d 92 | 93 | def mkparam(self, method, pdef, object): 94 | # 95 | # Expand list parameters into individual parameters 96 | # each with the type information. This is because in document 97 | # arrays are simply unbounded elements. 98 | # 99 | if isinstance(object, (list, tuple)): 100 | tags = [] 101 | for item in object: 102 | tags.append(self.mkparam(method, pdef, item)) 103 | return tags 104 | else: 105 | return Binding.mkparam(self, method, pdef, object) 106 | 107 | def param_defs(self, method): 108 | # 109 | # Get parameter definitions for document literal. 110 | # The I{wrapped} vs I{bare} style is detected in 2 ways. 111 | # If there is 2+ parts in the message then it is I{bare}. 112 | # If there is only (1) part and that part resolves to a builtin then 113 | # it is I{bare}. Otherwise, it is I{wrapped}. 114 | # 115 | pts = self.bodypart_types(method) 116 | wrapped = method.soap.input.body.wrapped 117 | if not wrapped: 118 | return pts 119 | result = [] 120 | # wrapped 121 | for p in pts: 122 | resolved = p[1].resolve() 123 | for child, ancestry in resolved: 124 | if child.isattr(): 125 | continue 126 | if self.bychoice(ancestry): 127 | log.debug( 128 | '%s\ncontained by , excluded as param for %s()', 129 | child, 130 | method.name) 131 | continue 132 | result.append((child.name, child)) 133 | return result 134 | 135 | def returned_types(self, method): 136 | result = [] 137 | wrapped = method.soap.output.body.wrapped 138 | rts = self.bodypart_types(method, input=False) 139 | if wrapped: 140 | for pt in rts: 141 | resolved = pt.resolve(nobuiltin=True) 142 | for child, ancestry in resolved: 143 | result.append(child) 144 | break 145 | else: 146 | result += rts 147 | return result 148 | 149 | def bychoice(self, ancestry): 150 | """ 151 | The ancestry contains a 152 | @param ancestry: A list of ancestors. 153 | @type ancestry: list 154 | @return: True if contains 155 | @rtype: boolean 156 | """ 157 | for x in ancestry: 158 | if x.choice(): 159 | return True 160 | return False -------------------------------------------------------------------------------- /suds/bindings/multiref.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides classes for handling soap multirefs. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.sax.element import Element 24 | 25 | log = getLogger(__name__) 26 | 27 | soapenc = (None, 'http://schemas.xmlsoap.org/soap/encoding/') 28 | 29 | class MultiRef: 30 | """ 31 | Resolves and replaces multirefs. 32 | @ivar nodes: A list of non-multiref nodes. 33 | @type nodes: list 34 | @ivar catalog: A dictionary of multiref nodes by id. 35 | @type catalog: dict 36 | """ 37 | 38 | def __init__(self): 39 | self.nodes = [] 40 | self.catalog = {} 41 | 42 | def process(self, body): 43 | """ 44 | Process the specified soap envelope body and replace I{multiref} node 45 | references with the contents of the referenced node. 46 | @param body: A soap envelope body node. 47 | @type body: L{Element} 48 | @return: The processed I{body} 49 | @rtype: L{Element} 50 | """ 51 | self.nodes = [] 52 | self.catalog = {} 53 | self.build_catalog(body) 54 | self.update(body) 55 | body.children = self.nodes 56 | return body 57 | 58 | def update(self, node): 59 | """ 60 | Update the specified I{node} by replacing the I{multiref} references with 61 | the contents of the referenced nodes and remove the I{href} attribute. 62 | @param node: A node to update. 63 | @type node: L{Element} 64 | @return: The updated node 65 | @rtype: L{Element} 66 | """ 67 | self.replace_references(node) 68 | for c in node.children: 69 | self.update(c) 70 | return node 71 | 72 | def replace_references(self, node): 73 | """ 74 | Replacing the I{multiref} references with the contents of the 75 | referenced nodes and remove the I{href} attribute. Warning: since 76 | the I{ref} is not cloned, 77 | @param node: A node to update. 78 | @type node: L{Element} 79 | """ 80 | href = node.getAttribute('href') 81 | if href is None: 82 | return 83 | id = href.getValue() 84 | ref = self.catalog.get(id) 85 | if ref is None: 86 | log.error('soap multiref: %s, not-resolved', id) 87 | return 88 | node.append(ref.children) 89 | node.setText(ref.getText()) 90 | for a in ref.attributes: 91 | if a.name != 'id': 92 | node.append(a) 93 | node.remove(href) 94 | 95 | def build_catalog(self, body): 96 | """ 97 | Create the I{catalog} of multiref nodes by id and the list of 98 | non-multiref nodes. 99 | @param body: A soap envelope body node. 100 | @type body: L{Element} 101 | """ 102 | for child in body.children: 103 | if self.soaproot(child): 104 | self.nodes.append(child) 105 | id = child.get('id') 106 | if id is None: continue 107 | key = '#%s' % id 108 | self.catalog[key] = child 109 | 110 | def soaproot(self, node): 111 | """ 112 | Get whether the specified I{node} is a soap encoded root. 113 | This is determined by examining @soapenc:root='1'. 114 | The node is considered to be a root when the attribute 115 | is not specified. 116 | @param node: A node to evaluate. 117 | @type node: L{Element} 118 | @return: True if a soap encoded root. 119 | @rtype: bool 120 | """ 121 | root = node.getAttribute('root', ns=soapenc) 122 | if root is None: 123 | return True 124 | else: 125 | return ( root.value == '1' ) 126 | -------------------------------------------------------------------------------- /suds/bindings/rpc.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides classes for the (WS) SOAP I{rpc/literal} and I{rpc/encoded} bindings. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.mx.encoded import Encoded as MxEncoded 24 | from suds.umx.encoded import Encoded as UmxEncoded 25 | from suds.bindings.binding import Binding, envns 26 | from suds.sax.element import Element 27 | 28 | log = getLogger(__name__) 29 | 30 | 31 | encns = ('SOAP-ENC', 'http://schemas.xmlsoap.org/soap/encoding/') 32 | 33 | class RPC(Binding): 34 | """ 35 | RPC/Literal binding style. 36 | """ 37 | 38 | def param_defs(self, method): 39 | return self.bodypart_types(method) 40 | 41 | def envelope(self, header, body): 42 | env = Binding.envelope(self, header, body) 43 | env.addPrefix(encns[0], encns[1]) 44 | env.set('%s:encodingStyle' % envns[0], 45 | 'http://schemas.xmlsoap.org/soap/encoding/') 46 | return env 47 | 48 | def bodycontent(self, method, args, kwargs): 49 | n = 0 50 | root = self.method(method) 51 | for pd in self.param_defs(method): 52 | if n < len(args): 53 | value = args[n] 54 | else: 55 | value = kwargs.get(pd[0]) 56 | p = self.mkparam(method, pd, value) 57 | if p is not None: 58 | root.append(p) 59 | n += 1 60 | return root 61 | 62 | def replycontent(self, method, body): 63 | return body[0].children 64 | 65 | def method(self, method): 66 | """ 67 | Get the document root. For I{rpc/(literal|encoded)}, this is the 68 | name of the method qualifed by the schema tns. 69 | @param method: A service method. 70 | @type method: I{service.Method} 71 | @return: A root element. 72 | @rtype: L{Element} 73 | """ 74 | ns = method.soap.input.body.namespace 75 | if ns[0] is None: 76 | ns = ('ns0', ns[1]) 77 | method = Element(method.name, ns=ns) 78 | return method 79 | 80 | 81 | class Encoded(RPC): 82 | """ 83 | RPC/Encoded (section 5) binding style. 84 | """ 85 | 86 | def marshaller(self): 87 | return MxEncoded(self.schema()) 88 | 89 | def unmarshaller(self, typed=True): 90 | """ 91 | Get the appropriate XML decoder. 92 | @return: Either the (basic|typed) unmarshaller. 93 | @rtype: L{UmxTyped} 94 | """ 95 | if typed: 96 | return UmxEncoded(self.schema()) 97 | else: 98 | return RPC.unmarshaller(self, typed) 99 | -------------------------------------------------------------------------------- /suds/builder.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{builder} module provides an wsdl/xsd defined types factory 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.sudsobject import Factory 24 | 25 | log = getLogger(__name__) 26 | 27 | 28 | class Builder: 29 | """ Builder used to construct an object for types defined in the schema """ 30 | 31 | def __init__(self, resolver): 32 | """ 33 | @param resolver: A schema object name resolver. 34 | @type resolver: L{resolver.Resolver} 35 | """ 36 | self.resolver = resolver 37 | 38 | def build(self, name): 39 | """ build a an object for the specified typename as defined in the schema """ 40 | if isinstance(name, basestring): 41 | type = self.resolver.find(name) 42 | if type is None: 43 | raise TypeNotFound(name) 44 | else: 45 | type = name 46 | cls = type.name 47 | if len(type): 48 | data = Factory.object(cls) 49 | else: 50 | data = Factory.property(cls) 51 | resolved = type.resolve() 52 | md = data.__metadata__ 53 | md.sxtype = resolved 54 | md.ordering = self.ordering(resolved) 55 | history = [] 56 | self.add_attributes(data, resolved) 57 | for child, ancestry in type.children(): 58 | if self.skip_child(child, ancestry): 59 | continue 60 | self.process(data, child, history[:]) 61 | return data 62 | 63 | def process(self, data, type, history): 64 | """ process the specified type then process its children """ 65 | if type in history: 66 | return 67 | if type.enum(): 68 | return 69 | history.append(type) 70 | resolved = type.resolve() 71 | value = None 72 | if type.unbounded(): 73 | value = [] 74 | else: 75 | if len(resolved) > 0: 76 | value = Factory.object(resolved.name) 77 | md = value.__metadata__ 78 | md.sxtype = resolved 79 | md.ordering = self.ordering(resolved) 80 | setattr(data, type.name, value) 81 | if value is not None: 82 | data = value 83 | if not isinstance(data, list): 84 | self.add_attributes(data, resolved) 85 | for child, ancestry in resolved.children(): 86 | if self.skip_child(child, ancestry): 87 | continue 88 | self.process(data, child, history[:]) 89 | 90 | def add_attributes(self, data, type): 91 | """ add required attributes """ 92 | for attr, ancestry in type.attributes(): 93 | name = '_%s' % attr.name 94 | value = attr.get_default() 95 | setattr(data, name, value) 96 | 97 | def skip_child(self, child, ancestry): 98 | """ get whether or not to skip the specified child """ 99 | if child.any(): return True 100 | for x in ancestry: 101 | if x.choice(): 102 | return True 103 | return False 104 | 105 | def ordering(self, type): 106 | """ get the ordering """ 107 | result = [] 108 | for child, ancestry in type.resolve(): 109 | name = child.name 110 | if child.name is None: 111 | continue 112 | if child.isattr(): 113 | name = '_%s' % child.name 114 | result.append(name) 115 | return result 116 | -------------------------------------------------------------------------------- /suds/cache.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains basic caching classes. 19 | """ 20 | 21 | import os 22 | from tempfile import gettempdir as tmp 23 | from suds.transport import * 24 | from datetime import datetime as dt 25 | from datetime import timedelta 26 | from cStringIO import StringIO 27 | from logging import getLogger 28 | try: 29 | import cPickle as pickle 30 | except: 31 | import pickle 32 | 33 | log = getLogger(__name__) 34 | 35 | 36 | class Cache: 37 | """ 38 | An object object cache. 39 | """ 40 | 41 | def get(self, id): 42 | """ 43 | Get a object from the cache by ID. 44 | @param id: The object ID. 45 | @type id: str 46 | @return: The object, else None 47 | @rtype: any 48 | """ 49 | raise Exception('not-implemented') 50 | 51 | def getf(self, id): 52 | """ 53 | Get a object from the cache by ID. 54 | @param id: The object ID. 55 | @type id: str 56 | @return: The object, else None 57 | @rtype: any 58 | """ 59 | raise Exception('not-implemented') 60 | 61 | def put(self, id, object): 62 | """ 63 | Put a object into the cache. 64 | @param id: The object ID. 65 | @type id: str 66 | @param object: The object to add. 67 | @type object: any 68 | """ 69 | raise Exception('not-implemented') 70 | 71 | def putf(self, id, fp): 72 | """ 73 | Write a fp into the cache. 74 | @param id: The object ID. 75 | @type id: str 76 | @param fp: File pointer. 77 | @type fp: file-like object. 78 | """ 79 | raise Exception('not-implemented') 80 | 81 | def purge(self, id): 82 | """ 83 | Purge a object from the cache by id. 84 | @param id: A object ID. 85 | @type id: str 86 | """ 87 | raise Exception('not-implemented') 88 | 89 | def clear(self): 90 | """ 91 | Clear all objects from the cache. 92 | """ 93 | raise Exception('not-implemented') 94 | 95 | 96 | class NoCache(Cache): 97 | """ 98 | The passthru object cache. 99 | """ 100 | 101 | def get(self, id): 102 | return None 103 | 104 | def getf(self, id): 105 | return None 106 | 107 | def put(self, id, object): 108 | pass 109 | 110 | def putf(self, id, fp): 111 | pass 112 | 113 | 114 | class FileCache(Cache): 115 | """ 116 | A file-based URL cache. 117 | @cvar fnprefix: The file name prefix. 118 | @type fnprefix: str 119 | @ivar fnsuffix: The file name suffix. 120 | @type fnsuffix: str 121 | @ivar duration: The cached file duration which defines how 122 | long the file will be cached. 123 | @type duration: (unit, value) 124 | @ivar location: The directory for the cached files. 125 | @type location: str 126 | """ 127 | fnprefix = 'suds' 128 | fnsuffix = 'gcf' 129 | units = ('months', 'weeks', 'days', 'hours', 'minutes', 'seconds') 130 | 131 | def __init__(self, location=None, **duration): 132 | """ 133 | @param location: The directory for the cached files. 134 | @type location: str 135 | @param duration: The cached file duration which defines how 136 | long the file will be cached. A duration=0 means forever. 137 | The duration may be: (months|weeks|days|hours|minutes|seconds). 138 | @type duration: {unit:value} 139 | """ 140 | if location is None: 141 | location = os.path.join(tmp(), 'suds') 142 | self.location = location 143 | self.duration = (None, 0) 144 | self.setduration(**duration) 145 | 146 | def setduration(self, **duration): 147 | """ 148 | Set the caching duration which defines how long the 149 | file will be cached. 150 | @param duration: The cached file duration which defines how 151 | long the file will be cached. A duration=0 means forever. 152 | The duration may be: (months|weeks|days|hours|minutes|seconds). 153 | @type duration: {unit:value} 154 | """ 155 | if len(duration) == 1: 156 | arg = duration.items()[0] 157 | if not arg[0] in self.units: 158 | raise Exception('must be: %s' % str(self.units)) 159 | self.duration = arg 160 | return self 161 | 162 | def setlocation(self, location): 163 | """ 164 | Set the location (directory) for the cached files. 165 | @param location: The directory for the cached files. 166 | @type location: str 167 | """ 168 | self.location = location 169 | 170 | def mktmp(self): 171 | """ 172 | Make the I{location} directory if it doesn't already exits. 173 | """ 174 | try: 175 | if not os.path.isdir(self.location): 176 | os.makedirs(self.location) 177 | except: 178 | log.debug(self.location, exc_info=1) 179 | return self 180 | 181 | def put(self, id, bfr): 182 | try: 183 | fn = self.__fn(id) 184 | f = self.open(fn, 'w') 185 | f.write(bfr) 186 | f.close() 187 | return bfr 188 | except: 189 | log.debug(id, exc_info=1) 190 | return bfr 191 | 192 | def putf(self, id, fp): 193 | try: 194 | fn = self.__fn(id) 195 | f = self.open(fn, 'w') 196 | f.write(fp.read()) 197 | f.close() 198 | return fp 199 | except: 200 | log.debug(id, exc_info=1) 201 | return fp 202 | 203 | def get(self, id): 204 | try: 205 | f = self.getf(id) 206 | bfr = f.read() 207 | f.close() 208 | return bfr 209 | except: 210 | pass 211 | 212 | def getf(self, id): 213 | try: 214 | fn = self.__fn(id) 215 | self.validate(fn) 216 | return self.open(fn) 217 | except: 218 | pass 219 | 220 | def validate(self, fn): 221 | """ 222 | Validate that the file has not expired based on the I{duration}. 223 | @param fn: The file name. 224 | @type fn: str 225 | """ 226 | if self.duration[1] < 1: 227 | return 228 | created = dt.fromtimestamp(os.path.getctime(fn)) 229 | d = {self.duration[0] : self.duration[1]} 230 | expired = created+timedelta(**d) 231 | if expired < dt.now(): 232 | log.debug('%s expired, deleted', fn) 233 | os.remove(fn) 234 | 235 | def clear(self): 236 | for fn in os.listdir(self.location): 237 | if os.path.isdir(fn): 238 | continue 239 | if fn.startswith(self.fnprefix): 240 | log.debug('deleted: %s', fn) 241 | os.remove(os.path.join(self.location, fn)) 242 | 243 | def purge(self, id): 244 | fn = self.__fn(id) 245 | try: 246 | os.remove(fn) 247 | except: 248 | pass 249 | 250 | def open(self, fn, *args): 251 | """ 252 | Open the cache file making sure the directory is created. 253 | """ 254 | self.mktmp() 255 | return open(fn, *args) 256 | 257 | def __fn(self, id): 258 | if hasattr(id, 'name') and hasattr(id, 'suffix'): 259 | name = id.name 260 | suffix = id.suffix 261 | else: 262 | name = id 263 | suffix = self.fnsuffix 264 | fn = '%s-%s.%s' % (self.fnprefix, abs(hash(name)), suffix) 265 | return os.path.join(self.location, fn) 266 | 267 | 268 | class ObjectCache(FileCache): 269 | """ 270 | Provides pickled object caching. 271 | @cvar protocol: The pickling protocol. 272 | @type protocol: int 273 | """ 274 | protocol = 2 275 | 276 | def get(self, id): 277 | try: 278 | fp = FileCache.getf(self, id) 279 | if fp is None: 280 | return None 281 | else: 282 | return pickle.load(fp) 283 | except: 284 | FileCache.purge(self, id) 285 | 286 | def put(self, id, object): 287 | bfr = pickle.dumps(object, self.protocol) 288 | FileCache.put(self, id, bfr) 289 | return object 290 | -------------------------------------------------------------------------------- /suds/metrics.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{metrics} module defines classes and other resources 19 | designed for collecting and reporting performance metrics. 20 | """ 21 | 22 | import time 23 | from logging import getLogger 24 | from suds import * 25 | from math import modf 26 | 27 | log = getLogger(__name__) 28 | 29 | class Timer: 30 | 31 | def __init__(self): 32 | self.started = 0 33 | self.stopped = 0 34 | 35 | def start(self): 36 | self.started = time.time() 37 | self.stopped = 0 38 | return self 39 | 40 | def stop(self): 41 | if self.started > 0: 42 | self.stopped = time.time() 43 | return self 44 | 45 | def duration(self): 46 | return ( self.stopped - self.started ) 47 | 48 | def __str__(self): 49 | if self.started == 0: 50 | return 'not-running' 51 | if self.started > 0 and self.stopped == 0: 52 | return 'started: %d (running)' % self.started 53 | duration = self.duration() 54 | jmod = ( lambda m : (m[1], m[0]*1000) ) 55 | if duration < 1: 56 | ms = (duration*1000) 57 | return '%d (ms)' % ms 58 | if duration < 60: 59 | m = modf(duration) 60 | return '%d.%.3d (seconds)' % jmod(m) 61 | m = modf(duration/60) 62 | return '%d.%.3d (minutes)' % jmod(m) 63 | -------------------------------------------------------------------------------- /suds/mx/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides modules containing classes to support 19 | marshalling (XML). 20 | """ 21 | 22 | from suds.sudsobject import Object 23 | 24 | 25 | class Content(Object): 26 | """ 27 | Marshaller Content. 28 | @ivar tag: The content tag. 29 | @type tag: str 30 | @ivar value: The content's value. 31 | @type value: I{any} 32 | """ 33 | 34 | extensions = [] 35 | 36 | def __init__(self, tag=None, value=None, **kwargs): 37 | """ 38 | @param tag: The content tag. 39 | @type tag: str 40 | @param value: The content's value. 41 | @type value: I{any} 42 | """ 43 | Object.__init__(self) 44 | self.tag = tag 45 | self.value = value 46 | for k,v in kwargs.items(): 47 | setattr(self, k, v) 48 | 49 | def __getattr__(self, name): 50 | if name not in self.__dict__: 51 | if name in self.extensions: 52 | v = None 53 | setattr(self, name, v) 54 | else: 55 | raise AttributeError, \ 56 | 'Content has no attribute %s' % name 57 | else: 58 | v = self.__dict__[name] 59 | return v -------------------------------------------------------------------------------- /suds/mx/basic.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides basic I{marshaller} classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.mx import * 24 | from suds.mx.core import Core 25 | 26 | log = getLogger(__name__) 27 | 28 | 29 | class Basic(Core): 30 | """ 31 | A I{basic} (untyped) marshaller. 32 | """ 33 | 34 | def process(self, value, tag=None): 35 | """ 36 | Process (marshal) the tag with the specified value using the 37 | optional type information. 38 | @param value: The value (content) of the XML node. 39 | @type value: (L{Object}|any) 40 | @param tag: The (optional) tag name for the value. The default is 41 | value.__class__.__name__ 42 | @type tag: str 43 | @return: An xml node. 44 | @rtype: L{Element} 45 | """ 46 | content = Content(tag=tag, value=value) 47 | result = Core.process(self, content) 48 | return result -------------------------------------------------------------------------------- /suds/mx/core.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides I{marshaller} core classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.mx import * 24 | from suds.mx.appender import ContentAppender 25 | from suds.sax.element import Element 26 | from suds.sax.document import Document 27 | from suds.sudsobject import Property 28 | 29 | 30 | log = getLogger(__name__) 31 | 32 | 33 | class Core: 34 | """ 35 | An I{abstract} marshaller. This class implement the core 36 | functionality of the marshaller. 37 | @ivar appender: A content appender. 38 | @type appender: L{ContentAppender} 39 | """ 40 | 41 | def __init__(self): 42 | """ 43 | """ 44 | self.appender = ContentAppender(self) 45 | 46 | def process(self, content): 47 | """ 48 | Process (marshal) the tag with the specified value using the 49 | optional type information. 50 | @param content: The content to process. 51 | @type content: L{Object} 52 | """ 53 | log.debug('processing:\n%s', content) 54 | self.reset() 55 | if content.tag is None: 56 | content.tag = content.value.__class__.__name__ 57 | document = Document() 58 | if isinstance(content.value, Property): 59 | root = self.node(content) 60 | self.append(document, content) 61 | else: 62 | self.append(document, content) 63 | return document.root() 64 | 65 | def append(self, parent, content): 66 | """ 67 | Append the specified L{content} to the I{parent}. 68 | @param parent: The parent node to append to. 69 | @type parent: L{Element} 70 | @param content: The content to append. 71 | @type content: L{Object} 72 | """ 73 | log.debug('appending parent:\n%s\ncontent:\n%s', parent, content) 74 | if self.start(content): 75 | self.appender.append(parent, content) 76 | self.end(parent, content) 77 | 78 | def reset(self): 79 | """ 80 | Reset the marshaller. 81 | """ 82 | pass 83 | 84 | def node(self, content): 85 | """ 86 | Create and return an XML node. 87 | @param content: The content for which proccessing has been suspended. 88 | @type content: L{Object} 89 | @return: An element. 90 | @rtype: L{Element} 91 | """ 92 | return Element(content.tag) 93 | 94 | def start(self, content): 95 | """ 96 | Appending this content has started. 97 | @param content: The content for which proccessing has started. 98 | @type content: L{Content} 99 | @return: True to continue appending 100 | @rtype: boolean 101 | """ 102 | return True 103 | 104 | def suspend(self, content): 105 | """ 106 | Appending this content has suspended. 107 | @param content: The content for which proccessing has been suspended. 108 | @type content: L{Content} 109 | """ 110 | pass 111 | 112 | def resume(self, content): 113 | """ 114 | Appending this content has resumed. 115 | @param content: The content for which proccessing has been resumed. 116 | @type content: L{Content} 117 | """ 118 | pass 119 | 120 | def end(self, parent, content): 121 | """ 122 | Appending this content has ended. 123 | @param parent: The parent node ending. 124 | @type parent: L{Element} 125 | @param content: The content for which proccessing has ended. 126 | @type content: L{Content} 127 | """ 128 | pass 129 | 130 | def setnil(self, node, content): 131 | """ 132 | Set the value of the I{node} to nill. 133 | @param node: A I{nil} node. 134 | @type node: L{Element} 135 | @param content: The content to set nil. 136 | @type content: L{Content} 137 | """ 138 | pass 139 | 140 | def setdefault(self, node, content): 141 | """ 142 | Set the value of the I{node} to a default value. 143 | @param node: A I{nil} node. 144 | @type node: L{Element} 145 | @param content: The content to set the default value. 146 | @type content: L{Content} 147 | @return: The default. 148 | """ 149 | pass 150 | 151 | def optional(self, content): 152 | """ 153 | Get whether the specified content is optional. 154 | @param content: The content which to check. 155 | @type content: L{Content} 156 | """ 157 | return False 158 | 159 | -------------------------------------------------------------------------------- /suds/mx/encoded.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides encoded I{marshaller} classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.mx import * 24 | from suds.mx.literal import Literal 25 | from suds.mx.typer import Typer 26 | from suds.sudsobject import Factory, Object 27 | from suds.xsd.query import TypeQuery 28 | 29 | log = getLogger(__name__) 30 | 31 | # 32 | # Add encoded extensions 33 | # aty = The soap (section 5) encoded array type. 34 | # 35 | Content.extensions.append('aty') 36 | 37 | 38 | class Encoded(Literal): 39 | """ 40 | A SOAP section (5) encoding marshaller. 41 | This marshaller supports rpc/encoded soap styles. 42 | """ 43 | 44 | def start(self, content): 45 | # 46 | # For soap encoded arrays, the 'aty' (array type) information 47 | # is extracted and added to the 'content'. Then, the content.value 48 | # is replaced with an object containing an 'item=[]' attribute 49 | # containing values that are 'typed' suds objects. 50 | # 51 | start = Literal.start(self, content) 52 | if start and isinstance(content.value, (list,tuple)): 53 | resolved = content.type.resolve() 54 | for c in resolved: 55 | if hasattr(c[0], 'aty'): 56 | content.aty = (content.tag, c[0].aty) 57 | self.cast(content) 58 | break 59 | return start 60 | 61 | def end(self, parent, content): 62 | # 63 | # For soap encoded arrays, the soapenc:arrayType attribute is 64 | # added with proper type and size information. 65 | # Eg: soapenc:arrayType="xs:int[3]" 66 | # 67 | Literal.end(self, parent, content) 68 | if content.aty is None: 69 | return 70 | tag, aty = content.aty 71 | ns0 = ('at0', aty[1]) 72 | ns1 = ('at1', 'http://schemas.xmlsoap.org/soap/encoding/') 73 | array = content.value.item 74 | child = parent.getChild(tag) 75 | child.addPrefix(ns0[0], ns0[1]) 76 | child.addPrefix(ns1[0], ns1[1]) 77 | name = '%s:arrayType' % ns1[0] 78 | value = '%s:%s[%d]' % (ns0[0], aty[0], len(array)) 79 | child.set(name, value) 80 | 81 | def encode(self, node, content): 82 | if content.type.any(): 83 | Typer.auto(node, content.value) 84 | return 85 | if content.real.any(): 86 | Typer.auto(node, content.value) 87 | return 88 | ns = None 89 | name = content.real.name 90 | if self.xstq: 91 | ns = content.real.namespace() 92 | Typer.manual(node, name, ns) 93 | 94 | def cast(self, content): 95 | """ 96 | Cast the I{untyped} list items found in content I{value}. 97 | Each items contained in the list is checked for XSD type information. 98 | Items (values) that are I{untyped}, are replaced with suds objects and 99 | type I{metadata} is added. 100 | @param content: The content holding the collection. 101 | @type content: L{Content} 102 | @return: self 103 | @rtype: L{Encoded} 104 | """ 105 | aty = content.aty[1] 106 | resolved = content.type.resolve() 107 | array = Factory.object(resolved.name) 108 | array.item = [] 109 | query = TypeQuery(aty) 110 | ref = query.execute(self.schema) 111 | if ref is None: 112 | raise TypeNotFound(qref) 113 | for x in content.value: 114 | if isinstance(x, (list, tuple)): 115 | array.item.append(x) 116 | continue 117 | if isinstance(x, Object): 118 | md = x.__metadata__ 119 | md.sxtype = ref 120 | array.item.append(x) 121 | continue 122 | if isinstance(x, dict): 123 | x = Factory.object(ref.name, x) 124 | md = x.__metadata__ 125 | md.sxtype = ref 126 | array.item.append(x) 127 | continue 128 | x = Factory.property(ref.name, x) 129 | md = x.__metadata__ 130 | md.sxtype = ref 131 | array.item.append(x) 132 | content.value = array 133 | return self 134 | -------------------------------------------------------------------------------- /suds/mx/typer.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides sx typing classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.mx import * 24 | from suds.sax import Namespace as NS 25 | from suds.sax.text import Text 26 | 27 | log = getLogger(__name__) 28 | 29 | 30 | class Typer: 31 | """ 32 | Provides XML node typing as either automatic or manual. 33 | @cvar types: A dict of class to xs type mapping. 34 | @type types: dict 35 | """ 36 | 37 | types = { 38 | int : ('int', NS.xsdns), 39 | long : ('long', NS.xsdns), 40 | float : ('float', NS.xsdns), 41 | str : ('string', NS.xsdns), 42 | unicode : ('string', NS.xsdns), 43 | Text : ('string', NS.xsdns), 44 | bool : ('boolean', NS.xsdns), 45 | } 46 | 47 | @classmethod 48 | def auto(cls, node, value=None): 49 | """ 50 | Automatically set the node's xsi:type attribute based on either I{value}'s 51 | class or the class of the node's text. When I{value} is an unmapped class, 52 | the default type (xs:any) is set. 53 | @param node: An XML node 54 | @type node: L{sax.element.Element} 55 | @param value: An object that is or would be the node's text. 56 | @type value: I{any} 57 | @return: The specified node. 58 | @rtype: L{sax.element.Element} 59 | """ 60 | if value is None: 61 | value = node.getText() 62 | if isinstance(value, Object): 63 | known = cls.known(value) 64 | if known.name is None: 65 | return node 66 | tm = (known.name, known.namespace()) 67 | else: 68 | tm = cls.types.get(value.__class__, cls.types.get(str)) 69 | cls.manual(node, *tm) 70 | return node 71 | 72 | @classmethod 73 | def manual(cls, node, tval, ns=None): 74 | """ 75 | Set the node's xsi:type attribute based on either I{value}'s 76 | class or the class of the node's text. Then adds the referenced 77 | prefix(s) to the node's prefix mapping. 78 | @param node: An XML node 79 | @type node: L{sax.element.Element} 80 | @param tval: The name of the schema type. 81 | @type tval: str 82 | @param ns: The XML namespace of I{tval}. 83 | @type ns: (prefix, uri) 84 | @return: The specified node. 85 | @rtype: L{sax.element.Element} 86 | """ 87 | xta = ':'.join((NS.xsins[0], 'type')) 88 | node.addPrefix(NS.xsins[0], NS.xsins[1]) 89 | if ns is None: 90 | node.set(xta, tval) 91 | else: 92 | ns = cls.genprefix(node, ns) 93 | qname = ':'.join((ns[0], tval)) 94 | node.set(xta, qname) 95 | node.addPrefix(ns[0], ns[1]) 96 | return node 97 | 98 | @classmethod 99 | def genprefix(cls, node, ns): 100 | """ 101 | Generate a prefix. 102 | @param node: An XML node on which the prefix will be used. 103 | @type node: L{sax.element.Element} 104 | @param ns: A namespace needing an unique prefix. 105 | @type ns: (prefix, uri) 106 | @return: The I{ns} with a new prefix. 107 | """ 108 | for n in range(1, 1024): 109 | p = 'ns%d' % n 110 | u = node.resolvePrefix(p, default=None) 111 | if u is None or u == ns[1]: 112 | return (p, ns[1]) 113 | raise Exception('auto prefix, exhausted') 114 | 115 | @classmethod 116 | def known(cls, object): 117 | try: 118 | md = object.__metadata__ 119 | known = md.sxtype 120 | return known 121 | except: 122 | pass 123 | 124 | -------------------------------------------------------------------------------- /suds/options.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Suds basic options classes. 19 | """ 20 | 21 | from suds.properties import * 22 | from suds.wsse import Security 23 | from suds.xsd.doctor import Doctor 24 | from suds.transport import Transport 25 | from suds.cache import Cache, NoCache 26 | 27 | 28 | class TpLinker(AutoLinker): 29 | """ 30 | Transport (auto) linker used to manage linkage between 31 | transport objects Properties and those Properties that contain them. 32 | """ 33 | 34 | def updated(self, properties, prev, next): 35 | if isinstance(prev, Transport): 36 | tp = Unskin(prev.options) 37 | properties.unlink(tp) 38 | if isinstance(next, Transport): 39 | tp = Unskin(next.options) 40 | properties.link(tp) 41 | 42 | 43 | class Options(Skin): 44 | """ 45 | Options: 46 | - B{cache} - The XML document cache. May be set (None) for no caching. 47 | - type: L{Cache} 48 | - default: L{NoCache} 49 | - B{faults} - Raise faults raised by server, 50 | else return tuple from service method invocation as (httpcode, object). 51 | - type: I{bool} 52 | - default: True 53 | - B{service} - The default service name. 54 | - type: I{str} 55 | - default: None 56 | - B{port} - The default service port name, not tcp port. 57 | - type: I{str} 58 | - default: None 59 | - B{location} - This overrides the service port address I{URL} defined 60 | in the WSDL. 61 | - type: I{str} 62 | - default: None 63 | - B{transport} - The message transport. 64 | - type: L{Transport} 65 | - default: None 66 | - B{soapheaders} - The soap headers to be included in the soap message. 67 | - type: I{any} 68 | - default: None 69 | - B{wsse} - The web services I{security} provider object. 70 | - type: L{Security} 71 | - default: None 72 | - B{doctor} - A schema I{doctor} object. 73 | - type: L{Doctor} 74 | - default: None 75 | - B{xstq} - The B{x}ml B{s}chema B{t}ype B{q}ualified flag indicates 76 | that the I{xsi:type} attribute values should be qualified by namespace. 77 | - type: I{bool} 78 | - default: True 79 | - B{prefixes} - Elements of the soap message should be qualified (when needed) 80 | using XML prefixes as opposed to xmlns="" syntax. 81 | - type: I{bool} 82 | - default: True 83 | - B{retxml} - Flag that causes the I{raw} soap envelope to be returned instead 84 | of the python object graph. 85 | - type: I{bool} 86 | - default: False 87 | - B{autoblend} - Flag that ensures that the schema(s) defined within the 88 | WSDL import each other. B{**Experimental**}. 89 | - type: I{bool} 90 | - default: False 91 | """ 92 | def __init__(self, **kwargs): 93 | domain = __name__ 94 | definitions = [ 95 | Definition('cache', Cache, NoCache()), 96 | Definition('faults', bool, True), 97 | Definition('transport', Transport, None, TpLinker()), 98 | Definition('service', (int, basestring), None), 99 | Definition('port', (int, basestring), None), 100 | Definition('location', basestring, None), 101 | Definition('soapheaders', (), ()), 102 | Definition('wsse', Security, None), 103 | Definition('doctor', Doctor, None), 104 | Definition('xstq', bool, True), 105 | Definition('prefixes', bool, True), 106 | Definition('retxml', bool, False), 107 | Definition('autoblend', bool, False), 108 | ] 109 | Skin.__init__(self, domain, definitions, kwargs) 110 | -------------------------------------------------------------------------------- /suds/reader.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains xml document reader classes. 19 | """ 20 | 21 | 22 | from suds.sax.parser import Parser 23 | from suds.transport import Request 24 | from suds.store import DocumentStore 25 | from logging import getLogger 26 | 27 | 28 | log = getLogger(__name__) 29 | 30 | 31 | class ObjectId(object): 32 | 33 | def __init__(self, name, suffix): 34 | self.name = name 35 | self.suffix = suffix 36 | 37 | 38 | class DocumentReader: 39 | """ 40 | The XML document reader provides an integration 41 | between the SAX L{Parser} and the document cache. 42 | @cvar suffix: The cache file suffix. 43 | @type suffix: str 44 | @ivar options: An options object. 45 | @type options: I{Options} 46 | """ 47 | 48 | suffix = 'pxd' 49 | 50 | def __init__(self, options): 51 | """ 52 | @param options: An options object. 53 | @type options: I{Options} 54 | """ 55 | self.options = options 56 | 57 | def open(self, url): 58 | """ 59 | Open an XML document at the specified I{url}. 60 | First, the document attempted to be retrieved from 61 | the I{object cache}. If not found, it is downloaded and 62 | parsed using the SAX parser. The result is added to the 63 | cache for the next open(). 64 | @param url: A document url. 65 | @type url: str. 66 | @return: The specified XML document. 67 | @rtype: I{Document} 68 | """ 69 | id = ObjectId(url, self.suffix) 70 | cache = self.options.cache 71 | d = cache.get(id) 72 | if d is None: 73 | d = self.download(url) 74 | cache.put(id, d) 75 | return d 76 | 77 | def download(self, url): 78 | """ 79 | Download the docuemnt. 80 | @param url: A document url. 81 | @type url: str. 82 | @return: A file pointer to the docuemnt. 83 | @rtype: file-like 84 | """ 85 | store = DocumentStore() 86 | fp = store.open(url) 87 | if fp is None: 88 | fp = self.options.transport.open(Request(url)) 89 | sax = Parser() 90 | return sax.parse(file=fp) 91 | 92 | 93 | class DefinitionsReader: 94 | """ 95 | The WSDL definitions reader provides an integration 96 | between the Definitions and the object cache. 97 | @cvar suffix: The cache file suffix. 98 | @type suffix: str 99 | @ivar options: An options object. 100 | @type options: I{Options} 101 | @ivar fn: A factory function (constructor) used to 102 | create the object not found in the cache. 103 | @type fn: I{Constructor} 104 | """ 105 | 106 | suffix = 'pw' 107 | 108 | def __init__(self, options, fn): 109 | """ 110 | @param options: An options object. 111 | @type options: I{Options} 112 | @param fn: A factory function (constructor) used to 113 | create the object not found in the cache. 114 | @type fn: I{Constructor} 115 | """ 116 | self.options = options 117 | self.fn = fn 118 | 119 | def open(self, url): 120 | """ 121 | Open a WSDL at the specified I{url}. 122 | First, the WSDL attempted to be retrieved from 123 | the I{object cache}. After unpickled from the cache, the 124 | I{options} attribute is restored. 125 | If not found, it is downloaded and instantiated using the 126 | I{fn} constructor and added to the cache for the next open(). 127 | @param url: A WSDL url. 128 | @type url: str. 129 | @return: The WSDL object. 130 | @rtype: I{Definitions} 131 | """ 132 | id = ObjectId(url, self.suffix) 133 | cache = self.options.cache 134 | d = cache.get(id) 135 | if d is None: 136 | d = self.fn(url, self.options) 137 | cache.put(id, d) 138 | else: 139 | d.options = self.options 140 | for imp in d.imports: 141 | imp.imported.options = self.options 142 | return d 143 | -------------------------------------------------------------------------------- /suds/sax/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The sax module contains a collection of classes that provide a 19 | (D)ocument (O)bject (M)odel representation of an XML document. 20 | The goal is to provide an easy, intuative interface for managing XML 21 | documents. Although, the term, DOM, is used above, this model is 22 | B{far} better. 23 | 24 | XML namespaces in suds are represented using a (2) element tuple 25 | containing the prefix and the URI. Eg: I{('tns', 'http://myns')} 26 | 27 | @var encoder: A I{pluggable} XML special character processor used to 28 | encode/decode strings. 29 | @type encoder: L{Encoder} 30 | """ 31 | 32 | from suds.sax.enc import Encoder 33 | 34 | # 35 | # pluggable XML special character encoder. 36 | # 37 | encoder = Encoder() 38 | 39 | 40 | def splitPrefix(name): 41 | """ 42 | Split the name into a tuple (I{prefix}, I{name}). The first element in 43 | the tuple is I{None} when the name does't have a prefix. 44 | @param name: A node name containing an optional prefix. 45 | @type name: basestring 46 | @return: A tuple containing the (2) parts of I{name} 47 | @rtype: (I{prefix}, I{name}) 48 | """ 49 | if isinstance(name, basestring) \ 50 | and ':' in name: 51 | return tuple(name.split(':', 1)) 52 | else: 53 | return (None, name) 54 | 55 | 56 | class Namespace: 57 | """ 58 | The namespace class represents XML namespaces. 59 | """ 60 | 61 | default = (None, None) 62 | xmlns = ('xml', 'http://www.w3.org/XML/1998/namespace') 63 | xsdns = ('xs', 'http://www.w3.org/2001/XMLSchema') 64 | xsins = ('xsi', 'http://www.w3.org/2001/XMLSchema-instance') 65 | all = (xsdns, xsins) 66 | 67 | @classmethod 68 | def create(cls, p=None, u=None): 69 | return (p, u) 70 | 71 | @classmethod 72 | def xsd(cls, ns): 73 | try: 74 | return cls.w3(ns) and ns[1].endswith('XMLSchema') 75 | except: 76 | pass 77 | return False 78 | 79 | @classmethod 80 | def xsi(cls, ns): 81 | try: 82 | return cls.w3(ns) and ns[1].endswith('XMLSchema-instance') 83 | except: 84 | pass 85 | return False 86 | 87 | @classmethod 88 | def xs(cls, ns): 89 | return ( cls.xsd(ns) or cls.xsi(ns) ) 90 | 91 | @classmethod 92 | def w3(cls, ns): 93 | try: 94 | return ns[1].startswith('http://www.w3.org') 95 | except: 96 | pass 97 | return False 98 | 99 | @classmethod 100 | def isns(cls, ns): 101 | try: 102 | return isinstance(ns, tuple) and len(ns) == len(cls.default) 103 | except: 104 | pass 105 | return False 106 | -------------------------------------------------------------------------------- /suds/sax/attribute.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides XML I{attribute} classes. 19 | """ 20 | 21 | import suds.sax 22 | from logging import getLogger 23 | from suds import * 24 | from suds.sax import * 25 | from suds.sax.text import Text 26 | 27 | log = getLogger(__name__) 28 | 29 | class Attribute: 30 | """ 31 | An XML attribute object. 32 | @ivar parent: The node containing this attribute 33 | @type parent: L{element.Element} 34 | @ivar prefix: The I{optional} namespace prefix. 35 | @type prefix: basestring 36 | @ivar name: The I{unqualified} name of the attribute 37 | @type name: basestring 38 | @ivar value: The attribute's value 39 | @type value: basestring 40 | """ 41 | def __init__(self, name, value=None): 42 | """ 43 | @param name: The attribute's name with I{optional} namespace prefix. 44 | @type name: basestring 45 | @param value: The attribute's value 46 | @type value: basestring 47 | """ 48 | self.parent = None 49 | self.prefix, self.name = splitPrefix(name) 50 | self.setValue(value) 51 | 52 | def clone(self, parent=None): 53 | """ 54 | Clone this object. 55 | @param parent: The parent for the clone. 56 | @type parent: L{element.Element} 57 | @return: A copy of this object assigned to the new parent. 58 | @rtype: L{Attribute} 59 | """ 60 | a = Attribute(self.qname(), self.value) 61 | a.parent = parent 62 | return a 63 | 64 | def qname(self): 65 | """ 66 | Get the B{fully} qualified name of this attribute 67 | @return: The fully qualified name. 68 | @rtype: basestring 69 | """ 70 | if self.prefix is None: 71 | return self.name 72 | else: 73 | return ':'.join((self.prefix, self.name)) 74 | 75 | def setValue(self, value): 76 | """ 77 | Set the attributes value 78 | @param value: The new value (may be None) 79 | @type value: basestring 80 | @return: self 81 | @rtype: L{Attribute} 82 | """ 83 | if isinstance(value, Text): 84 | self.value = value 85 | else: 86 | self.value = Text(value) 87 | return self 88 | 89 | def getValue(self, default=Text('')): 90 | """ 91 | Get the attributes value with optional default. 92 | @param default: An optional value to be return when the 93 | attribute's has not been set. 94 | @type default: basestring 95 | @return: The attribute's value, or I{default} 96 | @rtype: L{Text} 97 | """ 98 | if self.hasText(): 99 | return self.value 100 | else: 101 | return default 102 | 103 | def hasText(self): 104 | """ 105 | Get whether the attribute has I{text} and that it is not an empty 106 | (zero length) string. 107 | @return: True when has I{text}. 108 | @rtype: boolean 109 | """ 110 | return ( self.value is not None and len(self.value) ) 111 | 112 | def namespace(self): 113 | """ 114 | Get the attributes namespace. This may either be the namespace 115 | defined by an optional prefix, or its parent's namespace. 116 | @return: The attribute's namespace 117 | @rtype: (I{prefix}, I{name}) 118 | """ 119 | if self.prefix is None: 120 | return Namespace.default 121 | else: 122 | return self.resolvePrefix(self.prefix) 123 | 124 | def resolvePrefix(self, prefix): 125 | """ 126 | Resolve the specified prefix to a known namespace. 127 | @param prefix: A declared prefix 128 | @type prefix: basestring 129 | @return: The namespace that has been mapped to I{prefix} 130 | @rtype: (I{prefix}, I{name}) 131 | """ 132 | ns = Namespace.default 133 | if self.parent is not None: 134 | ns = self.parent.resolvePrefix(prefix) 135 | return ns 136 | 137 | def match(self, name=None, ns=None): 138 | """ 139 | Match by (optional) name and/or (optional) namespace. 140 | @param name: The optional attribute tag name. 141 | @type name: str 142 | @param ns: An optional namespace. 143 | @type ns: (I{prefix}, I{name}) 144 | @return: True if matched. 145 | @rtype: boolean 146 | """ 147 | if name is None: 148 | byname = True 149 | else: 150 | byname = ( self.name == name ) 151 | if ns is None: 152 | byns = True 153 | else: 154 | byns = ( self.namespace()[1] == ns[1] ) 155 | return ( byname and byns ) 156 | 157 | def __eq__(self, rhs): 158 | """ equals operator """ 159 | return rhs is not None and \ 160 | isinstance(rhs, Attribute) and \ 161 | self.prefix == rhs.name and \ 162 | self.name == rhs.name 163 | 164 | def __repr__(self): 165 | """ get a string representation """ 166 | return \ 167 | 'attr (prefix=%s, name=%s, value=(%s))' %\ 168 | (self.prefix, self.name, self.value) 169 | 170 | def __str__(self): 171 | """ get an xml string representation """ 172 | return unicode(self).encode('utf-8') 173 | 174 | def __unicode__(self): 175 | """ get an xml string representation """ 176 | n = self.qname() 177 | if self.hasText(): 178 | v = self.value.escape() 179 | else: 180 | v = self.value 181 | return u'%s="%s"' % (n, v) 182 | -------------------------------------------------------------------------------- /suds/sax/document.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides XML I{document} classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.sax import * 24 | from suds.sax.element import Element 25 | 26 | log = getLogger(__name__) 27 | 28 | class Document(Element): 29 | """ simple document """ 30 | 31 | def __init__(self, root=None): 32 | Element.__init__(self, 'document') 33 | if root is not None: 34 | self.append(root) 35 | 36 | def root(self): 37 | if len(self.children) > 0: 38 | return self.children[0] 39 | else: 40 | return None 41 | 42 | def __str__(self): 43 | return unicode(self).encode('utf-8') 44 | 45 | def __unicode__(self): 46 | result = '' 47 | root = self.root() 48 | if root is not None: 49 | result += '\n' 50 | result += root.str() 51 | return unicode(result) 52 | -------------------------------------------------------------------------------- /suds/sax/enc.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides XML I{special character} encoder classes. 19 | """ 20 | 21 | import re 22 | 23 | class Encoder: 24 | """ 25 | An XML special character encoder/decoder. 26 | @cvar encodings: A mapping of special characters encoding. 27 | @type encodings: [(str,str)] 28 | @cvar decodings: A mapping of special characters decoding. 29 | @type decodings: [(str,str)] 30 | @cvar special: A list of special characters 31 | @type special: [char] 32 | """ 33 | 34 | encodings = \ 35 | (( '&(?!(amp|lt|gt|quot|apos);)', '&' ),( '<', '<' ),( '>', '>' ),( '"', '"' ),("'", ''' )) 36 | decodings = \ 37 | (( '<', '<' ),( '>', '>' ),( '"', '"' ),( ''', "'" ),( '&', '&' )) 38 | special = \ 39 | ('&', '<', '>', '"', "'") 40 | 41 | def needsEncoding(self, s): 42 | """ 43 | Get whether string I{s} contains special characters. 44 | @param s: A string to check. 45 | @type s: str 46 | @return: True if needs encoding. 47 | @rtype: boolean 48 | """ 49 | if isinstance(s, basestring): 50 | for c in self.special: 51 | if c in s: 52 | return True 53 | return False 54 | 55 | def encode(self, s): 56 | """ 57 | Encode special characters found in string I{s}. 58 | @param s: A string to encode. 59 | @type s: str 60 | @return: The encoded string. 61 | @rtype: str 62 | """ 63 | if isinstance(s, basestring) and self.needsEncoding(s): 64 | for x in self.encodings: 65 | s = re.sub(x[0], x[1], s) 66 | return s 67 | 68 | def decode(self, s): 69 | """ 70 | Decode special characters encodings found in string I{s}. 71 | @param s: A string to decode. 72 | @type s: str 73 | @return: The decoded string. 74 | @rtype: str 75 | """ 76 | if isinstance(s, basestring) and '&' in s: 77 | for x in self.decodings: 78 | s = s.replace(x[0], x[1]) 79 | return s 80 | -------------------------------------------------------------------------------- /suds/sax/parser.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The sax module contains a collection of classes that provide a 19 | (D)ocument (O)bject (M)odel representation of an XML document. 20 | The goal is to provide an easy, intuative interface for managing XML 21 | documents. Although, the term, DOM, is used above, this model is 22 | B{far} better. 23 | 24 | XML namespaces in suds are represented using a (2) element tuple 25 | containing the prefix and the URI. Eg: I{('tns', 'http://myns')} 26 | 27 | """ 28 | 29 | from logging import getLogger 30 | import suds.metrics 31 | from suds import * 32 | from suds.sax import * 33 | from suds.sax.document import Document 34 | from suds.sax.element import Element 35 | from suds.sax.text import Text 36 | from suds.sax.attribute import Attribute 37 | from xml.sax import make_parser, InputSource, ContentHandler 38 | from xml.sax.handler import feature_external_ges 39 | from cStringIO import StringIO 40 | 41 | log = getLogger(__name__) 42 | 43 | 44 | class Handler(ContentHandler): 45 | """ sax hanlder """ 46 | 47 | def __init__(self): 48 | self.nodes = [Document()] 49 | 50 | def startElement(self, name, attrs): 51 | top = self.top() 52 | node = Element(unicode(name), parent=top) 53 | for a in attrs.getNames(): 54 | n = unicode(a) 55 | v = unicode(attrs.getValue(a)) 56 | attribute = Attribute(n,v) 57 | if self.mapPrefix(node, attribute): 58 | continue 59 | node.append(attribute) 60 | node.charbuffer = [] 61 | top.append(node) 62 | self.push(node) 63 | 64 | def mapPrefix(self, node, attribute): 65 | skip = False 66 | if attribute.name == 'xmlns': 67 | if len(attribute.value): 68 | node.expns = unicode(attribute.value) 69 | skip = True 70 | elif attribute.prefix == 'xmlns': 71 | prefix = attribute.name 72 | node.nsprefixes[prefix] = unicode(attribute.value) 73 | skip = True 74 | return skip 75 | 76 | def endElement(self, name): 77 | name = unicode(name) 78 | current = self.top() 79 | if len(current.charbuffer): 80 | current.text = Text(u''.join(current.charbuffer)) 81 | del current.charbuffer 82 | if len(current): 83 | current.trim() 84 | currentqname = current.qname() 85 | if name == currentqname: 86 | self.pop() 87 | else: 88 | raise Exception('malformed document') 89 | 90 | def characters(self, content): 91 | text = unicode(content) 92 | node = self.top() 93 | node.charbuffer.append(text) 94 | 95 | def push(self, node): 96 | self.nodes.append(node) 97 | return node 98 | 99 | def pop(self): 100 | return self.nodes.pop() 101 | 102 | def top(self): 103 | return self.nodes[len(self.nodes)-1] 104 | 105 | 106 | class Parser: 107 | """ SAX Parser """ 108 | 109 | @classmethod 110 | def saxparser(cls): 111 | p = make_parser() 112 | p.setFeature(feature_external_ges, 0) 113 | h = Handler() 114 | p.setContentHandler(h) 115 | return (p, h) 116 | 117 | def parse(self, file=None, string=None): 118 | """ 119 | SAX parse XML text. 120 | @param file: Parse a python I{file-like} object. 121 | @type file: I{file-like} object. 122 | @param string: Parse string XML. 123 | @type string: str 124 | """ 125 | timer = metrics.Timer() 126 | timer.start() 127 | sax, handler = self.saxparser() 128 | if file is not None: 129 | sax.parse(file) 130 | timer.stop() 131 | metrics.log.debug('sax (%s) duration: %s', file, timer) 132 | return handler.nodes[0] 133 | if string is not None: 134 | source = InputSource(None) 135 | source.setByteStream(StringIO(string)) 136 | sax.parse(source) 137 | timer.stop() 138 | metrics.log.debug('%s\nsax duration: %s', string, timer) 139 | return handler.nodes[0] -------------------------------------------------------------------------------- /suds/sax/text.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains XML text classes. 19 | """ 20 | 21 | from suds import * 22 | from suds.sax import * 23 | 24 | 25 | class Text(unicode): 26 | """ 27 | An XML text object used to represent text content. 28 | @ivar lang: The (optional) language flag. 29 | @type lang: bool 30 | @ivar escaped: The (optional) XML special character escaped flag. 31 | @type escaped: bool 32 | """ 33 | __slots__ = ('lang', 'escaped',) 34 | 35 | @classmethod 36 | def __valid(cls, *args): 37 | return ( len(args) and args[0] is not None ) 38 | 39 | def __new__(cls, *args, **kwargs): 40 | if cls.__valid(*args): 41 | lang = kwargs.pop('lang', None) 42 | escaped = kwargs.pop('escaped', False) 43 | result = super(Text, cls).__new__(cls, *args, **kwargs) 44 | result.lang = lang 45 | result.escaped = escaped 46 | else: 47 | result = None 48 | return result 49 | 50 | def escape(self): 51 | """ 52 | Encode (escape) special XML characters. 53 | @return: The text with XML special characters escaped. 54 | @rtype: L{Text} 55 | """ 56 | if not self.escaped: 57 | post = sax.encoder.encode(self) 58 | escaped = ( post != self ) 59 | return Text(post, lang=self.lang, escaped=escaped) 60 | return self 61 | 62 | def unescape(self): 63 | """ 64 | Decode (unescape) special XML characters. 65 | @return: The text with escaped XML special characters decoded. 66 | @rtype: L{Text} 67 | """ 68 | if self.escaped: 69 | post = sax.encoder.decode(self) 70 | return Text(post, lang=self.lang) 71 | return self 72 | 73 | def trim(self): 74 | post = self.strip() 75 | return Text(post, lang=self.lang, escaped=self.escaped) 76 | 77 | def __add__(self, other): 78 | joined = u''.join((self, other)) 79 | result = Text(joined, lang=self.lang, escaped=self.escaped) 80 | if isinstance(other, Text): 81 | result.escaped = ( self.escaped or other.escaped ) 82 | return result 83 | 84 | def __repr__(self): 85 | s = [self] 86 | if self.lang is not None: 87 | s.append(' [%s]' % self.lang) 88 | if self.escaped: 89 | s.append(' ') 90 | return ''.join(s) 91 | 92 | def __getstate__(self): 93 | state = {} 94 | for k in self.__slots__: 95 | state[k] = getattr(self, k) 96 | return state 97 | 98 | def __setstate__(self, state): 99 | for k in self.__slots__: 100 | setattr(self, k, state[k]) 101 | 102 | 103 | class Raw(Text): 104 | """ 105 | Raw text which is not XML escaped. 106 | This may include I{string} XML. 107 | """ 108 | def escape(self): 109 | return self 110 | 111 | def unescape(self): 112 | return self 113 | 114 | def __add__(self, other): 115 | joined = u''.join((self, other)) 116 | return Raw(joined, lang=self.lang) 117 | -------------------------------------------------------------------------------- /suds/serviceproxy.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The service proxy provides access to web services. 19 | 20 | Replaced by: L{client.Client} 21 | """ 22 | 23 | from logging import getLogger 24 | from suds import * 25 | from suds.client import Client 26 | 27 | log = getLogger(__name__) 28 | 29 | 30 | class ServiceProxy(object): 31 | 32 | """ 33 | A lightweight soap based web service proxy. 34 | @ivar __client__: A client. 35 | Everything is delegated to the 2nd generation API. 36 | @type __client__: L{Client} 37 | @note: Deprecated, replaced by L{Client}. 38 | """ 39 | 40 | def __init__(self, url, **kwargs): 41 | """ 42 | @param url: The URL for the WSDL. 43 | @type url: str 44 | @param kwargs: keyword arguments. 45 | @keyword faults: Raise faults raised by server (default:True), 46 | else return tuple from service method invocation as (http code, object). 47 | @type faults: boolean 48 | @keyword proxy: An http proxy to be specified on requests (default:{}). 49 | The proxy is defined as {protocol:proxy,} 50 | @type proxy: dict 51 | """ 52 | client = Client(url, **kwargs) 53 | self.__client__ = client 54 | 55 | def get_instance(self, name): 56 | """ 57 | Get an instance of a WSDL type by name 58 | @param name: The name of a type defined in the WSDL. 59 | @type name: str 60 | @return: An instance on success, else None 61 | @rtype: L{sudsobject.Object} 62 | """ 63 | return self.__client__.factory.create(name) 64 | 65 | def get_enum(self, name): 66 | """ 67 | Get an instance of an enumeration defined in the WSDL by name. 68 | @param name: The name of a enumeration defined in the WSDL. 69 | @type name: str 70 | @return: An instance on success, else None 71 | @rtype: L{sudsobject.Object} 72 | """ 73 | return self.__client__.factory.create(name) 74 | 75 | def __str__(self): 76 | return str(self.__client__) 77 | 78 | def __unicode__(self): 79 | return unicode(self.__client__) 80 | 81 | def __getattr__(self, name): 82 | builtin = name.startswith('__') and name.endswith('__') 83 | if builtin: 84 | return self.__dict__[name] 85 | else: 86 | return getattr(self.__client__.service, name) -------------------------------------------------------------------------------- /suds/soaparray.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{soaparray} module provides XSD extensions for handling 19 | soap (section 5) encoded arrays. 20 | """ 21 | 22 | from suds import * 23 | from logging import getLogger 24 | from suds.xsd.sxbasic import Factory as SXFactory 25 | from suds.xsd.sxbasic import Attribute as SXAttribute 26 | 27 | 28 | class Attribute(SXAttribute): 29 | """ 30 | Represents an XSD that handles special 31 | attributes that are extensions for WSDLs. 32 | @ivar aty: Array type information. 33 | @type aty: The value of wsdl:arrayType. 34 | """ 35 | 36 | def __init__(self, schema, root, aty): 37 | """ 38 | @param aty: Array type information. 39 | @type aty: The value of wsdl:arrayType. 40 | """ 41 | SXAttribute.__init__(self, schema, root) 42 | self.aty = aty[:-2] 43 | 44 | def autoqualified(self): 45 | aqs = SXAttribute.autoqualified(self) 46 | aqs.append('aty') 47 | return aqs 48 | 49 | def description(self): 50 | d = SXAttribute.description(self) 51 | d = d+('aty',) 52 | return d 53 | 54 | # 55 | # Builder function, only builds Attribute when arrayType 56 | # attribute is defined on root. 57 | # 58 | def __fn(x, y): 59 | ns = (None, "http://schemas.xmlsoap.org/wsdl/") 60 | aty = y.get('arrayType', ns=ns) 61 | if aty is None: 62 | return SXAttribute(x, y) 63 | else: 64 | return Attribute(x, y, aty) 65 | 66 | # 67 | # Remap tags to __fn() builder. 68 | # 69 | SXFactory.maptag('attribute', __fn) -------------------------------------------------------------------------------- /suds/transport/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains transport interface (classes). 19 | """ 20 | 21 | 22 | class TransportError(Exception): 23 | def __init__(self, reason, httpcode, fp=None): 24 | Exception.__init__(self, reason) 25 | self.httpcode = httpcode 26 | self.fp = fp 27 | 28 | class Request: 29 | """ 30 | A transport request 31 | @ivar url: The url for the request. 32 | @type url: str 33 | @ivar message: The message to be sent in a POST request. 34 | @type message: str 35 | @ivar headers: The http headers to be used for the request. 36 | @type headers: dict 37 | """ 38 | 39 | def __init__(self, url, message=None): 40 | """ 41 | @param url: The url for the request. 42 | @type url: str 43 | @param message: The (optional) message to be send in the request. 44 | @type message: str 45 | """ 46 | self.url = url 47 | self.headers = {} 48 | self.message = message 49 | 50 | def __str__(self): 51 | s = [] 52 | s.append('URL:%s' % self.url) 53 | s.append('HEADERS: %s' % self.headers) 54 | s.append('MESSAGE:') 55 | s.append(self.message) 56 | return '\n'.join(s) 57 | 58 | 59 | class Reply: 60 | """ 61 | A transport reply 62 | @ivar code: The http code returned. 63 | @type code: int 64 | @ivar message: The message to be sent in a POST request. 65 | @type message: str 66 | @ivar headers: The http headers to be used for the request. 67 | @type headers: dict 68 | """ 69 | 70 | def __init__(self, code, headers, message): 71 | """ 72 | @param code: The http code returned. 73 | @type code: int 74 | @param headers: The http returned headers. 75 | @type headers: dict 76 | @param message: The (optional) reply message received. 77 | @type message: str 78 | """ 79 | self.code = code 80 | self.headers = headers 81 | self.message = message 82 | 83 | def __str__(self): 84 | s = [] 85 | s.append('CODE: %s' % self.code) 86 | s.append('HEADERS: %s' % self.headers) 87 | s.append('MESSAGE:') 88 | s.append(self.message) 89 | return '\n'.join(s) 90 | 91 | 92 | class Transport: 93 | """ 94 | The transport I{interface}. 95 | """ 96 | 97 | def __init__(self): 98 | """ 99 | Constructor. 100 | """ 101 | from suds.transport.options import Options 102 | self.options = Options() 103 | del Options 104 | 105 | def open(self, request): 106 | """ 107 | Open the url in the specified request. 108 | @param request: A transport request. 109 | @type request: L{Request} 110 | @return: An input stream. 111 | @rtype: stream 112 | @raise TransportError: On all transport errors. 113 | """ 114 | raise Exception('not-implemented') 115 | 116 | def send(self, request): 117 | """ 118 | Send soap message. Implementations are expected to handle: 119 | - proxies 120 | - I{http} headers 121 | - cookies 122 | - sending message 123 | - brokering exceptions into L{TransportError} 124 | @param request: A transport request. 125 | @type request: L{Request} 126 | @return: The reply 127 | @rtype: L{Reply} 128 | @raise TransportError: On all transport errors. 129 | """ 130 | raise Exception('not-implemented') 131 | -------------------------------------------------------------------------------- /suds/transport/http.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains classes for basic HTTP transport implementations. 19 | """ 20 | 21 | import urllib2 as u2 22 | import base64 23 | import socket 24 | from suds.transport import * 25 | from suds.properties import Unskin 26 | from urlparse import urlparse 27 | from cookielib import CookieJar 28 | from logging import getLogger 29 | 30 | log = getLogger(__name__) 31 | 32 | 33 | class HttpTransport(Transport): 34 | """ 35 | HTTP transport using urllib2. Provided basic http transport 36 | that provides for cookies, proxies but no authentication. 37 | """ 38 | 39 | def __init__(self, **kwargs): 40 | """ 41 | @param kwargs: Keyword arguments. 42 | - B{proxy} - An http proxy to be specified on requests. 43 | The proxy is defined as {protocol:proxy,} 44 | - type: I{dict} 45 | - default: {} 46 | - B{timeout} - Set the url open timeout (seconds). 47 | - type: I{float} 48 | - default: 90 49 | """ 50 | Transport.__init__(self) 51 | Unskin(self.options).update(kwargs) 52 | self.cookiejar = CookieJar() 53 | self.proxy = {} 54 | self.urlopener = None 55 | 56 | def open(self, request): 57 | try: 58 | url = request.url 59 | log.debug('opening (%s)', url) 60 | u2request = u2.Request(url) 61 | self.proxy = self.options.proxy 62 | return self.u2open(u2request) 63 | except u2.HTTPError, e: 64 | raise TransportError(str(e), e.code, e.fp) 65 | 66 | def send(self, request): 67 | result = None 68 | url = request.url 69 | msg = request.message 70 | headers = request.headers 71 | try: 72 | u2request = u2.Request(url, msg, headers) 73 | self.addcookies(u2request) 74 | self.proxy = self.options.proxy 75 | request.headers.update(u2request.headers) 76 | log.debug('sending:\n%s', request) 77 | fp = self.u2open(u2request) 78 | self.getcookies(fp, u2request) 79 | result = Reply(200, fp.headers.dict, fp.read()) 80 | log.debug('received:\n%s', result) 81 | except u2.HTTPError, e: 82 | if e.code in (202,204): 83 | result = None 84 | else: 85 | raise TransportError(e.msg, e.code, e.fp) 86 | return result 87 | 88 | def addcookies(self, u2request): 89 | """ 90 | Add cookies in the cookiejar to the request. 91 | @param u2request: A urllib2 request. 92 | @rtype: u2request: urllib2.Requet. 93 | """ 94 | self.cookiejar.add_cookie_header(u2request) 95 | 96 | def getcookies(self, fp, u2request): 97 | """ 98 | Add cookies in the request to the cookiejar. 99 | @param u2request: A urllib2 request. 100 | @rtype: u2request: urllib2.Requet. 101 | """ 102 | self.cookiejar.extract_cookies(fp, u2request) 103 | 104 | def u2open(self, u2request): 105 | """ 106 | Open a connection. 107 | @param u2request: A urllib2 request. 108 | @type u2request: urllib2.Requet. 109 | @return: The opened file-like urllib2 object. 110 | @rtype: fp 111 | """ 112 | tm = self.options.timeout 113 | url = self.u2opener() 114 | if self.u2ver() < 2.6: 115 | socket.setdefaulttimeout(tm) 116 | return url.open(u2request) 117 | else: 118 | return url.open(u2request, timeout=tm) 119 | 120 | def u2opener(self): 121 | """ 122 | Create a urllib opener. 123 | @return: An opener. 124 | @rtype: I{OpenerDirector} 125 | """ 126 | if self.urlopener is None: 127 | return u2.build_opener(*self.u2handlers()) 128 | else: 129 | return self.urlopener 130 | 131 | def u2handlers(self): 132 | """ 133 | Get a collection of urllib handlers. 134 | @return: A list of handlers to be installed in the opener. 135 | @rtype: [Handler,...] 136 | """ 137 | handlers = [] 138 | handlers.append(u2.ProxyHandler(self.proxy)) 139 | return handlers 140 | 141 | def u2ver(self): 142 | """ 143 | Get the major/minor version of the urllib2 lib. 144 | @return: The urllib2 version. 145 | @rtype: float 146 | """ 147 | try: 148 | part = u2.__version__.split('.', 1) 149 | n = float('.'.join(part)) 150 | return n 151 | except Exception, e: 152 | log.exception(e) 153 | return 0 154 | 155 | def __deepcopy__(self, memo={}): 156 | clone = self.__class__() 157 | p = Unskin(self.options) 158 | cp = Unskin(clone.options) 159 | cp.update(p) 160 | return clone 161 | 162 | 163 | class HttpAuthenticated(HttpTransport): 164 | """ 165 | Provides basic http authentication for servers that don't follow 166 | the specified challenge / response model. This implementation 167 | appends the I{Authorization} http header with base64 encoded 168 | credentials on every http request. 169 | """ 170 | 171 | def open(self, request): 172 | self.addcredentials(request) 173 | return HttpTransport.open(self, request) 174 | 175 | def send(self, request): 176 | self.addcredentials(request) 177 | return HttpTransport.send(self, request) 178 | 179 | def addcredentials(self, request): 180 | credentials = self.credentials() 181 | if not (None in credentials): 182 | encoded = base64.encodestring(':'.join(credentials)) 183 | basic = 'Basic %s' % encoded[:-1] 184 | request.headers['Authorization'] = basic 185 | 186 | def credentials(self): 187 | return (self.options.username, self.options.password) -------------------------------------------------------------------------------- /suds/transport/https.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains classes for basic HTTP (authenticated) transport implementations. 19 | """ 20 | 21 | import urllib2 as u2 22 | from suds.transport import * 23 | from suds.transport.http import HttpTransport 24 | from logging import getLogger 25 | 26 | log = getLogger(__name__) 27 | 28 | 29 | class HttpAuthenticated(HttpTransport): 30 | """ 31 | Provides basic http authentication that follows the RFC-2617 specification. 32 | As defined by specifications, credentials are provided to the server 33 | upon request (HTTP/1.0 401 Authorization Required) by the server only. 34 | @ivar pm: The password manager. 35 | @ivar handler: The authentication handler. 36 | """ 37 | 38 | def __init__(self, **kwargs): 39 | """ 40 | @param kwargs: Keyword arguments. 41 | - B{proxy} - An http proxy to be specified on requests. 42 | The proxy is defined as {protocol:proxy,} 43 | - type: I{dict} 44 | - default: {} 45 | - B{timeout} - Set the url open timeout (seconds). 46 | - type: I{float} 47 | - default: 90 48 | - B{username} - The username used for http authentication. 49 | - type: I{str} 50 | - default: None 51 | - B{password} - The password used for http authentication. 52 | - type: I{str} 53 | - default: None 54 | """ 55 | HttpTransport.__init__(self, **kwargs) 56 | self.pm = u2.HTTPPasswordMgrWithDefaultRealm() 57 | 58 | def open(self, request): 59 | self.addcredentials(request) 60 | return HttpTransport.open(self, request) 61 | 62 | def send(self, request): 63 | self.addcredentials(request) 64 | return HttpTransport.send(self, request) 65 | 66 | def addcredentials(self, request): 67 | credentials = self.credentials() 68 | if not (None in credentials): 69 | u = credentials[0] 70 | p = credentials[1] 71 | self.pm.add_password(None, request.url, u, p) 72 | 73 | def credentials(self): 74 | return (self.options.username, self.options.password) 75 | 76 | def u2handlers(self): 77 | handlers = HttpTransport.u2handlers(self) 78 | handlers.append(u2.HTTPBasicAuthHandler(self.pm)) 79 | return handlers 80 | 81 | 82 | class WindowsHttpAuthenticated(HttpAuthenticated): 83 | """ 84 | Provides Windows (NTLM) http authentication. 85 | @ivar pm: The password manager. 86 | @ivar handler: The authentication handler. 87 | @author: Christopher Bess 88 | """ 89 | 90 | def u2handlers(self): 91 | # try to import ntlm support 92 | try: 93 | from ntlm import HTTPNtlmAuthHandler 94 | except ImportError: 95 | raise Exception("Cannot import python-ntlm module") 96 | handlers = HttpTransport.u2handlers(self) 97 | handlers.append(HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(self.pm)) 98 | return handlers 99 | -------------------------------------------------------------------------------- /suds/transport/options.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Contains classes for transport options. 19 | """ 20 | 21 | 22 | from suds.transport import * 23 | from suds.properties import * 24 | 25 | 26 | class Options(Skin): 27 | """ 28 | Options: 29 | - B{proxy} - An http proxy to be specified on requests. 30 | The proxy is defined as {protocol:proxy,} 31 | - type: I{dict} 32 | - default: {} 33 | - B{timeout} - Set the url open timeout (seconds). 34 | - type: I{float} 35 | - default: 90 36 | - B{headers} - Extra HTTP headers. 37 | - type: I{dict} 38 | - I{str} B{http} - The I{http} protocol proxy URL. 39 | - I{str} B{https} - The I{https} protocol proxy URL. 40 | - default: {} 41 | - B{username} - The username used for http authentication. 42 | - type: I{str} 43 | - default: None 44 | - B{password} - The password used for http authentication. 45 | - type: I{str} 46 | - default: None 47 | """ 48 | def __init__(self, **kwargs): 49 | domain = __name__ 50 | definitions = [ 51 | Definition('proxy', dict, {}), 52 | Definition('timeout', (int,float), 90), 53 | Definition('headers', dict, {}), 54 | Definition('username', basestring, None), 55 | Definition('password', basestring, None), 56 | ] 57 | Skin.__init__(self, domain, definitions, kwargs) -------------------------------------------------------------------------------- /suds/umx/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides modules containing classes to support 19 | unmarshalling (XML). 20 | """ 21 | 22 | from suds.sudsobject import Object 23 | 24 | 25 | 26 | class Content(Object): 27 | """ 28 | @ivar node: The content source node. 29 | @type node: L{sax.element.Element} 30 | @ivar data: The (optional) content data. 31 | @type data: L{Object} 32 | @ivar text: The (optional) content (xml) text. 33 | @type text: basestring 34 | """ 35 | 36 | extensions = [] 37 | 38 | def __init__(self, node, **kwargs): 39 | Object.__init__(self) 40 | self.node = node 41 | self.data = None 42 | self.text = None 43 | for k,v in kwargs.items(): 44 | setattr(self, k, v) 45 | 46 | def __getattr__(self, name): 47 | if name not in self.__dict__: 48 | if name in self.extensions: 49 | v = None 50 | setattr(self, name, v) 51 | else: 52 | raise AttributeError, \ 53 | 'Content has no attribute %s' % name 54 | else: 55 | v = self.__dict__[name] 56 | return v -------------------------------------------------------------------------------- /suds/umx/attrlist.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides filtered attribute list classes. 19 | """ 20 | 21 | from suds import * 22 | from suds.umx import * 23 | from suds.sax import Namespace 24 | 25 | 26 | class AttrList: 27 | """ 28 | A filtered attribute list. 29 | Items are included during iteration if they are in either the (xs) or 30 | (xml) namespaces. 31 | @ivar raw: The I{raw} attribute list. 32 | @type raw: list 33 | """ 34 | def __init__(self, attributes): 35 | """ 36 | @param attributes: A list of attributes 37 | @type attributes: list 38 | """ 39 | self.raw = attributes 40 | 41 | def real(self): 42 | """ 43 | Get list of I{real} attributes which exclude xs and xml attributes. 44 | @return: A list of I{real} attributes. 45 | @rtype: I{generator} 46 | """ 47 | for a in self.raw: 48 | if self.skip(a): continue 49 | yield a 50 | 51 | def rlen(self): 52 | """ 53 | Get the number of I{real} attributes which exclude xs and xml attributes. 54 | @return: A count of I{real} attributes. 55 | @rtype: L{int} 56 | """ 57 | n = 0 58 | for a in self.real(): 59 | n += 1 60 | return n 61 | 62 | def lang(self): 63 | """ 64 | Get list of I{filtered} attributes which exclude xs. 65 | @return: A list of I{filtered} attributes. 66 | @rtype: I{generator} 67 | """ 68 | for a in self.raw: 69 | if a.qname() == 'xml:lang': 70 | return a.value 71 | return None 72 | 73 | def skip(self, attr): 74 | """ 75 | Get whether to skip (filter-out) the specified attribute. 76 | @param attr: An attribute. 77 | @type attr: I{Attribute} 78 | @return: True if should be skipped. 79 | @rtype: bool 80 | """ 81 | ns = attr.namespace() 82 | skip = ( 83 | Namespace.xmlns[1], 84 | 'http://schemas.xmlsoap.org/soap/encoding/', 85 | 'http://schemas.xmlsoap.org/soap/envelope/', 86 | 'http://www.w3.org/2003/05/soap-envelope', 87 | ) 88 | return ( Namespace.xs(ns) or ns[1] in skip ) 89 | -------------------------------------------------------------------------------- /suds/umx/basic.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides basic unmarshaller classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.umx import * 24 | from suds.umx.core import Core 25 | 26 | 27 | class Basic(Core): 28 | """ 29 | A object builder (unmarshaller). 30 | """ 31 | 32 | def process(self, node): 33 | """ 34 | Process an object graph representation of the xml I{node}. 35 | @param node: An XML tree. 36 | @type node: L{sax.element.Element} 37 | @return: A suds object. 38 | @rtype: L{Object} 39 | """ 40 | content = Content(node) 41 | return Core.process(self, content) -------------------------------------------------------------------------------- /suds/umx/core.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides base classes for XML->object I{unmarshalling}. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.umx import * 24 | from suds.umx.attrlist import AttrList 25 | from suds.sax.text import Text 26 | from suds.sudsobject import Factory, merge 27 | 28 | 29 | log = getLogger(__name__) 30 | 31 | reserved = { 'class':'cls', 'def':'dfn', } 32 | 33 | class Core: 34 | """ 35 | The abstract XML I{node} unmarshaller. This class provides the 36 | I{core} unmarshalling functionality. 37 | """ 38 | 39 | def process(self, content): 40 | """ 41 | Process an object graph representation of the xml I{node}. 42 | @param content: The current content being unmarshalled. 43 | @type content: L{Content} 44 | @return: A suds object. 45 | @rtype: L{Object} 46 | """ 47 | self.reset() 48 | return self.append(content) 49 | 50 | def append(self, content): 51 | """ 52 | Process the specified node and convert the XML document into 53 | a I{suds} L{object}. 54 | @param content: The current content being unmarshalled. 55 | @type content: L{Content} 56 | @return: A I{append-result} tuple as: (L{Object}, I{value}) 57 | @rtype: I{append-result} 58 | @note: This is not the proper entry point. 59 | @see: L{process()} 60 | """ 61 | self.start(content) 62 | self.append_attributes(content) 63 | self.append_children(content) 64 | self.append_text(content) 65 | self.end(content) 66 | return self.postprocess(content) 67 | 68 | def postprocess(self, content): 69 | """ 70 | Perform final processing of the resulting data structure as follows: 71 | - Mixed values (children and text) will have a result of the I{content.node}. 72 | - Simi-simple values (attributes, no-children and text) will have a result of a 73 | property object. 74 | - Simple values (no-attributes, no-children with text nodes) will have a string 75 | result equal to the value of the content.node.getText(). 76 | @param content: The current content being unmarshalled. 77 | @type content: L{Content} 78 | @return: The post-processed result. 79 | @rtype: I{any} 80 | """ 81 | node = content.node 82 | if len(node.children) and node.hasText(): 83 | return node 84 | attributes = AttrList(node.attributes) 85 | if attributes.rlen() and \ 86 | not len(node.children) and \ 87 | node.hasText(): 88 | p = Factory.property(node.name, node.getText()) 89 | return merge(content.data, p) 90 | if len(content.data): 91 | return content.data 92 | lang = attributes.lang() 93 | if content.node.isnil(): 94 | return None 95 | if not len(node.children) and content.text is None: 96 | if self.nillable(content): 97 | return None 98 | else: 99 | return Text('', lang=lang) 100 | if isinstance(content.text, basestring): 101 | return Text(content.text, lang=lang) 102 | else: 103 | return content.text 104 | 105 | def append_attributes(self, content): 106 | """ 107 | Append attribute nodes into L{Content.data}. 108 | Attributes in the I{schema} or I{xml} namespaces are skipped. 109 | @param content: The current content being unmarshalled. 110 | @type content: L{Content} 111 | """ 112 | attributes = AttrList(content.node.attributes) 113 | for attr in attributes.real(): 114 | name = attr.name 115 | value = attr.value 116 | self.append_attribute(name, value, content) 117 | 118 | def append_attribute(self, name, value, content): 119 | """ 120 | Append an attribute name/value into L{Content.data}. 121 | @param name: The attribute name 122 | @type name: basestring 123 | @param value: The attribute's value 124 | @type value: basestring 125 | @param content: The current content being unmarshalled. 126 | @type content: L{Content} 127 | """ 128 | key = name 129 | key = '_%s' % reserved.get(key, key) 130 | setattr(content.data, key, value) 131 | 132 | def append_children(self, content): 133 | """ 134 | Append child nodes into L{Content.data} 135 | @param content: The current content being unmarshalled. 136 | @type content: L{Content} 137 | """ 138 | for child in content.node.children: 139 | cont = Content(child) 140 | cval = self.append(cont) 141 | key = reserved.get(child.name, child.name) 142 | if key in content.data: 143 | v = getattr(content.data, key) 144 | if isinstance(v, list): 145 | v.append(cval) 146 | else: 147 | setattr(content.data, key, [v, cval]) 148 | continue 149 | if self.unbounded(cont): 150 | if cval is None: 151 | setattr(content.data, key, []) 152 | else: 153 | setattr(content.data, key, [cval,]) 154 | else: 155 | setattr(content.data, key, cval) 156 | 157 | def append_text(self, content): 158 | """ 159 | Append text nodes into L{Content.data} 160 | @param content: The current content being unmarshalled. 161 | @type content: L{Content} 162 | """ 163 | if content.node.hasText(): 164 | content.text = content.node.getText() 165 | 166 | def reset(self): 167 | pass 168 | 169 | def start(self, content): 170 | """ 171 | Processing on I{node} has started. Build and return 172 | the proper object. 173 | @param content: The current content being unmarshalled. 174 | @type content: L{Content} 175 | @return: A subclass of Object. 176 | @rtype: L{Object} 177 | """ 178 | content.data = Factory.object(content.node.name) 179 | 180 | def end(self, content): 181 | """ 182 | Processing on I{node} has ended. 183 | @param content: The current content being unmarshalled. 184 | @type content: L{Content} 185 | """ 186 | pass 187 | 188 | def bounded(self, content): 189 | """ 190 | Get whether the content is bounded (not a list). 191 | @param content: The current content being unmarshalled. 192 | @type content: L{Content} 193 | @return: True if bounded, else False 194 | @rtype: boolean 195 | '""" 196 | return ( not self.unbounded(content) ) 197 | 198 | def unbounded(self, content): 199 | """ 200 | Get whether the object is unbounded (a list). 201 | @param content: The current content being unmarshalled. 202 | @type content: L{Content} 203 | @return: True if unbounded, else False 204 | @rtype: boolean 205 | '""" 206 | return False 207 | 208 | def nillable(self, content): 209 | """ 210 | Get whether the object is nillable. 211 | @param content: The current content being unmarshalled. 212 | @type content: L{Content} 213 | @return: True if nillable, else False 214 | @rtype: boolean 215 | '""" 216 | return False -------------------------------------------------------------------------------- /suds/umx/encoded.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides soap encoded unmarshaller classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.umx import * 24 | from suds.umx.typed import Typed 25 | from suds.sax import splitPrefix, Namespace 26 | 27 | log = getLogger(__name__) 28 | 29 | # 30 | # Add encoded extensions 31 | # aty = The soap (section 5) encoded array type. 32 | # 33 | Content.extensions.append('aty') 34 | 35 | 36 | class Encoded(Typed): 37 | """ 38 | A SOAP section (5) encoding unmarshaller. 39 | This marshaller supports rpc/encoded soap styles. 40 | """ 41 | 42 | def start(self, content): 43 | # 44 | # Grab the array type and continue 45 | # 46 | self.setaty(content) 47 | Typed.start(self, content) 48 | 49 | def end(self, content): 50 | # 51 | # Squash soap encoded arrays into python lists. This is 52 | # also where we insure that empty arrays are represented 53 | # as empty python lists. 54 | # 55 | aty = content.aty 56 | if aty is not None: 57 | self.promote(content) 58 | return Typed.end(self, content) 59 | 60 | def postprocess(self, content): 61 | # 62 | # Ensure proper rendering of empty arrays. 63 | # 64 | if content.aty is None: 65 | return Typed.postprocess(self, content) 66 | else: 67 | return content.data 68 | 69 | def setaty(self, content): 70 | """ 71 | Grab the (aty) soap-enc:arrayType and attach it to the 72 | content for proper array processing later in end(). 73 | @param content: The current content being unmarshalled. 74 | @type content: L{Content} 75 | @return: self 76 | @rtype: L{Encoded} 77 | """ 78 | name = 'arrayType' 79 | ns = (None, 'http://schemas.xmlsoap.org/soap/encoding/') 80 | aty = content.node.get(name, ns) 81 | if aty is not None: 82 | content.aty = aty 83 | parts = aty.split('[') 84 | ref = parts[0] 85 | if len(parts) == 2: 86 | self.applyaty(content, ref) 87 | else: 88 | pass # (2) dimensional array 89 | return self 90 | 91 | def applyaty(self, content, xty): 92 | """ 93 | Apply the type referenced in the I{arrayType} to the content 94 | (child nodes) of the array. Each element (node) in the array 95 | that does not have an explicit xsi:type attribute is given one 96 | based on the I{arrayType}. 97 | @param content: An array content. 98 | @type content: L{Content} 99 | @param xty: The XSI type reference. 100 | @type xty: str 101 | @return: self 102 | @rtype: L{Encoded} 103 | """ 104 | name = 'type' 105 | ns = Namespace.xsins 106 | parent = content.node 107 | for child in parent.getChildren(): 108 | ref = child.get(name, ns) 109 | if ref is None: 110 | parent.addPrefix(ns[0], ns[1]) 111 | attr = ':'.join((ns[0], name)) 112 | child.set(attr, xty) 113 | return self 114 | 115 | def promote(self, content): 116 | """ 117 | Promote (replace) the content.data with the first attribute 118 | of the current content.data that is a I{list}. Note: the 119 | content.data may be empty or contain only _x attributes. 120 | In either case, the content.data is assigned an empty list. 121 | @param content: An array content. 122 | @type content: L{Content} 123 | """ 124 | for n,v in content.data: 125 | if isinstance(v, list): 126 | content.data = v 127 | return 128 | content.data = [] -------------------------------------------------------------------------------- /suds/umx/typed.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | Provides typed unmarshaller classes. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.umx import * 24 | from suds.umx.core import Core 25 | from suds.resolver import NodeResolver, Frame 26 | from suds.sudsobject import Factory 27 | 28 | log = getLogger(__name__) 29 | 30 | 31 | # 32 | # Add typed extensions 33 | # type = The expected xsd type 34 | # real = The 'true' XSD type 35 | # 36 | Content.extensions.append('type') 37 | Content.extensions.append('real') 38 | 39 | 40 | class Typed(Core): 41 | """ 42 | A I{typed} XML unmarshaller 43 | @ivar resolver: A schema type resolver. 44 | @type resolver: L{NodeResolver} 45 | """ 46 | 47 | def __init__(self, schema): 48 | """ 49 | @param schema: A schema object. 50 | @type schema: L{xsd.schema.Schema} 51 | """ 52 | self.resolver = NodeResolver(schema) 53 | 54 | def process(self, node, type): 55 | """ 56 | Process an object graph representation of the xml L{node}. 57 | @param node: An XML tree. 58 | @type node: L{sax.element.Element} 59 | @param type: The I{optional} schema type. 60 | @type type: L{xsd.sxbase.SchemaObject} 61 | @return: A suds object. 62 | @rtype: L{Object} 63 | """ 64 | content = Content(node) 65 | content.type = type 66 | return Core.process(self, content) 67 | 68 | def reset(self): 69 | log.debug('reset') 70 | self.resolver.reset() 71 | 72 | def start(self, content): 73 | # 74 | # Resolve to the schema type; build an object and setup metadata. 75 | # 76 | if content.type is None: 77 | found = self.resolver.find(content.node) 78 | if found is None: 79 | log.error(self.resolver.schema) 80 | raise TypeNotFound(content.node.qname()) 81 | content.type = found 82 | else: 83 | known = self.resolver.known(content.node) 84 | frame = Frame(content.type, resolved=known) 85 | self.resolver.push(frame) 86 | real = self.resolver.top().resolved 87 | content.real = real 88 | cls_name = real.name 89 | if cls_name is None: 90 | cls_name = content.node.name 91 | content.data = Factory.object(cls_name) 92 | md = content.data.__metadata__ 93 | md.sxtype = real 94 | 95 | def end(self, content): 96 | self.resolver.pop() 97 | 98 | def unbounded(self, content): 99 | return content.type.unbounded() 100 | 101 | def nillable(self, content): 102 | resolved = content.type.resolve() 103 | return ( content.type.nillable or \ 104 | (resolved.builtin() and resolved.nillable ) ) 105 | 106 | def append_attribute(self, name, value, content): 107 | """ 108 | Append an attribute name/value into L{Content.data}. 109 | @param name: The attribute name 110 | @type name: basestring 111 | @param value: The attribute's value 112 | @type value: basestring 113 | @param content: The current content being unmarshalled. 114 | @type content: L{Content} 115 | """ 116 | type = self.resolver.findattr(name) 117 | if type is None: 118 | log.warn('attribute (%s) type, not-found', name) 119 | else: 120 | value = self.translated(value, type) 121 | Core.append_attribute(self, name, value, content) 122 | 123 | def append_text(self, content): 124 | """ 125 | Append text nodes into L{Content.data} 126 | Here is where the I{true} type is used to translate the value 127 | into the proper python type. 128 | @param content: The current content being unmarshalled. 129 | @type content: L{Content} 130 | """ 131 | Core.append_text(self, content) 132 | known = self.resolver.top().resolved 133 | content.text = self.translated(content.text, known) 134 | 135 | def translated(self, value, type): 136 | """ translate using the schema type """ 137 | if value is not None: 138 | resolved = type.resolve() 139 | return resolved.translate(value) 140 | else: 141 | return value -------------------------------------------------------------------------------- /suds/wsse.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{wsse} module provides WS-Security. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.sudsobject import Object 24 | from suds.sax.element import Element 25 | from suds.sax.date import UTC 26 | from datetime import datetime, timedelta 27 | 28 | try: 29 | from hashlib import md5 30 | except ImportError: 31 | # Python 2.4 compatibility 32 | from md5 import md5 33 | 34 | 35 | dsns = \ 36 | ('ds', 37 | 'http://www.w3.org/2000/09/xmldsig#') 38 | wssens = \ 39 | ('wsse', 40 | 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd') 41 | wsuns = \ 42 | ('wsu', 43 | 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd') 44 | wsencns = \ 45 | ('wsenc', 46 | 'http://www.w3.org/2001/04/xmlenc#') 47 | 48 | 49 | class Security(Object): 50 | """ 51 | WS-Security object. 52 | @ivar tokens: A list of security tokens 53 | @type tokens: [L{Token},...] 54 | @ivar signatures: A list of signatures. 55 | @type signatures: TBD 56 | @ivar references: A list of references. 57 | @type references: TBD 58 | @ivar keys: A list of encryption keys. 59 | @type keys: TBD 60 | """ 61 | 62 | def __init__(self): 63 | """ """ 64 | Object.__init__(self) 65 | self.mustUnderstand = True 66 | self.tokens = [] 67 | self.signatures = [] 68 | self.references = [] 69 | self.keys = [] 70 | 71 | def xml(self): 72 | """ 73 | Get xml representation of the object. 74 | @return: The root node. 75 | @rtype: L{Element} 76 | """ 77 | root = Element('Security', ns=wssens) 78 | root.set('mustUnderstand', str(self.mustUnderstand).lower()) 79 | for t in self.tokens: 80 | root.append(t.xml()) 81 | return root 82 | 83 | 84 | class Token(Object): 85 | """ I{Abstract} security token. """ 86 | 87 | @classmethod 88 | def now(cls): 89 | return datetime.now() 90 | 91 | @classmethod 92 | def utc(cls): 93 | return datetime.utcnow() 94 | 95 | @classmethod 96 | def sysdate(cls): 97 | utc = UTC() 98 | return str(utc) 99 | 100 | def __init__(self): 101 | Object.__init__(self) 102 | 103 | 104 | class UsernameToken(Token): 105 | """ 106 | Represents a basic I{UsernameToken} WS-Secuirty token. 107 | @ivar username: A username. 108 | @type username: str 109 | @ivar password: A password. 110 | @type password: str 111 | @ivar nonce: A set of bytes to prevent reply attacks. 112 | @type nonce: str 113 | @ivar created: The token created. 114 | @type created: L{datetime} 115 | """ 116 | 117 | def __init__(self, username=None, password=None): 118 | """ 119 | @param username: A username. 120 | @type username: str 121 | @param password: A password. 122 | @type password: str 123 | """ 124 | Token.__init__(self) 125 | self.username = username 126 | self.password = password 127 | self.nonce = None 128 | self.created = None 129 | 130 | def setnonce(self, text=None): 131 | """ 132 | Set I{nonce} which is arbitraty set of bytes to prevent 133 | reply attacks. 134 | @param text: The nonce text value. 135 | Generated when I{None}. 136 | @type text: str 137 | """ 138 | if text is None: 139 | s = [] 140 | s.append(self.username) 141 | s.append(self.password) 142 | s.append(Token.sysdate()) 143 | m = md5() 144 | m.update(':'.join(s)) 145 | self.nonce = m.hexdigest() 146 | else: 147 | self.nonce = text 148 | 149 | def setcreated(self, dt=None): 150 | """ 151 | Set I{created}. 152 | @param dt: The created date & time. 153 | Set as datetime.utc() when I{None}. 154 | @type dt: L{datetime} 155 | """ 156 | if dt is None: 157 | self.created = Token.utc() 158 | else: 159 | self.created = dt 160 | 161 | 162 | def xml(self): 163 | """ 164 | Get xml representation of the object. 165 | @return: The root node. 166 | @rtype: L{Element} 167 | """ 168 | root = Element('UsernameToken', ns=wssens) 169 | u = Element('Username', ns=wssens) 170 | u.setText(self.username) 171 | root.append(u) 172 | p = Element('Password', ns=wssens) 173 | p.setText(self.password) 174 | root.append(p) 175 | if self.nonce is not None: 176 | n = Element('Nonce', ns=wssens) 177 | n.setText(self.nonce) 178 | root.append(n) 179 | if self.created is not None: 180 | n = Element('Created', ns=wsuns) 181 | n.setText(str(UTC(self.created))) 182 | root.append(n) 183 | return root 184 | 185 | 186 | class Timestamp(Token): 187 | """ 188 | Represents the I{Timestamp} WS-Secuirty token. 189 | @ivar created: The token created. 190 | @type created: L{datetime} 191 | @ivar expires: The token expires. 192 | @type expires: L{datetime} 193 | """ 194 | 195 | def __init__(self, validity=90): 196 | """ 197 | @param validity: The time in seconds. 198 | @type validity: int 199 | """ 200 | Token.__init__(self) 201 | self.created = Token.utc() 202 | self.expires = self.created + timedelta(seconds=validity) 203 | 204 | def xml(self): 205 | root = Element("Timestamp", ns=wsuns) 206 | created = Element('Created', ns=wsuns) 207 | created.setText(str(UTC(self.created))) 208 | expires = Element('Expires', ns=wsuns) 209 | expires.setText(str(UTC(self.expires))) 210 | root.append(created) 211 | root.append(expires) 212 | return root -------------------------------------------------------------------------------- /suds/xsd/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{schema} module provides a intelligent representation of 19 | an XSD schema. The I{raw} model is the XML tree and the I{model} 20 | is the denormalized, objectified and intelligent view of the schema. 21 | Most of the I{value-add} provided by the model is centered around 22 | tranparent referenced type resolution and targeted denormalization. 23 | """ 24 | 25 | from logging import getLogger 26 | from suds import * 27 | from suds.sax import Namespace, splitPrefix 28 | 29 | log = getLogger(__name__) 30 | 31 | 32 | def qualify(ref, resolvers, defns=Namespace.default): 33 | """ 34 | Get a reference that is I{qualified} by namespace. 35 | @param ref: A referenced schema type name. 36 | @type ref: str 37 | @param resolvers: A list of objects to be used to resolve types. 38 | @type resolvers: [L{sax.element.Element},] 39 | @param defns: An optional target namespace used to qualify references 40 | when no prefix is specified. 41 | @type defns: A default namespace I{tuple: (prefix,uri)} used when ref not prefixed. 42 | @return: A qualified reference. 43 | @rtype: (name, namespace-uri) 44 | """ 45 | ns = None 46 | p, n = splitPrefix(ref) 47 | if p is not None: 48 | if not isinstance(resolvers, (list, tuple)): 49 | resolvers = (resolvers,) 50 | for r in resolvers: 51 | resolved = r.resolvePrefix(p) 52 | if resolved[1] is not None: 53 | ns = resolved 54 | break 55 | if ns is None: 56 | raise Exception('prefix (%s) not resolved' % p) 57 | else: 58 | ns = defns 59 | return (n, ns[1]) 60 | 61 | def isqref(object): 62 | """ 63 | Get whether the object is a I{qualified reference}. 64 | @param object: An object to be tested. 65 | @type object: I{any} 66 | @rtype: boolean 67 | @see: L{qualify} 68 | """ 69 | return (\ 70 | isinstance(object, tuple) and \ 71 | len(object) == 2 and \ 72 | isinstance(object[0], basestring) and \ 73 | isinstance(object[1], basestring)) 74 | 75 | 76 | class Filter: 77 | def __init__(self, inclusive=False, *items): 78 | self.inclusive = inclusive 79 | self.items = items 80 | def __contains__(self, x): 81 | if self.inclusive: 82 | result = ( x in self.items ) 83 | else: 84 | result = ( x not in self.items ) 85 | return result 86 | 87 | -------------------------------------------------------------------------------- /suds/xsd/deplist.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{depsolve} module defines a class for performing dependancy solving. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | 24 | log = getLogger(__name__) 25 | 26 | 27 | class DepList: 28 | """ 29 | Dependancy solving list. 30 | Items are tuples: (object, (deps,)) 31 | @ivar raw: The raw (unsorted) items. 32 | @type raw: list 33 | @ivar index: The index of (unsorted) items. 34 | @type index: list 35 | @ivar stack: The sorting stack. 36 | @type stack: list 37 | @ivar pushed: The I{pushed} set tracks items that have been 38 | processed. 39 | @type pushed: set 40 | @ivar sorted: The sorted list of items. 41 | @type sorted: list 42 | """ 43 | 44 | def __init__(self): 45 | """ """ 46 | self.unsorted = [] 47 | self.index = {} 48 | self.stack = [] 49 | self.pushed = set() 50 | self.sorted = None 51 | 52 | def add(self, *items): 53 | """ 54 | Add items to be sorted. 55 | @param items: One or more items to be added. 56 | @type items: I{item} 57 | @return: self 58 | @rtype: L{DepList} 59 | """ 60 | for item in items: 61 | self.unsorted.append(item) 62 | key = item[0] 63 | self.index[key] = item 64 | return self 65 | 66 | def sort(self): 67 | """ 68 | Sort the list based on dependancies. 69 | @return: The sorted items. 70 | @rtype: list 71 | """ 72 | self.sorted = list() 73 | self.pushed = set() 74 | for item in self.unsorted: 75 | popped = [] 76 | self.push(item) 77 | while len(self.stack): 78 | try: 79 | top = self.top() 80 | ref = top[1].next() 81 | refd = self.index.get(ref) 82 | if refd is None: 83 | log.debug('"%s" not found, skipped', Repr(ref)) 84 | continue 85 | self.push(refd) 86 | except StopIteration: 87 | popped.append(self.pop()) 88 | continue 89 | for p in popped: 90 | self.sorted.append(p) 91 | self.unsorted = self.sorted 92 | return self.sorted 93 | 94 | def top(self): 95 | """ 96 | Get the item at the top of the stack. 97 | @return: The top item. 98 | @rtype: (item, iter) 99 | """ 100 | return self.stack[-1] 101 | 102 | def push(self, item): 103 | """ 104 | Push and item onto the sorting stack. 105 | @param item: An item to push. 106 | @type item: I{item} 107 | @return: The number of items pushed. 108 | @rtype: int 109 | """ 110 | if item in self.pushed: 111 | return 112 | frame = (item, iter(item[1])) 113 | self.stack.append(frame) 114 | self.pushed.add(item) 115 | 116 | def pop(self): 117 | """ 118 | Pop the top item off the stack and append 119 | it to the sorted list. 120 | @return: The popped item. 121 | @rtype: I{item} 122 | """ 123 | try: 124 | frame = self.stack.pop() 125 | return frame[0] 126 | except: 127 | pass 128 | 129 | 130 | if __name__ == '__main__': 131 | a = ('a', ('x',)) 132 | b = ('b', ('a',)) 133 | c = ('c', ('a','b')) 134 | d = ('d', ('c',)) 135 | e = ('e', ('d','a')) 136 | f = ('f', ('e','c','d','a')) 137 | x = ('x', ()) 138 | L = DepList() 139 | L.add(c, e, d, b, f, a, x) 140 | print [x[0] for x in L.sort()] -------------------------------------------------------------------------------- /suds/xsd/doctor.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{doctor} module provides classes for fixing broken (sick) 19 | schema(s). 20 | """ 21 | 22 | from logging import getLogger 23 | from suds.sax import splitPrefix, Namespace 24 | from suds.sax.element import Element 25 | 26 | log = getLogger(__name__) 27 | 28 | 29 | class Doctor: 30 | """ 31 | Schema Doctor. 32 | """ 33 | def examine(self, root): 34 | """ 35 | Examine and repair the schema (if necessary). 36 | @param root: A schema root element. 37 | @type root: L{Element} 38 | """ 39 | pass 40 | 41 | 42 | class Practice(Doctor): 43 | """ 44 | A collection of doctors. 45 | @ivar doctors: A list of doctors. 46 | @type doctors: list 47 | """ 48 | 49 | def __init__(self): 50 | self.doctors = [] 51 | 52 | def add(self, doctor): 53 | """ 54 | Add a doctor to the practice 55 | @param doctor: A doctor to add. 56 | @type doctor: L{Doctor} 57 | """ 58 | self.doctors.append(doctor) 59 | 60 | def examine(self, root): 61 | for d in self.doctors: 62 | d.examine(root) 63 | return root 64 | 65 | 66 | class TnsFilter: 67 | """ 68 | Target Namespace filter. 69 | @ivar tns: A list of target namespaces. 70 | @type tns: [str,...] 71 | """ 72 | 73 | def __init__(self, *tns): 74 | """ 75 | @param tns: A list of target namespaces. 76 | @type tns: [str,...] 77 | """ 78 | self.tns = [] 79 | self.add(*tns) 80 | 81 | def add(self, *tns): 82 | """ 83 | Add I{targetNamesapces} to be added. 84 | @param tns: A list of target namespaces. 85 | @type tns: [str,...] 86 | """ 87 | self.tns += tns 88 | 89 | def match(self, root, ns): 90 | """ 91 | Match by I{targetNamespace} excluding those that 92 | are equal to the specified namespace to prevent 93 | adding an import to itself. 94 | @param root: A schema root. 95 | @type root: L{Element} 96 | """ 97 | tns = root.get('targetNamespace') 98 | if len(self.tns): 99 | matched = ( tns in self.tns ) 100 | else: 101 | matched = 1 102 | itself = ( ns == tns ) 103 | return ( matched and not itself ) 104 | 105 | 106 | class Import: 107 | """ 108 | An to be applied. 109 | @cvar xsdns: The XSD namespace. 110 | @type xsdns: (p,u) 111 | @ivar ns: An import namespace. 112 | @type ns: str 113 | @ivar location: An optional I{schemaLocation}. 114 | @type location: str 115 | @ivar filter: A filter used to restrict application to 116 | a particular schema. 117 | @type filter: L{TnsFilter} 118 | """ 119 | 120 | xsdns = Namespace.xsdns 121 | 122 | def __init__(self, ns, location=None): 123 | """ 124 | @param ns: An import namespace. 125 | @type ns: str 126 | @param location: An optional I{schemaLocation}. 127 | @type location: str 128 | """ 129 | self.ns = ns 130 | self.location = location 131 | self.filter = TnsFilter() 132 | 133 | def setfilter(self, filter): 134 | """ 135 | Set the filter. 136 | @param filter: A filter to set. 137 | @type filter: L{TnsFilter} 138 | """ 139 | self.filter = filter 140 | 141 | def apply(self, root): 142 | """ 143 | Apply the import (rule) to the specified schema. 144 | If the schema does not already contain an import for the 145 | I{namespace} specified here, it is added. 146 | @param root: A schema root. 147 | @type root: L{Element} 148 | """ 149 | if not self.filter.match(root, self.ns): 150 | return 151 | if self.exists(root): 152 | return 153 | node = Element('import', ns=self.xsdns) 154 | node.set('namespace', self.ns) 155 | if self.location is not None: 156 | node.set('schemaLocation', self.location) 157 | log.debug('inserting: %s', node) 158 | root.insert(node) 159 | 160 | def add(self, root): 161 | """ 162 | Add an to the specified schema root. 163 | @param root: A schema root. 164 | @type root: L{Element} 165 | """ 166 | node = Element('import', ns=self.xsdns) 167 | node.set('namespace', self.ns) 168 | if self.location is not None: 169 | node.set('schemaLocation', self.location) 170 | log.debug('%s inserted', node) 171 | root.insert(node) 172 | 173 | def exists(self, root): 174 | """ 175 | Check to see if the already exists 176 | in the specified schema root by matching I{namesapce}. 177 | @param root: A schema root. 178 | @type root: L{Element} 179 | """ 180 | for node in root.children: 181 | if node.name != 'import': 182 | continue 183 | ns = node.get('namespace') 184 | if self.ns == ns: 185 | return 1 186 | return 0 187 | 188 | 189 | class ImportDoctor(Doctor): 190 | """ 191 | Doctor used to fix missing imports. 192 | @ivar imports: A list of imports to apply. 193 | @type imports: [L{Import},...] 194 | """ 195 | 196 | def __init__(self, *imports): 197 | """ 198 | """ 199 | self.imports = [] 200 | self.add(*imports) 201 | 202 | def add(self, *imports): 203 | """ 204 | Add a namesapce to be checked. 205 | @param imports: A list of L{Import} objects. 206 | @type imports: [L{Import},..] 207 | """ 208 | self.imports += imports 209 | 210 | def examine(self, root): 211 | for imp in self.imports: 212 | imp.apply(root) 213 | -------------------------------------------------------------------------------- /suds/xsd/query.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{query} module defines a class for performing schema queries. 19 | """ 20 | 21 | from logging import getLogger 22 | from suds import * 23 | from suds.sudsobject import * 24 | from suds.xsd import qualify, isqref 25 | from suds.xsd.sxbuiltin import Factory 26 | 27 | log = getLogger(__name__) 28 | 29 | 30 | class Query(Object): 31 | """ 32 | Schema query base class. 33 | """ 34 | 35 | def __init__(self, ref=None): 36 | """ 37 | @param ref: The schema reference being queried. 38 | @type ref: qref 39 | """ 40 | Object.__init__(self) 41 | self.id = objid(self) 42 | self.ref = ref 43 | self.history = [] 44 | self.resolved = False 45 | if not isqref(self.ref): 46 | raise Exception('%s, must be qref' % tostr(self.ref)) 47 | 48 | def execute(self, schema): 49 | """ 50 | Execute this query using the specified schema. 51 | @param schema: The schema associated with the query. The schema 52 | is used by the query to search for items. 53 | @type schema: L{schema.Schema} 54 | @return: The item matching the search criteria. 55 | @rtype: L{sxbase.SchemaObject} 56 | """ 57 | raise Exception, 'not-implemented by subclass' 58 | 59 | def filter(self, result): 60 | """ 61 | Filter the specified result based on query criteria. 62 | @param result: A potential result. 63 | @type result: L{sxbase.SchemaObject} 64 | @return: True if result should be excluded. 65 | @rtype: boolean 66 | """ 67 | if result is None: 68 | return True 69 | reject = ( result in self.history ) 70 | if reject: 71 | log.debug('result %s, rejected by\n%s', Repr(result), self) 72 | return reject 73 | 74 | def result(self, result): 75 | """ 76 | Query result post processing. 77 | @param result: A query result. 78 | @type result: L{sxbase.SchemaObject} 79 | """ 80 | if result is None: 81 | log.debug('%s, not-found', self.ref) 82 | return 83 | if self.resolved: 84 | result = result.resolve() 85 | log.debug('%s, found as: %s', self.ref, Repr(result)) 86 | self.history.append(result) 87 | return result 88 | 89 | 90 | class BlindQuery(Query): 91 | """ 92 | Schema query class that I{blindly} searches for a reference in 93 | the specified schema. It may be used to find Elements and Types but 94 | will match on an Element first. This query will also find builtins. 95 | """ 96 | 97 | def execute(self, schema): 98 | if schema.builtin(self.ref): 99 | name = self.ref[0] 100 | b = Factory.create(schema, name) 101 | log.debug('%s, found builtin (%s)', self.id, name) 102 | return b 103 | result = None 104 | for d in (schema.elements, schema.types): 105 | result = d.get(self.ref) 106 | if self.filter(result): 107 | result = None 108 | else: 109 | break 110 | if result is None: 111 | eq = ElementQuery(self.ref) 112 | eq.history = self.history 113 | result = eq.execute(schema) 114 | return self.result(result) 115 | 116 | 117 | class TypeQuery(Query): 118 | """ 119 | Schema query class that searches for Type references in 120 | the specified schema. Matches on root types only. 121 | """ 122 | 123 | def execute(self, schema): 124 | if schema.builtin(self.ref): 125 | name = self.ref[0] 126 | b = Factory.create(schema, name) 127 | log.debug('%s, found builtin (%s)', self.id, name) 128 | return b 129 | result = schema.types.get(self.ref) 130 | if self.filter(result): 131 | result = None 132 | return self.result(result) 133 | 134 | 135 | class GroupQuery(Query): 136 | """ 137 | Schema query class that searches for Group references in 138 | the specified schema. 139 | """ 140 | 141 | def execute(self, schema): 142 | result = schema.groups.get(self.ref) 143 | if self.filter(result): 144 | result = None 145 | return self.result(result) 146 | 147 | 148 | class AttrQuery(Query): 149 | """ 150 | Schema query class that searches for Attribute references in 151 | the specified schema. Matches on root Attribute by qname first, then searches 152 | deep into the document. 153 | """ 154 | 155 | def execute(self, schema): 156 | result = schema.attributes.get(self.ref) 157 | if self.filter(result): 158 | result = self.__deepsearch(schema) 159 | return self.result(result) 160 | 161 | def __deepsearch(self, schema): 162 | from suds.xsd.sxbasic import Attribute 163 | result = None 164 | for e in schema.all: 165 | result = e.find(self.ref, (Attribute,)) 166 | if self.filter(result): 167 | result = None 168 | else: 169 | break 170 | return result 171 | 172 | 173 | class AttrGroupQuery(Query): 174 | """ 175 | Schema query class that searches for attributeGroup references in 176 | the specified schema. 177 | """ 178 | 179 | def execute(self, schema): 180 | result = schema.agrps.get(self.ref) 181 | if self.filter(result): 182 | result = None 183 | return self.result(result) 184 | 185 | 186 | class ElementQuery(Query): 187 | """ 188 | Schema query class that searches for Element references in 189 | the specified schema. Matches on root Elements by qname first, then searches 190 | deep into the document. 191 | """ 192 | 193 | def execute(self, schema): 194 | result = schema.elements.get(self.ref) 195 | if self.filter(result): 196 | result = self.__deepsearch(schema) 197 | return self.result(result) 198 | 199 | def __deepsearch(self, schema): 200 | from suds.xsd.sxbasic import Element 201 | result = None 202 | for e in schema.all: 203 | result = e.find(self.ref, (Element,)) 204 | if self.filter(result): 205 | result = None 206 | else: 207 | break 208 | return result -------------------------------------------------------------------------------- /suds/xsd/sxbuiltin.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | """ 18 | The I{sxbuiltin} module provides classes that represent 19 | XSD I{builtin} schema objects. 20 | """ 21 | 22 | from logging import getLogger 23 | from suds import * 24 | from suds.xsd import * 25 | from suds.sax.date import * 26 | from suds.xsd.sxbase import XBuiltin 27 | import datetime as dt 28 | 29 | 30 | log = getLogger(__name__) 31 | 32 | 33 | class XString(XBuiltin): 34 | """ 35 | Represents an (xsd) node 36 | """ 37 | pass 38 | 39 | 40 | class XAny(XBuiltin): 41 | """ 42 | Represents an (xsd) node 43 | """ 44 | 45 | def __init__(self, schema, name): 46 | XBuiltin.__init__(self, schema, name) 47 | self.nillable = False 48 | 49 | def get_child(self, name): 50 | child = XAny(self.schema, name) 51 | return (child, []) 52 | 53 | def any(self): 54 | return True 55 | 56 | 57 | class XBoolean(XBuiltin): 58 | """ 59 | Represents an (xsd) boolean builtin type. 60 | """ 61 | 62 | translation = ( 63 | { '1':True,'true':True,'0':False,'false':False }, 64 | { True:'true',1:'true',False:'false',0:'false' }, 65 | ) 66 | 67 | def translate(self, value, topython=True): 68 | if topython: 69 | if isinstance(value, basestring): 70 | return XBoolean.translation[0].get(value) 71 | else: 72 | return None 73 | else: 74 | if isinstance(value, (bool,int)): 75 | return XBoolean.translation[1].get(value) 76 | else: 77 | return value 78 | 79 | 80 | class XInteger(XBuiltin): 81 | """ 82 | Represents an (xsd) xs:int builtin type. 83 | """ 84 | 85 | def translate(self, value, topython=True): 86 | if topython: 87 | if isinstance(value, basestring) and len(value): 88 | return int(value) 89 | else: 90 | return None 91 | else: 92 | if isinstance(value, int): 93 | return str(value) 94 | else: 95 | return value 96 | 97 | class XLong(XBuiltin): 98 | """ 99 | Represents an (xsd) xs:long builtin type. 100 | """ 101 | 102 | def translate(self, value, topython=True): 103 | if topython: 104 | if isinstance(value, basestring) and len(value): 105 | return long(value) 106 | else: 107 | return None 108 | else: 109 | if isinstance(value, (int,long)): 110 | return str(value) 111 | else: 112 | return value 113 | 114 | 115 | class XFloat(XBuiltin): 116 | """ 117 | Represents an (xsd) xs:float builtin type. 118 | """ 119 | 120 | def translate(self, value, topython=True): 121 | if topython: 122 | if isinstance(value, basestring) and len(value): 123 | return float(value) 124 | else: 125 | return None 126 | else: 127 | if isinstance(value, float): 128 | return str(value) 129 | else: 130 | return value 131 | 132 | 133 | class XDate(XBuiltin): 134 | """ 135 | Represents an (xsd) xs:date builtin type. 136 | """ 137 | 138 | def translate(self, value, topython=True): 139 | if topython: 140 | if isinstance(value, basestring) and len(value): 141 | return Date(value).date 142 | else: 143 | return None 144 | else: 145 | if isinstance(value, dt.date): 146 | return str(Date(value)) 147 | else: 148 | return value 149 | 150 | 151 | class XTime(XBuiltin): 152 | """ 153 | Represents an (xsd) xs:time builtin type. 154 | """ 155 | 156 | def translate(self, value, topython=True): 157 | if topython: 158 | if isinstance(value, basestring) and len(value): 159 | return Time(value).time 160 | else: 161 | return None 162 | else: 163 | if isinstance(value, dt.date): 164 | return str(Time(value)) 165 | else: 166 | return value 167 | 168 | 169 | class XDateTime(XBuiltin): 170 | """ 171 | Represents an (xsd) xs:datetime builtin type. 172 | """ 173 | 174 | def translate(self, value, topython=True): 175 | if topython: 176 | if isinstance(value, basestring) and len(value): 177 | return DateTime(value).datetime 178 | else: 179 | return None 180 | else: 181 | if isinstance(value, dt.date): 182 | return str(DateTime(value)) 183 | else: 184 | return value 185 | 186 | 187 | class Factory: 188 | 189 | tags =\ 190 | { 191 | # any 192 | 'anyType' : XAny, 193 | # strings 194 | 'string' : XString, 195 | 'normalizedString' : XString, 196 | 'ID' : XString, 197 | 'Name' : XString, 198 | 'QName' : XString, 199 | 'NCName' : XString, 200 | 'anySimpleType' : XString, 201 | 'anyURI' : XString, 202 | 'NOTATION' : XString, 203 | 'token' : XString, 204 | 'language' : XString, 205 | 'IDREFS' : XString, 206 | 'ENTITIES' : XString, 207 | 'IDREF' : XString, 208 | 'ENTITY' : XString, 209 | 'NMTOKEN' : XString, 210 | 'NMTOKENS' : XString, 211 | # binary 212 | 'hexBinary' : XString, 213 | 'base64Binary' : XString, 214 | # integers 215 | 'int' : XInteger, 216 | 'integer' : XInteger, 217 | 'unsignedInt' : XInteger, 218 | 'positiveInteger' : XInteger, 219 | 'negativeInteger' : XInteger, 220 | 'nonPositiveInteger' : XInteger, 221 | 'nonNegativeInteger' : XInteger, 222 | # longs 223 | 'long' : XLong, 224 | 'unsignedLong' : XLong, 225 | # shorts 226 | 'short' : XInteger, 227 | 'unsignedShort' : XInteger, 228 | 'byte' : XInteger, 229 | 'unsignedByte' : XInteger, 230 | # floats 231 | 'float' : XFloat, 232 | 'double' : XFloat, 233 | 'decimal' : XFloat, 234 | # dates & times 235 | 'date' : XDate, 236 | 'time' : XTime, 237 | 'dateTime': XDateTime, 238 | 'duration': XString, 239 | 'gYearMonth' : XString, 240 | 'gYear' : XString, 241 | 'gMonthDay' : XString, 242 | 'gDay' : XString, 243 | 'gMonth' : XString, 244 | # boolean 245 | 'boolean' : XBoolean, 246 | } 247 | 248 | @classmethod 249 | def maptag(cls, tag, fn): 250 | """ 251 | Map (override) tag => I{class} mapping. 252 | @param tag: An xsd tag name. 253 | @type tag: str 254 | @param fn: A function or class. 255 | @type fn: fn|class. 256 | """ 257 | cls.tags[tag] = fn 258 | 259 | @classmethod 260 | def create(cls, schema, name): 261 | """ 262 | Create an object based on the root tag name. 263 | @param schema: A schema object. 264 | @type schema: L{schema.Schema} 265 | @param name: The name. 266 | @type name: str 267 | @return: The created object. 268 | @rtype: L{XBuiltin} 269 | """ 270 | fn = cls.tags.get(name) 271 | if fn is not None: 272 | return fn(schema, name) 273 | else: 274 | return XBuiltin(schema, name) 275 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | import sys 18 | import logging 19 | 20 | def setup_logging(): 21 | if sys.version_info < (2, 5): 22 | fmt = '%(asctime)s [%(levelname)s] @%(filename)s:%(lineno)d\n%(message)s\n' 23 | else: 24 | fmt = '%(asctime)s [%(levelname)s] %(funcName)s() @%(filename)s:%(lineno)d\n%(message)s\n' 25 | logging.basicConfig(level=logging.INFO, format=fmt) -------------------------------------------------------------------------------- /tests/axis1.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | # 18 | # This test requires installation or visability to my local axis(1) server. 19 | # 20 | 21 | import sys 22 | sys.path.append('../') 23 | 24 | import logging 25 | import traceback as tb 26 | import suds.metrics as metrics 27 | from tests import * 28 | from suds import WebFault 29 | from suds.client import Client 30 | from suds.sudsobject import Object 31 | from suds.transport.https import HttpAuthenticated 32 | 33 | errors = 0 34 | 35 | credentials = dict(username='jortel', password='abc123') 36 | 37 | setup_logging() 38 | 39 | 40 | #logging.getLogger('suds.client').setLevel(logging.DEBUG) 41 | 42 | def start(url): 43 | global errors 44 | print '\n________________________________________________________________\n' 45 | print 'Test @ ( %s )\nerrors = %d\n' % (url, errors) 46 | 47 | try: 48 | url = 'http://localhost:8081/axis/services/basic-rpc-encoded?wsdl' 49 | start(url) 50 | t = HttpAuthenticated(**credentials) 51 | client = Client(url, transport=t, cache=None) 52 | print client 53 | # 54 | # create a name object using the wsdl 55 | # 56 | print 'create name' 57 | name = client.factory.create('ns0:Name') 58 | name.first = u'jeff'+unichr(1234) 59 | name.last = 'ortel' 60 | print name 61 | # 62 | # create a phone object using the wsdl 63 | # 64 | print 'create phone' 65 | phoneA = client.factory.create('ns0:Phone') 66 | phoneA.npa = 410 67 | phoneA.nxx = 555 68 | phoneA.number = 5138 69 | phoneB = client.factory.create('ns0:Phone') 70 | phoneB.npa = 919 71 | phoneB.nxx = 555 72 | phoneB.number = 4406 73 | phoneC = { 74 | 'npa':205, 75 | 'nxx':777, 76 | 'number':1212 77 | } 78 | # 79 | # create a dog 80 | # 81 | dog = client.factory.create('ns0:Dog') 82 | dog.name = 'Chance' 83 | dog.trained = True 84 | # 85 | # create a person object using the wsdl 86 | # 87 | person = client.factory.create('ns0:Person') 88 | print '{empty} person=\n%s' % person 89 | person.name = name 90 | person.age = 43 91 | person.phone = [phoneA,phoneB,phoneC] 92 | person.pets = [dog] 93 | print 'person=\n%s' % person 94 | # 95 | # add the person (using the webservice) 96 | # 97 | print 'addPersion()' 98 | result = client.service.addPerson(person) 99 | print '\nreply(\n%s\n)\n' % str(result) 100 | # 101 | # create a new name object used to update the person 102 | # 103 | newname = client.factory.create('ns0:Name') 104 | newname.first = 'Todd' 105 | newname.last = None 106 | # 107 | # create AnotherPerson using Person 108 | # 109 | ap = client.factory.create('ns0:AnotherPerson') 110 | ap.name = person.name 111 | ap.age = person.age 112 | ap.phone = person.phone 113 | ap.pets = person.pets 114 | print 'AnotherPerson\n%s' % ap 115 | # 116 | # update the person's name (using the webservice) 117 | # 118 | print 'updatePersion()' 119 | result = client.service.updatePerson(ap, newname) 120 | print '\nreply(\n%s\n)\n' % str(result) 121 | result = client.service.updatePerson(ap, None) 122 | print '\nreply(\n%s\n)\n' % str(result) 123 | except WebFault, f: 124 | errors += 1 125 | print f 126 | print f.fault 127 | except Exception, e: 128 | errors += 1 129 | print e 130 | tb.print_exc() 131 | 132 | try: 133 | url = 'http://localhost:8081/axis/services/basic-rpc-encoded?wsdl' 134 | start(url) 135 | t = HttpAuthenticated(**credentials) 136 | client = Client(url, transport=t, cache=None) 137 | print client 138 | # 139 | # create a name object as dict 140 | # 141 | print 'create name' 142 | name = {} 143 | name['first'] = 'Elmer' 144 | name['last'] = 'Fudd' 145 | print name 146 | # 147 | # create a phone as dict 148 | # 149 | print 'create phone' 150 | phoneA = {} 151 | phoneA['npa'] = 410 152 | phoneA['nxx'] = 555 153 | phoneA['number'] = 5138 154 | phoneB = {} 155 | phoneB['npa'] = 919 156 | phoneB['nxx'] = 555 157 | phoneB['number'] = 4406 158 | phoneC = { 159 | 'npa':205, 160 | 'nxx':777, 161 | 'number':1212 162 | } 163 | # 164 | # create a dog 165 | # 166 | dog = { 167 | 'name':'Chance', 168 | 'trained':True, 169 | } 170 | # 171 | # create a person as dict 172 | # 173 | person = {} 174 | print '{empty} person=\n%s' % person 175 | person['name'] = name 176 | person['age'] = 43 177 | person['phone'] = [phoneA,phoneB, phoneC] 178 | person['pets'] = [dog] 179 | print 'person=\n%s' % person 180 | # 181 | # add the person (using the webservice) 182 | # 183 | print 'addPersion()' 184 | result = client.service.addPerson(person) 185 | print '\nreply(\n%s\n)\n' % str(result) 186 | except WebFault, f: 187 | errors += 1 188 | print f 189 | print f.fault 190 | except Exception, e: 191 | errors += 1 192 | print e 193 | tb.print_exc() 194 | 195 | try: 196 | print "echo(' this is cool ')" 197 | result = client.service.echo('this is cool') 198 | print '\nreply( "%s" )\n' % str(result) 199 | print 'echo(None)' 200 | result = client.service.echo(None) 201 | print '\nreply( "%s" )\n' % str(result) 202 | except WebFault, f: 203 | errors += 1 204 | print f 205 | print f.fault 206 | except Exception, e: 207 | errors += 1 208 | print e 209 | tb.print_exc() 210 | 211 | try: 212 | print 'hello()' 213 | result = client.service.hello() 214 | print '\nreply( %s )\n' % str(result) 215 | except WebFault, f: 216 | errors += 1 217 | print f 218 | print f.fault 219 | except Exception, e: 220 | errors += 1 221 | print e 222 | tb.print_exc() 223 | 224 | try: 225 | print 'testVoid()' 226 | result = client.service.getVoid() 227 | print '\nreply( %s )\n' % str(result) 228 | except WebFault, f: 229 | errors += 1 230 | print f 231 | print f.fault 232 | except Exception, e: 233 | errors += 1 234 | print e 235 | tb.print_exc() 236 | 237 | try: 238 | print '** new style arrays **' 239 | words = ['my', 'dog', 'likes', 'steak'] 240 | result = client.service.printList(words) 241 | print '\nreply( %s )\n' % str(result) 242 | 243 | print '** old style arrays **' 244 | array = client.factory.create('ArrayOf_xsd_string') 245 | print 'ArrayOf_xsd_string=\n%s' % array 246 | array.item = ['my', 'dog', 'likes', 'steak'] 247 | result = client.service.printList(array) 248 | print '\nreply( %s )\n' % str(result) 249 | except WebFault, f: 250 | errors += 1 251 | print f 252 | print f.fault 253 | except Exception, e: 254 | errors += 1 255 | print e 256 | tb.print_exc() 257 | 258 | try: 259 | s = 'hello' 260 | for n in range(0, 3): 261 | print 'getList(%s, %d)' % (s, n) 262 | result = client.service.getList(s, n) 263 | print '\nreply( %s )\n' % str(result) 264 | assert ( isinstance(result, list) and len(result) == n ) 265 | except WebFault, f: 266 | errors += 1 267 | print f 268 | print f.fault 269 | except Exception, e: 270 | errors += 1 271 | print e 272 | tb.print_exc() 273 | 274 | try: 275 | print 'testExceptions()' 276 | result = client.service.throwException() 277 | print '\nreply( %s )\n' % tostr(result) 278 | raise Exception('Fault expected and not raised') 279 | except WebFault, f: 280 | print f 281 | print f.fault 282 | except Exception, e: 283 | errors += 1 284 | print e 285 | tb.print_exc() 286 | 287 | try: 288 | url = 'http://localhost:8081/axis/services/basic-rpc-encoded?wsdl' 289 | start(url) 290 | client = Client(url, faults=False, **credentials) 291 | print 'testExceptions()' 292 | result = client.service.throwException() 293 | print '\nreply( %s )\n' % str(result) 294 | except WebFault, f: 295 | errors += 1 296 | print f 297 | print f.fault 298 | except Exception, e: 299 | errors += 1 300 | print e 301 | tb.print_exc() 302 | 303 | print '\nFinished: errors=%d' % errors 304 | -------------------------------------------------------------------------------- /tests/axis2.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | import sys 18 | sys.path.append('../') 19 | 20 | import logging 21 | import traceback as tb 22 | import suds.metrics as metrics 23 | from tests import * 24 | from suds import * 25 | from suds.client import Client 26 | from datetime import datetime 27 | 28 | errors = 0 29 | 30 | setup_logging() 31 | 32 | #logging.getLogger('suds.client').setLevel(logging.DEBUG) 33 | 34 | url = 'http://localhost:8080/axis2/services/BasicService?wsdl' 35 | 36 | print 'url=%s' % url 37 | 38 | # 39 | # create a service client using the wsdl. 40 | # 41 | client = Client(url) 42 | 43 | # 44 | # print the service (introspection) 45 | # 46 | print client 47 | 48 | print 'printList()' 49 | print client.service.printList(['a','b']) 50 | 51 | # 52 | # create a name object using the wsdl 53 | # 54 | print 'create name' 55 | name = client.factory.create('ns2:Name') 56 | name.first = u'jeff'+unichr(1234) 57 | name.last = 'ortel' 58 | 59 | print name 60 | 61 | # 62 | # create a phone object using the wsdl 63 | # 64 | print 'create phone' 65 | phoneA = client.factory.create('ns2:Phone') 66 | phoneA.npa = 410 67 | phoneA.nxx = 822 68 | phoneA.number = 5138 69 | 70 | phoneB = client.factory.create('ns2:Phone') 71 | phoneB.npa = 919 72 | phoneB.nxx = 606 73 | phoneB.number = 4406 74 | 75 | # 76 | # create a dog 77 | # 78 | dog = client.factory.create('ns2:Dog') 79 | print dog 80 | dog.name = 'Chance' 81 | dog.trained = True 82 | print dog 83 | 84 | # 85 | # create a person object using the wsdl 86 | # 87 | person = client.factory.create('ns2:Person') 88 | 89 | # 90 | # inspect empty person 91 | # 92 | print '{empty} person=\n%s' % person 93 | 94 | person.name = name 95 | person.age = None 96 | person.birthday = datetime.now() 97 | person.phone.append(phoneA) 98 | person.phone.append(phoneB) 99 | person.pets.append(dog) 100 | 101 | # 102 | # inspect person 103 | # 104 | print 'person=\n%s' % person 105 | 106 | # 107 | # add the person (using the webservice) 108 | # 109 | print 'addPersion()' 110 | result = client.service.addPerson(person) 111 | print '\nreply(\n%s\n)\n' % result.encode('utf-8') 112 | 113 | # 114 | # create a new name object used to update the person 115 | # 116 | newname = client.factory.create('ns2:Name') 117 | newname.first = 'Todd' 118 | newname.last = None 119 | 120 | # 121 | # update the person's name (using the webservice) and print return person object 122 | # 123 | print 'updatePersion()' 124 | result = client.service.updatePerson(person, newname) 125 | print '\nreply(\n%s\n)\n' % str(result) 126 | result = client.service.updatePerson(person, None) 127 | print '\nreply(\n%s\n)\n' % str(result) 128 | 129 | 130 | # 131 | # invoke the echo service 132 | # 133 | print 'echo()' 134 | client.service.echo(None) 135 | result = client.service.echo('this is cool') 136 | print '\nreply( %s )\n' % str(result) 137 | 138 | print 'echo() with {none}' 139 | result = client.service.echo(None) 140 | print '\nreply( %s )\n' % str(result) 141 | 142 | # 143 | # invoke the hello service 144 | # 145 | print 'hello()' 146 | result = client.service.hello() 147 | print '\nreply( %s )\n' % str(result) 148 | 149 | # 150 | # invoke the testVoid service 151 | # 152 | try: 153 | print 'getVoid()' 154 | result = client.service.getVoid() 155 | print '\nreply( %s )\n' % str(result) 156 | except Exception, e: 157 | print e 158 | 159 | # 160 | # test list args 161 | # 162 | print 'getList(list)' 163 | mylist = ['my', 'dog', 'likes', 'steak'] 164 | result = client.service.printList(mylist) 165 | print '\nreply( %s )\n' % str(result) 166 | # tuple 167 | print 'testListArgs(tuple)' 168 | mylist = ('my', 'dog', 'likes', 'steak') 169 | result = client.service.printList(mylist) 170 | print '\nreply( %s )\n' % str(result) 171 | 172 | # 173 | # test list returned 174 | # 175 | for n in range(0, 3): 176 | print 'getList(str, %d)' % n 177 | result = client.service.getList('hello', n) 178 | print '\nreply( %s )\n' % str(result) 179 | assert ( isinstance(result, list) and len(result) == n ) 180 | 181 | print 'addPet()' 182 | dog = client.factory.create('ns2:Dog') 183 | dog.name = 'Chance' 184 | dog.trained = True 185 | print dog 186 | try: 187 | result = client.service.addPet(person, dog) 188 | print '\nreply( %s )\n' % str(result) 189 | except Exception, e: 190 | print e 191 | 192 | print '___________________ E X C E P T I O N S __________________________' 193 | 194 | # 195 | # test exceptions 196 | # 197 | try: 198 | print 'throwException() faults=True' 199 | result = client.service.throwException() 200 | print '\nreply( %s )\n' % tostr(result) 201 | except Exception, e: 202 | print e 203 | 204 | # 205 | # test faults 206 | # 207 | try: 208 | print 'throwException() faults=False' 209 | client.set_options(faults=False) 210 | result = client.service.throwException() 211 | print '\nreply( %s )\n' % tostr(result) 212 | except Exception, e: 213 | print e 214 | 215 | print '\nfinished: errors=%d' % errors -------------------------------------------------------------------------------- /tests/jasper.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | import sys 18 | sys.path.append('../') 19 | 20 | import logging 21 | import traceback as tb 22 | import urllib2 23 | import suds.metrics as metrics 24 | import traceback as tb 25 | from tests import * 26 | from suds import WebFault 27 | from suds.client import Client 28 | 29 | errors = 0 30 | 31 | setup_logging() 32 | 33 | #logging.getLogger('suds.client').setLevel(logging.DEBUG) 34 | 35 | def start(url): 36 | print '\n________________________________________________________________\n' 37 | print 'Test @ ( %s )' % url 38 | 39 | try: 40 | url = 'http://localhost:9090/jasperserver-pro/services/repository?wsdl' 41 | start(url) 42 | client = Client(url, username='jeff', password='ortel') 43 | print client 44 | print client.service.list('') 45 | except WebFault, f: 46 | errors += 1 47 | print f 48 | print f.fault 49 | except Exception, e: 50 | errors += 1 51 | print e 52 | tb.print_exc() 53 | 54 | print '\nFinished: errors = %d' % errors 55 | -------------------------------------------------------------------------------- /tests/public.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | import sys 18 | sys.path.append('../') 19 | 20 | import logging 21 | import traceback as tb 22 | import suds.metrics as metrics 23 | from tests import * 24 | from suds import WebFault 25 | from suds.client import Client 26 | 27 | errors = 0 28 | 29 | setup_logging() 30 | 31 | #logging.getLogger('suds.client').setLevel(logging.DEBUG) 32 | #logging.getLogger('suds.metrics').setLevel(logging.DEBUG) 33 | #logging.getLogger('suds').setLevel(logging.DEBUG) 34 | 35 | 36 | def start(url): 37 | global errors 38 | print '\n________________________________________________________________\n' 39 | print 'Test @ ( %s ) %d' % (url, errors) 40 | 41 | try: 42 | url = 'http://mssoapinterop.org/asmx/simple.asmx?WSDL' 43 | start(url) 44 | client = Client(url) 45 | print client 46 | # string 47 | input = "42" 48 | d = dict(inputString=input) 49 | result = client.service.echoString(**d) 50 | print 'echoString() = %s' % result 51 | assert result == input 52 | # int 53 | input = 42 54 | result = client.service.echoInteger(input) 55 | print 'echoInteger() = %s' % result 56 | assert result == input 57 | # float 58 | input = 4.2 59 | result = client.service.echoFloat(input) 60 | print 'echoFloat() = %s' % result 61 | assert result == input 62 | # suds 0.3.8+ 63 | result = client.service.echoIntegerArray([]) 64 | print 'echoIntegerArray() = %s' % result 65 | assert result is None 66 | input = [1,2,3,4] 67 | result = client.service.echoIntegerArray(input) 68 | print 'echoIntegerArray() = %s' % result 69 | assert result == input 70 | result = client.service.echoIntegerArray(inputIntegerArray=input) 71 | print 'echoIntegerArray() = %s' % result 72 | assert result == input 73 | except WebFault, f: 74 | errors += 1 75 | print f 76 | print f.fault 77 | except Exception, e: 78 | errors += 1 79 | print e 80 | tb.print_exc() 81 | 82 | try: 83 | url = 'http://jira.atlassian.com/rpc/soap/jirasoapservice-v2?wsdl' 84 | start(url) 85 | client = Client(url) 86 | print client 87 | token = client.service.login('soaptester', 'soaptester') 88 | print 'token="%s"' % token 89 | user = client.service.getUser(token, 'soaptester') 90 | print 'user="%s"' % user 91 | except WebFault, f: 92 | errors += 1 93 | print f 94 | print f.fault 95 | except Exception, e: 96 | errors += 1 97 | print e 98 | tb.print_exc() 99 | 100 | try: 101 | url = 'http://jira.atlassian.com/rpc/soap/jirasoapservice-v2?wsdl' 102 | start(url+' ** cloned **') 103 | client = Client(url).clone() 104 | print '**clone**\n%s' % client 105 | token = client.service.login('soaptester', 'soaptester') 106 | print '**clone** token="%s"' % token 107 | user = client.service.getUser(token, 'soaptester') 108 | print '**clone** user="%s"' % user 109 | except WebFault, f: 110 | errors += 1 111 | print f 112 | print f.fault 113 | except Exception, e: 114 | errors += 1 115 | print e 116 | tb.print_exc() 117 | 118 | try: 119 | url = ' http://www.boyzoid.com/comp/randomQuote.cfc?wsdl ' 120 | start(url) 121 | client = Client(url) 122 | print client 123 | print client.service.getQuote(False) 124 | except WebFault, f: 125 | errors += 1 126 | print f 127 | print f.fault 128 | except Exception, e: 129 | errors += 1 130 | print e 131 | tb.print_exc() 132 | 133 | try: 134 | url = 'http://www.zenfolio.com/zf/api/zfapi.asmx?wsdl' 135 | start(url) 136 | client = Client(url) 137 | print client 138 | #client.setport(0) 139 | group = client.factory.create('Group') 140 | print 'Group:\n%s' % group 141 | print 'LoadGroupHierarchy("demo")' 142 | groupHierarchy = client.service.LoadGroupHierarchy("demo") 143 | print 'result:\n%s' % groupHierarchy 144 | except WebFault, f: 145 | errors += 1 146 | print f 147 | print f.fault 148 | except Exception, e: 149 | errors += 1 150 | print e 151 | tb.print_exc() 152 | 153 | try: 154 | url = 'http://cert.synxis.com/interface/ChannelConnect.asmx?WSDL' 155 | start(url) 156 | client = Client(url) 157 | print client 158 | #client.setport(0) 159 | tpa = client.factory.create('ns1:TPA_Extensions') 160 | print client.service.Ping(tpa, "hello") 161 | except WebFault, f: 162 | errors += 1 163 | print f 164 | print f.fault 165 | except Exception, e: 166 | errors += 1 167 | print e 168 | tb.print_exc() 169 | 170 | try: 171 | url = 'https://sec.neurofuzz-software.com/paos/genSSHA-SOAP.php?wsdl' 172 | start(url) 173 | client = Client(url) 174 | print client 175 | print client.service.genSSHA('hello', 'sha1') 176 | except WebFault, f: 177 | errors += 1 178 | print f 179 | print f.fault 180 | except Exception, e: 181 | errors += 1 182 | print e 183 | tb.print_exc() 184 | 185 | try: 186 | url = 'http://ap1314-dsr.compmed.ucdavis.edu/dataserver/Aperio.Images/Image?method=wsdl' 187 | start(url) 188 | client = Client(url) 189 | #print client.factory.resolver.schema 190 | print client 191 | print 'Logon()' 192 | reply = client.service.Logon('testuser','test') 193 | print reply 194 | except WebFault, f: 195 | errors += 1 196 | print f 197 | print f.fault 198 | except Exception, e: 199 | errors += 1 200 | print e 201 | tb.print_exc() 202 | 203 | try: 204 | url = 'http://soa.ebrev.info/service.wsdl' 205 | start(url) 206 | client = Client(url) 207 | print client 208 | except WebFault, f: 209 | errors += 1 210 | print f 211 | print f.fault 212 | except Exception, e: 213 | errors += 1 214 | print e 215 | tb.print_exc() 216 | 217 | try: 218 | url = 'http://arcweb.esri.com/services/v2/MapImage.wsdl' 219 | start(url) 220 | client = Client(url) 221 | print client 222 | env = client.factory.create('ns2:Envelope') 223 | print env 224 | options = client.factory.create('ns4:MapImageOptions') 225 | print options 226 | except WebFault, f: 227 | errors += 1 228 | print f 229 | print f.fault 230 | except Exception, e: 231 | errors += 1 232 | print e 233 | tb.print_exc() 234 | 235 | try: 236 | url = "http://www.thomas-bayer.com/axis2/services/BLZService?wsdl" 237 | start(url) 238 | client = Client(url) 239 | print client 240 | #client.setport(0) 241 | print client.service.getBank("76251020") 242 | except WebFault, f: 243 | errors += 1 244 | print f 245 | print f.fault 246 | except Exception, e: 247 | errors += 1 248 | print e 249 | tb.print_exc() 250 | 251 | try: 252 | url = "http://arcweb.esri.com/services/v2/RouteFinder.wsdl" 253 | start(url) 254 | client = Client(url) 255 | print client 256 | except WebFault, f: 257 | errors += 1 258 | print f 259 | print f.fault 260 | except Exception, e: 261 | errors += 1 262 | print e 263 | tb.print_exc() 264 | 265 | timer = metrics.Timer() 266 | 267 | try: 268 | url = "https://www.e-conomic.com/secure/api1/EconomicWebService.asmx?WSDL" 269 | start(url) 270 | timer.start() 271 | client = Client(url) 272 | #client.setport(0) 273 | timer.stop() 274 | print 'create client: %s' % timer 275 | timer.start() 276 | s = str(client) 277 | timer.stop() 278 | print 'str(client): %s' % timer 279 | print 'client:\n%s' % s 280 | except WebFault, f: 281 | errors += 1 282 | print f 283 | print f.fault 284 | except Exception, e: 285 | errors += 1 286 | print e 287 | tb.print_exc() 288 | 289 | print '\nFinished: errors = %d' % errors 290 | -------------------------------------------------------------------------------- /tests/saxenc.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the (LGPL) GNU Lesser General Public License as 3 | # published by the Free Software Foundation; either version 3 of the 4 | # License, or (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Library Lesser General Public License for more details at 10 | # ( http://www.gnu.org/licenses/lgpl.html ). 11 | # 12 | # You should have received a copy of the GNU Lesser General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | # written by: Jeff Ortel ( jortel@redhat.com ) 16 | 17 | # 18 | # sax encoding/decoding test. 19 | # 20 | 21 | from suds.sax.element import Element 22 | from suds.sax.parser import Parser 23 | 24 | def basic(): 25 | xml = "Me && <b>my</b> shadow's <i>dog</i> love to 'play' and sing "la,la,la";" 26 | p = Parser() 27 | d = p.parse(string=xml) 28 | a = d.root() 29 | print 'A(parsed)=\n%s' % a 30 | assert str(a) == xml 31 | b = Element('a') 32 | b.setText('Me && <b>my shadow\'s dog love to \'play\' and sing "la,la,la";') 33 | print 'B(encoded)=\n%s' % b 34 | assert str(b) == xml 35 | print 'A(text-decoded)=\n%s' % a.getText() 36 | print 'B(text-decoded)=\n%s' % b.getText() 37 | assert a.getText() == b.getText() 38 | print 'test pruning' 39 | j = Element('A') 40 | j.set('n', 1) 41 | j.append(Element('B')) 42 | print j 43 | j.prune() 44 | print j 45 | 46 | def cdata(): 47 | xml = 'This is my &<tag>]]>' 48 | p = Parser() 49 | d = p.parse(string=xml) 50 | print d 51 | a = d.root() 52 | print a.getText() 53 | 54 | if __name__ == '__main__': 55 | #basic() 56 | cdata() 57 | --------------------------------------------------------------------------------