├── .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 |
--------------------------------------------------------------------------------