├── .gitignore
├── tests
├── test_suds.py
├── external
│ ├── __init__.py
│ ├── jasper.py
│ ├── saxenc.py
│ ├── axis2.py
│ └── rhq.py
└── __init__.py
├── MANIFEST.in
├── setup.cfg
├── suds
├── bindings
│ ├── __init__.py
│ ├── rpc.py
│ ├── multiref.py
│ └── document.py
├── version.py
├── umx
│ ├── basic.py
│ ├── __init__.py
│ ├── attrlist.py
│ ├── encoded.py
│ └── typed.py
├── mx
│ ├── basic.py
│ ├── __init__.py
│ ├── typer.py
│ ├── encoded.py
│ └── core.py
├── metrics.py
├── transport
│ ├── options.py
│ ├── https.py
│ ├── __init__.py
│ └── http.py
├── soaparray.py
├── xsd
│ ├── __init__.py
│ ├── deplist.py
│ ├── doctor.py
│ ├── query.py
│ └── sxbuiltin.py
├── sax
│ ├── enc.py
│ ├── __init__.py
│ ├── text.py
│ ├── parser.py
│ ├── attribute.py
│ └── document.py
├── serviceproxy.py
├── builder.py
├── options.py
├── reader.py
├── __init__.py
├── wsse.py
└── plugin.py
├── sdist
├── makefile
├── HACKING.txt
├── setup.py
└── LICENSE.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | suds_jurko.egg-info
2 | .pyc
3 |
--------------------------------------------------------------------------------
/tests/test_suds.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podio/suds/master/tests/test_suds.py
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | # Additional files to be included in the source distribution package (created
2 | # by running 'setup.py sdist'). Theoretically we could avoid having to manually
3 | # maintain this list by using a setuptools plugin that would automatically
4 | # include all files under Mercurial version control, but the setuptools_hg we
5 | # tried out did not work correctly with Python 3.
6 |
7 | # Top level project files.
8 | include .hgignore
9 | include .hgtags
10 | include .project
11 | include .pydevproject
12 | include HACKING.txt
13 | include LICENSE.txt
14 | include makefile
15 | include python-suds.spec
16 | include README.txt
17 | include sdist
18 | include TODO.txt
19 |
20 | # Test scripts explicitly not included in non-source distribution packages.
21 | recursive-include tests *.py
22 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | # ------------------------------------------
2 | # --- Build system related configuration ---
3 | # ------------------------------------------
4 |
5 | [install]
6 | # Added in the original project as a fix (trunk SVN revision 7) for a problem
7 | # with missing .pyo files when building rpms with Python 2.4.
8 | optimize = 1
9 |
10 | [sdist]
11 | # '.tar.bz2' source distribution format takes the least space while '.zip' is
12 | # the only format supported out-of-the-box on Windows.
13 | formats = bztar,zip
14 |
15 |
16 | # ----------------------------------
17 | # --- Test related configuration ---
18 | # ----------------------------------
19 |
20 | [pytest]
21 | # Folders 'pytest' unit testing framework should avoid when collecting test
22 | # cases to run, e.g. internal build & version control system folders.
23 | norecursedirs=.git .hg .svn build dist
24 |
--------------------------------------------------------------------------------
/tests/external/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
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: Jurko Gospodnetić ( jurko.gospodnetic@pke.hr )
18 |
--------------------------------------------------------------------------------
/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) bindings.
19 | """
20 |
--------------------------------------------------------------------------------
/suds/version.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 |
16 | """
17 | Module containing the library's version information.
18 |
19 | This version information has been extracted into a separate file so it can be
20 | read from the setup.py script without having to import the suds package itself.
21 | See the setup.py script for more detailed information.
22 |
23 | """
24 |
25 | __version__ = "0.4.1 jurko 5 (development)"
26 | __build__ = ""
27 |
--------------------------------------------------------------------------------
/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)
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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)
42 |
--------------------------------------------------------------------------------
/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
49 |
--------------------------------------------------------------------------------
/tests/external/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 |
--------------------------------------------------------------------------------
/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
57 |
--------------------------------------------------------------------------------
/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
60 |
--------------------------------------------------------------------------------
/tests/external/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 |
--------------------------------------------------------------------------------
/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/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)
58 |
--------------------------------------------------------------------------------
/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 | if aty.endswith('[]'):
43 | self.aty = aty[:-2]
44 | else:
45 | self.aty = aty
46 |
47 | def autoqualified(self):
48 | aqs = SXAttribute.autoqualified(self)
49 | aqs.append('aty')
50 | return aqs
51 |
52 | def description(self):
53 | d = SXAttribute.description(self)
54 | d = d+('aty',)
55 | return d
56 |
57 | #
58 | # Builder function, only builds Attribute when arrayType
59 | # attribute is defined on root.
60 | #
61 | def __fn(x, y):
62 | ns = (None, "http://schemas.xmlsoap.org/wsdl/")
63 | aty = y.get('arrayType', ns=ns)
64 | if aty is None:
65 | return SXAttribute(x, y)
66 | else:
67 | return Attribute(x, y, aty)
68 |
69 | #
70 | # Remap tags to __fn() builder.
71 | #
72 | SXFactory.maptag('attribute', __fn)
73 |
--------------------------------------------------------------------------------
/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 | BUILDROOT = ~/rpmbuild
24 | PYTHON3 = python3
25 | PYTHON2TO3 = python3-2to3
26 |
27 | all : rpm docs
28 |
29 | dist : clean
30 | mkdir dist
31 | ./sdist
32 | ./sdist python
33 |
34 | rpm : dist
35 | cp dist/$(PKG)*.gz $(BUILDROOT)/SOURCES
36 | rpmbuild -ba $(SPEC)
37 | cp $(BUILDROOT)/RPMS/noarch/$(PKG)*.rpm dist
38 | cp $(BUILDROOT)/SRPMS/$(PKG)*.rpm dist
39 | rpmlint -i dist/$(PKG)*.rpm
40 |
41 | release : rpm rdocs
42 | scp dist/python*.tar.gz fedorahosted.org:suds
43 | scp dist/python*.rpm fedorahosted.org:suds
44 |
45 | register :
46 | python setup.py sdist bdist_egg register upload
47 |
48 | rdocs : docs
49 | scp /tmp/$(DOCTAR) $(FEDORAPEOPLE):
50 | ssh $(FEDORAPEOPLE) 'cd public_html/suds; rm -rf doc; tar xmzvf ~/$(DOCTAR)'
51 |
52 | docs :
53 | rm -rf doc
54 | rm -f /tmp/$(DOCTAR)
55 | epydoc -vo doc `find suds -name "*.py"`
56 | tar czvf /tmp/$(DOCTAR) doc
57 |
58 | pdf :
59 | epydoc -vo doc --pdf `find suds -name \*.py`
60 | mv doc/api.pdf doc/sudsapi.pdf
61 |
62 | py3testenv :
63 | $(PYTHON3) setup.py build
64 | rm -rf build/lib/tests
65 | mkdir -p build/lib/tests
66 | cp -r tests/* build/lib/tests
67 | $(PYTHON2TO3) build/lib/tests -w -n
68 |
69 | clean :
70 | rm -rf dist
71 | rm -rf build
72 | rm -rf doc
73 | rm -rf *.egg-info
74 | rm -rf $(BUILDROOT)/BUILD/$(PKG)*
75 | rm -rf $(BUILDROOT)/RPMS/noarch/$(PKG)*
76 | rm -rf $(BUILDROOT)/SOURCES/$(PKG)*
77 | rm -rf $(BUILDROOT)/SRPMS/$(PKG)*
78 | find . -name "*.pyc" -exec rm -f {} \;
79 | find . -name "*~" -exec rm -f {} \;
80 |
81 | .PHONY : clean register docs pdf
82 |
--------------------------------------------------------------------------------
/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 | from logging import getLogger
19 | from suds import *
20 | from suds.sax import Namespace, splitPrefix
21 |
22 | log = getLogger(__name__)
23 |
24 |
25 | def qualify(ref, resolvers, defns=Namespace.default):
26 | """
27 | Get a reference that is I{qualified} by namespace.
28 | @param ref: A referenced schema type name.
29 | @type ref: str
30 | @param resolvers: A list of objects to be used to resolve types.
31 | @type resolvers: [L{sax.element.Element},]
32 | @param defns: An optional target namespace used to qualify references
33 | when no prefix is specified.
34 | @type defns: A default namespace I{tuple: (prefix,uri)} used when ref not prefixed.
35 | @return: A qualified reference.
36 | @rtype: (name, namespace-uri)
37 | """
38 | ns = None
39 | p, n = splitPrefix(ref)
40 | if p is not None:
41 | if not isinstance(resolvers, (list, tuple)):
42 | resolvers = (resolvers,)
43 | for r in resolvers:
44 | resolved = r.resolvePrefix(p)
45 | if resolved[1] is not None:
46 | ns = resolved
47 | break
48 | if ns is None:
49 | raise Exception('prefix (%s) not resolved' % p)
50 | else:
51 | ns = defns
52 | return (n, ns[1])
53 |
54 | def isqref(object):
55 | """
56 | Get whether the object is a I{qualified reference}.
57 | @param object: An object to be tested.
58 | @type object: I{any}
59 | @rtype: boolean
60 | @see: L{qualify}
61 | """
62 | return (\
63 | isinstance(object, tuple) and \
64 | len(object) == 2 and \
65 | isinstance(object[0], basestring) and \
66 | isinstance(object[1], basestring))
67 |
68 |
69 | class Filter:
70 | def __init__(self, inclusive=False, *items):
71 | self.inclusive = inclusive
72 | self.items = items
73 | def __contains__(self, x):
74 | if self.inclusive:
75 | result = ( x in self.items )
76 | else:
77 | result = ( x not in self.items )
78 | return result
79 |
--------------------------------------------------------------------------------
/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/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/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(UnicodeMixin):
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 __unicode__(self):
76 | return unicode(self.__client__)
77 |
78 | def __getattr__(self, name):
79 | builtin = name.startswith('__') and name.endswith('__')
80 | if builtin:
81 | return self.__dict__[name]
82 | else:
83 | return getattr(self.__client__.service, name)
84 |
--------------------------------------------------------------------------------
/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/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 none(cls, ns):
73 | return ( ns == cls.default )
74 |
75 | @classmethod
76 | def xsd(cls, ns):
77 | try:
78 | return cls.w3(ns) and ns[1].endswith('XMLSchema')
79 | except:
80 | pass
81 | return False
82 |
83 | @classmethod
84 | def xsi(cls, ns):
85 | try:
86 | return cls.w3(ns) and ns[1].endswith('XMLSchema-instance')
87 | except:
88 | pass
89 | return False
90 |
91 | @classmethod
92 | def xs(cls, ns):
93 | return ( cls.xsd(ns) or cls.xsi(ns) )
94 |
95 | @classmethod
96 | def w3(cls, ns):
97 | try:
98 | return ns[1].startswith('http://www.w3.org')
99 | except:
100 | pass
101 | return False
102 |
103 | @classmethod
104 | def isns(cls, ns):
105 | try:
106 | return isinstance(ns, tuple) and len(ns) == len(cls.default)
107 | except:
108 | pass
109 | return False
110 |
--------------------------------------------------------------------------------
/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
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 = urllib2.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(urllib2.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 = HttpAuthenticated.u2handlers(self)
97 | handlers.append(HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(self.pm))
98 | return handlers
99 |
--------------------------------------------------------------------------------
/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/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(str(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(str(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/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 |
--------------------------------------------------------------------------------
/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 dependency solving.
19 | """
20 |
21 | from logging import getLogger
22 | from suds import *
23 |
24 | log = getLogger(__name__)
25 |
26 |
27 | class DepList:
28 | """
29 | Dependency 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 dependencies.
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()]
141 |
--------------------------------------------------------------------------------
/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/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 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 = []
129 |
--------------------------------------------------------------------------------
/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_or_resolved_type, base_type):
39 | """ build a an object for the specified typename as defined in the schema """
40 | if isinstance(name_or_resolved_type, basestring):
41 | base_type, type = self.resolver.find(name_or_resolved_type)
42 | if type is None:
43 | raise TypeNotFound(name)
44 | else:
45 | type = name_or_resolved_type
46 | cls = type.name
47 | if type.mixed():
48 | data = Factory.property(cls)
49 | else:
50 | data = Factory.object(cls)
51 | md = data.__metadata__
52 | md.sxtype = type
53 | md.sxbase = base_type
54 | md.ordering = self.ordering(type)
55 | history = []
56 | self.add_attributes(data, type)
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.multi_occurrence():
73 | value = []
74 | else:
75 | if len(resolved) > 0:
76 | if resolved.mixed():
77 | value = Factory.property(resolved.name)
78 | md = value.__metadata__
79 | md.sxtype = resolved
80 | else:
81 | value = Factory.object(resolved.name)
82 | md = value.__metadata__
83 | md.sxtype = resolved
84 | md.ordering = self.ordering(resolved)
85 | setattr(data, type.name, value)
86 | if value is not None:
87 | data = value
88 | if not isinstance(data, list):
89 | self.add_attributes(data, resolved)
90 | for child, ancestry in resolved.children():
91 | if self.skip_child(child, ancestry):
92 | continue
93 | self.process(data, child, history[:])
94 |
95 | def add_attributes(self, data, type):
96 | """ add required attributes """
97 | for attr, ancestry in type.attributes():
98 | name = '_%s' % attr.name
99 | value = attr.get_default()
100 | setattr(data, name, value)
101 |
102 | def skip_child(self, child, ancestry):
103 | """ get whether or not to skip the specified child """
104 | if child.any(): return True
105 | for x in ancestry:
106 | if x.choice():
107 | return True
108 | return False
109 |
110 | def ordering(self, type):
111 | """ get the ordering """
112 | result = []
113 | for child, ancestry in type.resolve():
114 | name = child.name
115 | if child.name is None:
116 | continue
117 | if child.isattr():
118 | name = '_%s' % child.name
119 | result.append(name)
120 | return result
121 |
--------------------------------------------------------------------------------
/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 | import sys
30 | from logging import getLogger
31 | import suds.metrics
32 | from suds import *
33 | from suds.sax import *
34 | from suds.sax.document import Document
35 | from suds.sax.element import Element
36 | from suds.sax.text import Text
37 | from suds.sax.attribute import Attribute
38 | from xml.sax import make_parser, InputSource, ContentHandler
39 | from xml.sax.handler import feature_external_ges
40 |
41 | if sys.version_info < (3, 0):
42 | from cStringIO import StringIO as BytesIO
43 | else:
44 | from io import BytesIO
45 |
46 | log = getLogger(__name__)
47 |
48 |
49 | class Handler(ContentHandler):
50 | """ sax hanlder """
51 |
52 | def __init__(self):
53 | self.nodes = [Document()]
54 |
55 | def startElement(self, name, attrs):
56 | top = self.top()
57 | node = Element(unicode(name))
58 | for a in attrs.getNames():
59 | n = unicode(a)
60 | v = unicode(attrs.getValue(a))
61 | attribute = Attribute(n,v)
62 | if self.mapPrefix(node, attribute):
63 | continue
64 | node.append(attribute)
65 | node.charbuffer = []
66 | top.append(node)
67 | self.push(node)
68 |
69 | def mapPrefix(self, node, attribute):
70 | skip = False
71 | if attribute.name == 'xmlns':
72 | if len(attribute.value):
73 | node.expns = unicode(attribute.value)
74 | skip = True
75 | elif attribute.prefix == 'xmlns':
76 | prefix = attribute.name
77 | node.nsprefixes[prefix] = unicode(attribute.value)
78 | skip = True
79 | return skip
80 |
81 | def endElement(self, name):
82 | name = unicode(name)
83 | current = self.top()
84 | if len(current.charbuffer):
85 | current.text = Text(u''.join(current.charbuffer))
86 | del current.charbuffer
87 | if len(current):
88 | current.trim()
89 | currentqname = current.qname()
90 | if name == currentqname:
91 | self.pop()
92 | else:
93 | raise Exception('malformed document')
94 |
95 | def characters(self, content):
96 | text = unicode(content)
97 | node = self.top()
98 | node.charbuffer.append(text)
99 |
100 | def push(self, node):
101 | self.nodes.append(node)
102 | return node
103 |
104 | def pop(self):
105 | return self.nodes.pop()
106 |
107 | def top(self):
108 | return self.nodes[len(self.nodes)-1]
109 |
110 |
111 | class Parser:
112 | """ SAX Parser """
113 |
114 | @classmethod
115 | def saxparser(cls):
116 | p = make_parser()
117 | p.setFeature(feature_external_ges, 0)
118 | h = Handler()
119 | p.setContentHandler(h)
120 | return (p, h)
121 |
122 | def parse(self, file=None, string=None):
123 | """
124 | SAX parse XML text.
125 | @param file: Parse a python I{file-like} object.
126 | @type file: I{file-like} object.
127 | @param string: Parse string XML.
128 | @type string: str
129 | """
130 | timer = metrics.Timer()
131 | timer.start()
132 | sax, handler = self.saxparser()
133 | if file is not None:
134 | sax.parse(file)
135 | timer.stop()
136 | metrics.log.debug('sax (%s) duration: %s', file, timer)
137 | return handler.nodes[0]
138 | if string is not None:
139 | source = InputSource(None)
140 | source.setByteStream(BytesIO(suds.str2bytes(string)))
141 | sax.parse(source)
142 | timer.stop()
143 | metrics.log.debug('%s\nsax duration: %s', string, timer)
144 | return handler.nodes[0]
145 |
--------------------------------------------------------------------------------
/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/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 multi_occurrence(self, content):
99 | return content.type.multi_occurrence()
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
142 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/tests/external/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
--------------------------------------------------------------------------------
/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{prettyxml} - Flag that causes I{pretty} xml to be rendered when generating
88 | the outbound soap envelope.
89 | - type: I{bool}
90 | - default: False
91 | - B{autoblend} - Flag that ensures that the schema(s) defined within the
92 | WSDL import each other.
93 | - type: I{bool}
94 | - default: False
95 | - B{cachingpolicy} - The caching policy.
96 | - type: I{int}
97 | - 0 = Cache XML documents.
98 | - 1 = Cache WSDL (pickled) object.
99 | - default: 0
100 | - B{plugins} - A plugin container.
101 | - type: I{list}
102 | - B{nosend} - Create the soap envelope but don't send.
103 | When specified, method invocation returns a I{RequestContext}
104 | instead of sending it.
105 | - type: I{bool}
106 | - default: False
107 | """
108 | def __init__(self, **kwargs):
109 | domain = __name__
110 | definitions = [
111 | Definition('cache', Cache, NoCache()),
112 | Definition('faults', bool, True),
113 | Definition('transport', Transport, None, TpLinker()),
114 | Definition('service', (int, basestring), None),
115 | Definition('port', (int, basestring), None),
116 | Definition('location', basestring, None),
117 | Definition('soapheaders', (), ()),
118 | Definition('wsse', Security, None),
119 | Definition('doctor', Doctor, None),
120 | Definition('xstq', bool, True),
121 | Definition('prefixes', bool, True),
122 | Definition('retxml', bool, False),
123 | Definition('prettyxml', bool, False),
124 | Definition('autoblend', bool, False),
125 | Definition('cachingpolicy', int, 0),
126 | Definition('plugins', (list, tuple), []),
127 | Definition('nosend', bool, False),
128 | ]
129 | Skin.__init__(self, domain, definitions, kwargs)
130 |
--------------------------------------------------------------------------------
/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.cache import Cache, NoCache
25 | from suds.store import DocumentStore
26 | from suds.plugin import PluginContainer
27 | from logging import getLogger
28 |
29 |
30 | log = getLogger(__name__)
31 |
32 |
33 | class Reader:
34 | """
35 | The reader provides integration with cache.
36 | @ivar options: An options object.
37 | @type options: I{Options}
38 | """
39 |
40 | def __init__(self, options):
41 | """
42 | @param options: An options object.
43 | @type options: I{Options}
44 | """
45 | self.options = options
46 | self.plugins = PluginContainer(options.plugins)
47 |
48 | def mangle(self, name, x):
49 | """
50 | Mangle the name by hashing the I{name} and appending I{x}.
51 | @return: the mangled name.
52 | """
53 | h = abs(hash(name))
54 | return '%s-%s' % (h, x)
55 |
56 |
57 | class DocumentReader(Reader):
58 | """
59 | The XML document reader provides an integration
60 | between the SAX L{Parser} and the document cache.
61 | """
62 |
63 | def open(self, url):
64 | """
65 | Open an XML document at the specified I{URL}.
66 | First, the document attempted to be retrieved from
67 | the I{object cache}. If not found, it is downloaded and
68 | parsed using the SAX parser. The result is added to the
69 | cache for the next open().
70 | @param url: A document URL.
71 | @type url: str.
72 | @return: The specified XML document.
73 | @rtype: I{Document}
74 | """
75 | cache = self.cache()
76 | id = self.mangle(url, 'document')
77 | d = cache.get(id)
78 | if d is None:
79 | d = self.download(url)
80 | cache.put(id, d)
81 | self.plugins.document.parsed(url=url, document=d.root())
82 | return d
83 |
84 | def download(self, url):
85 | """
86 | Download the document.
87 | @param url: A document URL.
88 | @type url: str.
89 | @return: A file pointer to the document.
90 | @rtype: file-like
91 | """
92 | store = DocumentStore()
93 | fp = store.open(url)
94 | if fp is None:
95 | fp = self.options.transport.open(Request(url))
96 | content = fp.read()
97 | fp.close()
98 | ctx = self.plugins.document.loaded(url=url, document=content)
99 | content = ctx.document
100 | sax = Parser()
101 | return sax.parse(string=content)
102 |
103 | def cache(self):
104 | """
105 | Get the cache.
106 | @return: The I{options} when I{cachingpolicy} = B{0}.
107 | @rtype: L{Cache}
108 | """
109 | if self.options.cachingpolicy == 0:
110 | return self.options.cache
111 | else:
112 | return NoCache()
113 |
114 |
115 | class DefinitionsReader(Reader):
116 | """
117 | The WSDL definitions reader provides an integration
118 | between the Definitions and the object cache.
119 | @ivar fn: A factory function (constructor) used to
120 | create the object not found in the cache.
121 | @type fn: I{Constructor}
122 | """
123 |
124 | def __init__(self, options, fn):
125 | """
126 | @param options: An options object.
127 | @type options: I{Options}
128 | @param fn: A factory function (constructor) used to
129 | create the object not found in the cache.
130 | @type fn: I{Constructor}
131 | """
132 | Reader.__init__(self, options)
133 | self.fn = fn
134 |
135 | def open(self, url):
136 | """
137 | Open a WSDL at the specified I{URL}.
138 | First, the WSDL attempted to be retrieved from
139 | the I{object cache}. After unpickled from the cache, the
140 | I{options} attribute is restored.
141 | If not found, it is downloaded and instantiated using the
142 | I{fn} constructor and added to the cache for the next open().
143 | @param url: A WSDL URL.
144 | @type url: str.
145 | @return: The WSDL object.
146 | @rtype: I{Definitions}
147 | """
148 | cache = self.cache()
149 | id = self.mangle(url, 'wsdl')
150 | d = cache.get(id)
151 | if d is None:
152 | d = self.fn(url, self.options)
153 | cache.put(id, d)
154 | else:
155 | d.options = self.options
156 | for imp in d.imports:
157 | imp.imported.options = self.options
158 | return d
159 |
160 | def cache(self):
161 | """
162 | Get the cache.
163 | @return: The I{options} when I{cachingpolicy} = B{1}.
164 | @rtype: L{Cache}
165 | """
166 | if self.options.cachingpolicy == 1:
167 | return self.options.cache
168 | else:
169 | return NoCache()
170 |
--------------------------------------------------------------------------------
/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(UnicodeMixin):
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 __unicode__(self):
171 | """ get an xml string representation """
172 | n = self.qname()
173 | if self.hasText():
174 | v = self.value.escape()
175 | else:
176 | v = self.value
177 | return u'%s="%s"' % (n, v)
178 |
--------------------------------------------------------------------------------
/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:
29 | """ An XML Document """
30 |
31 | DECL = ''
32 |
33 | def __init__(self, root=None):
34 | """
35 | @param root: A root L{Element} or name used to build
36 | the document root element.
37 | @type root: (L{Element}|str|None)
38 | """
39 | self.__root = None
40 | self.append(root)
41 |
42 | def root(self):
43 | """
44 | Get the document root element (can be None)
45 | @return: The document root.
46 | @rtype: L{Element}
47 | """
48 | return self.__root
49 |
50 | def append(self, node):
51 | """
52 | Append (set) the document root.
53 | @param node: A root L{Element} or name used to build
54 | the document root element.
55 | @type node: (L{Element}|str|None)
56 | """
57 | if isinstance(node, basestring):
58 | self.__root = Element(node)
59 | return
60 | if isinstance(node, Element):
61 | self.__root = node
62 | return
63 |
64 | def getChild(self, name, ns=None, default=None):
65 | """
66 | Get a child by (optional) name and/or (optional) namespace.
67 | @param name: The name of a child element (may contain prefix).
68 | @type name: basestring
69 | @param ns: An optional namespace used to match the child.
70 | @type ns: (I{prefix}, I{name})
71 | @param default: Returned when child not-found.
72 | @type default: L{Element}
73 | @return: The requested child, or I{default} when not-found.
74 | @rtype: L{Element}
75 | """
76 | if self.__root is None:
77 | return default
78 | if ns is None:
79 | prefix, name = splitPrefix(name)
80 | if prefix is None:
81 | ns = None
82 | else:
83 | ns = self.__root.resolvePrefix(prefix)
84 | if self.__root.match(name, ns):
85 | return self.__root
86 | else:
87 | return default
88 |
89 | def childAtPath(self, path):
90 | """
91 | Get a child at I{path} where I{path} is a (/) separated
92 | list of element names that are expected to be children.
93 | @param path: A (/) separated list of element names.
94 | @type path: basestring
95 | @return: The leaf node at the end of I{path}
96 | @rtype: L{Element}
97 | """
98 | if self.__root is None:
99 | return None
100 | if path[0] == '/':
101 | path = path[1:]
102 | path = path.split('/',1)
103 | if self.getChild(path[0]) is None:
104 | return None
105 | if len(path) > 1:
106 | return self.__root.childAtPath(path[1])
107 | else:
108 | return self.__root
109 |
110 | def childrenAtPath(self, path):
111 | """
112 | Get a list of children at I{path} where I{path} is a (/) separated
113 | list of element names that are expected to be children.
114 | @param path: A (/) separated list of element names.
115 | @type path: basestring
116 | @return: The collection leaf nodes at the end of I{path}
117 | @rtype: [L{Element},...]
118 | """
119 | if self.__root is None:
120 | return []
121 | if path[0] == '/':
122 | path = path[1:]
123 | path = path.split('/',1)
124 | if self.getChild(path[0]) is None:
125 | return []
126 | if len(path) > 1:
127 | return self.__root.childrenAtPath(path[1])
128 | else:
129 | return [self.__root,]
130 |
131 | def getChildren(self, name=None, ns=None):
132 | """
133 | Get a list of children by (optional) name and/or (optional) namespace.
134 | @param name: The name of a child element (may contain prefix).
135 | @type name: basestring
136 | @param ns: An optional namespace used to match the child.
137 | @type ns: (I{prefix}, I{name})
138 | @return: The list of matching children.
139 | @rtype: [L{Element},...]
140 | """
141 | if name is None:
142 | matched = self.__root
143 | else:
144 | matched = self.getChild(name, ns)
145 | if matched is None:
146 | return []
147 | else:
148 | return [matched,]
149 |
150 | def str(self):
151 | """
152 | Get a string representation of this XML document.
153 | @return: A I{pretty} string.
154 | @rtype: basestring
155 | """
156 | s = []
157 | s.append(self.DECL)
158 | root = self.root()
159 | if root is not None:
160 | s.append('\n')
161 | s.append(root.str())
162 | return ''.join(s)
163 |
164 | def plain(self):
165 | """
166 | Get a string representation of this XML document.
167 | @return: A I{plain} string.
168 | @rtype: basestring
169 | """
170 | s = []
171 | s.append(self.DECL)
172 | root = self.root()
173 | if root is not None:
174 | s.append(root.plain())
175 | return ''.join(s)
176 |
177 | def __unicode__(self):
178 | return self.str()
179 |
--------------------------------------------------------------------------------
/HACKING.txt:
--------------------------------------------------------------------------------
1 | GENERAL DEVELOPMENT NOTES:
2 | =================================================
3 |
4 | * Project's sources accessible from a Mercurial version control repository
5 | hosted by BitBucket at 'https://bitbucket.org/jurko/suds'.
6 |
7 | * Project development should be tracked in the TODO.txt file.
8 | * Exact formatting is not important as long as its contect is kept
9 | formatted consistently.
10 | * Done tasks should be marked as such and not deleted.
11 |
12 | * Testing.
13 | * 'pytest' testing framework needed to run unit tests.
14 | * To run the tests using Python 3 first process them and the rest of the
15 | library sources using the Python 2to3 conversion tool.
16 |
17 | * Base sources should remain Python 2.x compatible. Since the original
18 | project states aiming for Python 2.4 compatibility we should do so as
19 | well.
20 | * Compatibility notes.
21 | * 'with' statement first introduced in Python 2.5.
22 |
23 | * Handling Python 3 related patches applicable to the original suds
24 | development project.
25 | * Should be first be added to the 'Python 3 support' branch and then
26 | merged back to the trunk from there.
27 | * Should be kept synchronized with the ones found in the Mercurial patch
28 | queue hosted at 'https://bitbucket.org/bernh/suds-python-3-patches'.
29 |
30 | * External documentation.
31 | * SOAP standards.
32 | * 'http://www.w3.org/TR/soap'.
33 | * Version 1.1.
34 | * 'http://www.w3.org/TR/2000/NOTE-SOAP-20000508'.
35 | * Version 1.2.
36 | * Part0: Primer.
37 | * 'http://www.w3.org/TR/2007/REC-soap12-part0-20070427'.
38 | * Errata: 'http://www.w3.org/2007/04/
39 | REC-soap12-part0-20070427-errata.html'
40 | * Part1: Messaging Framework.
41 | * 'http://www.w3.org/TR/2007/REC-soap12-part1-20070427'.
42 | * Errata: 'http://www.w3.org/2007/04/
43 | REC-soap12-part1-20070427-errata.html'.
44 | * Part2: Adjuncts.
45 | * 'http://www.w3.org/TR/2007/REC-soap12-part2-20070427'.
46 | * Errata: 'http://www.w3.org/2007/04/
47 | REC-soap12-part2-20070427-errata.html'.
48 | * Specification Assertions and Test Collection.
49 | * 'http://www.w3.org/TR/2007/
50 | REC-soap12-testcollection-20070427'.
51 | * Errata: 'http://www.w3.org/2007/04/
52 | REC-soap12-testcollection-20070427-errata.html'.
53 | * WSDL 1.1 standard.
54 | * 'http://www.w3.org/TR/wsdl'.
55 | * XML Schema standard.
56 | * Part 0: Primer Second Edition: 'http://www.w3.org/TR/xmlschema-0'.
57 | * Non-normative document intended to provide an easily readable
58 | description of the XML Schema facilities, and is oriented
59 | towards quickly understanding how to create schemas using the
60 | XML Schema language.
61 | * Part 1: Structures: 'http://www.w3.org/TR/xmlschema-1'.
62 | * Part 2: Datatypes: 'http://www.w3.org/TR/xmlschema-2'.
63 |
64 |
65 | TOP-LEVEL FILES & FOLDERS:
66 | =================================================
67 |
68 | * '.hg/', '.hgignore', '.hgtags'.
69 | * Mercurial version control related data.
70 |
71 | * 'build/', 'dist/', 'suds_jurko.egg-info/'.
72 | * Folders created during project setup procedure (build + install).
73 |
74 | * 'suds/'.
75 | * Basic project source code.
76 |
77 | * 'tests/'.
78 | * Project test code.
79 |
80 | * '.project', '.pydevproject', 'makefile', 'python-suds.spec', 'sdist'.
81 | * Original suds library development project's development environment
82 | configuration & scripts as used by the original developer.
83 |
84 | * 'MANIFEST.in'.
85 | * Build system configuration file listing the files to be included in
86 | the project's source distribution packages in addition to those
87 | automatically added to those packages by the used package preparation
88 | system.
89 |
90 | * 'HACKING.txt', 'LICENSE.txt', 'README.txt', 'TODO.txt'.
91 | * Internal project documentation.
92 |
93 | * 'setup.cfg'.
94 | * Basic project Python configuration.
95 |
96 | * 'setup.py'.
97 | * Standard Python project setup script.
98 | * Use 'setup.py --help' for more details.
99 | * 'setup.py build' for building the project.
100 | * 'setup.py develop' for preparing the development environment
101 | (adding the project folder to the Python module search path).
102 | * 'setup.py install' for builing & installing the project.
103 | * 'setup.py register' for registering a project release at PyPI.
104 | * 'setup.py sdist' for preparing a source distribution.
105 | * 'setup.py upload' for uploading prepared packages to PyPI.
106 |
107 |
108 | RELEASE PROCEDURE:
109 | =================================================
110 |
111 | * Document the release correctly in 'README.txt'.
112 |
113 | * Version identification.
114 | * Remove the '(development)' suffix for official release builds.
115 | * Format ' jurko #'.
116 | * E.g. '0.4.1 jurko 1'.
117 |
118 | * Tag in Hg.
119 | * Name the tag like 'release-'.
120 | * E.g. 'release-0.4.1 jurko 1'.
121 |
122 | * Prepare official releases based only on tagged commits.
123 | * Note.
124 | * Official releases should always be prepared based on tagged
125 | revisions with no local changes in the used sandbox.
126 | * Prepare source distribution packages (both .zip & .tar.bz2 formats),
127 | register the new release at PyPI and upload the prepared source
128 | packages.
129 | * Run 'setup.py sdist register upload'.
130 | * Upload the prepared source package to the project site.
131 | * Use the BitBucket project web interface.
132 | * Optionally archive the prepared source package locally.
133 |
134 | * Next development version identification.
135 | * Bump up the forked project version counter.
136 | * Add back the '(development)' suffix.
137 |
138 | * Notify whomever the new release might concern.
139 |
--------------------------------------------------------------------------------
/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 sys
23 |
24 | #
25 | # Project properties
26 | #
27 |
28 | from version import __build__, __version__
29 |
30 | #
31 | # Exceptions
32 | #
33 |
34 | class MethodNotFound(Exception):
35 | def __init__(self, name):
36 | Exception.__init__(self, "Method not found: '%s'" % name)
37 |
38 | class PortNotFound(Exception):
39 | def __init__(self, name):
40 | Exception.__init__(self, "Port not found: '%s'" % name)
41 |
42 | class ServiceNotFound(Exception):
43 | def __init__(self, name):
44 | Exception.__init__(self, "Service not found: '%s'" % name)
45 |
46 | class TypeNotFound(Exception):
47 | def __init__(self, name):
48 | Exception.__init__(self, "Type not found: '%s'" % tostr(name))
49 |
50 | class BuildError(Exception):
51 | msg = \
52 | """
53 | An error occured while building an instance of (%s). As a result
54 | the object you requested could not be constructed. It is recommended
55 | that you construct the type manually using a Suds object.
56 | Please open a ticket with a description of this error.
57 | Reason: %s
58 | """
59 | def __init__(self, name, exception):
60 | Exception.__init__(self, BuildError.msg % (name, exception))
61 |
62 | class SoapHeadersNotPermitted(Exception):
63 | msg = \
64 | """
65 | Method (%s) was invoked with SOAP headers. The WSDL does not
66 | define SOAP headers for this method. Retry without the soapheaders
67 | keyword argument.
68 | """
69 | def __init__(self, name):
70 | Exception.__init__(self, self.msg % name)
71 |
72 | class WebFault(Exception):
73 | def __init__(self, fault, document):
74 | if hasattr(fault, 'faultstring'):
75 | Exception.__init__(self, "Server raised fault: '%s'" % fault.faultstring)
76 | self.fault = fault
77 | self.document = document
78 |
79 | #
80 | # Logging
81 | #
82 |
83 | class Repr:
84 | def __init__(self, x):
85 | self.x = x
86 | def __str__(self):
87 | return repr(self.x)
88 |
89 | #
90 | # Utility
91 | #
92 |
93 | def tostr(object, encoding=None):
94 | """ get a unicode safe string representation of an object """
95 | if isinstance(object, basestring):
96 | if encoding is None:
97 | return object
98 | else:
99 | return object.encode(encoding)
100 | if isinstance(object, tuple):
101 | s = ['(']
102 | for item in object:
103 | if isinstance(item, basestring):
104 | s.append(item)
105 | else:
106 | s.append(tostr(item))
107 | s.append(', ')
108 | s.append(')')
109 | return ''.join(s)
110 | if isinstance(object, list):
111 | s = ['[']
112 | for item in object:
113 | if isinstance(item, basestring):
114 | s.append(item)
115 | else:
116 | s.append(tostr(item))
117 | s.append(', ')
118 | s.append(']')
119 | return ''.join(s)
120 | if isinstance(object, dict):
121 | s = ['{']
122 | for item in object.items():
123 | if isinstance(item[0], basestring):
124 | s.append(item[0])
125 | else:
126 | s.append(tostr(item[0]))
127 | s.append(' = ')
128 | if isinstance(item[1], basestring):
129 | s.append(item[1])
130 | else:
131 | s.append(tostr(item[1]))
132 | s.append(', ')
133 | s.append('}')
134 | return ''.join(s)
135 | try:
136 | return unicode(object)
137 | except:
138 | return str(object)
139 |
140 | class null:
141 | """
142 | The I{null} object.
143 | Used to pass NULL for optional XML nodes.
144 | """
145 | pass
146 |
147 | def objid(obj):
148 | return obj.__class__.__name__\
149 | +':'+hex(id(obj))
150 |
151 | #
152 | # Python 3 compatibility
153 | #
154 |
155 | # Idea from 'http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python'.
156 | class UnicodeMixin(object):
157 | if sys.version_info >= (3, 0):
158 | # For Python 3, __str__() and __unicode__() should be identical.
159 | __str__ = lambda x: x.__unicode__()
160 | else:
161 | __str__ = lambda x: unicode(x).encode('utf-8')
162 |
163 | # Compatibility wrappers to convert between bytes and strings.
164 | if sys.version_info >= (3, 0):
165 | def str2bytes(s):
166 | if isinstance(s, bytes):
167 | return s
168 | return s.encode('latin1')
169 | def bytes2str(s):
170 | if isinstance(s, str):
171 | return s
172 | return s.decode('latin1')
173 | else:
174 | # For Python 2 bytes and string types are the same.
175 | str2bytes = lambda s: s
176 | bytes2str = lambda s: s
177 |
178 | # Quick-fix helper function for making some __str__ & __repr__ function
179 | # implementations originally returning UTF-8 encoded strings portable to Python
180 | # 3. The original implementation worked in Python 2 but in Python 3 this would
181 | # return a bytes object which is not an allowed return type for those calls. In
182 | # Python 3 on the other hand directly returning a unicode string from them is
183 | # perfectly valid and there is no need for converting those strings to utf-8
184 | # encoded strings in the first place.
185 | # The original implementation classes should most likely be refactored to use
186 | # unicode for internal representation and convert to encoded bytes only at the
187 | # last possible moment, e.g. on an explicit __str__/__repr__ call.
188 | if sys.version_info >= (3, 0):
189 | str_to_utf8_in_py2 = lambda str: str
190 | else:
191 | str_to_utf8_in_py2 = lambda str: str.encode('utf-8')
192 |
193 |
194 | import client
195 |
--------------------------------------------------------------------------------
/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
213 |
--------------------------------------------------------------------------------
/tests/external/rhq.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 an RHQ server.
19 | # ( http://www.rhq-project.org )
20 | #
21 |
22 | import sys
23 | sys.path.append('../../')
24 |
25 | import logging
26 | import traceback as tb
27 | from tests import *
28 | from suds import WebFault
29 | from suds.client import Client
30 |
31 |
32 | errors = 0
33 |
34 | setup_logging()
35 |
36 | logging.getLogger('suds.client').setLevel(logging.DEBUG)
37 | #logging.getLogger('suds.metrics').setLevel(logging.DEBUG)
38 | #logging.getLogger('suds').setLevel(logging.DEBUG)
39 |
40 |
41 | def start(url):
42 | global errors
43 | print '\n________________________________________________________________\n'
44 | print 'Test @ ( %s ) %d' % (url, errors)
45 |
46 |
47 | def rhqTest():
48 |
49 | global errors
50 |
51 | url = 'http://localhost.localdomain:7080/rhq-rhq-enterprise-server-ejb3/WebservicesManagerBean?wsdl'
52 | start(url)
53 | client = Client(url)
54 | print client
55 |
56 | try:
57 |
58 | #
59 | # create name
60 | #
61 | name = client.factory.create('name')
62 | name.first = u'Jeff'+unichr(1234)
63 | name.last = 'Ortel < Company'
64 | #
65 | # create a phone object using the wsdl
66 | #
67 | phoneA = client.factory.create('phone')
68 | phoneA.npa = 410
69 | phoneA.nxx = 555
70 | phoneA.number = 5138
71 | phoneB = client.factory.create('phone')
72 | phoneB.npa = 919
73 | phoneB.nxx = 555
74 | phoneB.number = 4406
75 | #
76 | # lets add some animals
77 | #
78 | dog = client.factory.create('dog')
79 | dog.name = 'rover'
80 | dog.age = 3
81 | cat = client.factory.create('cat')
82 | cat.name = 'kitty'
83 | cat.age = 4
84 | #
85 | # create a person object using the wsdl
86 | #
87 | person = client.factory.create('person')
88 | print person
89 | person.name = name
90 | person.age = 43
91 | person.phone.append(phoneA)
92 | person.phone.append(phoneB)
93 | person.pet.append(dog)
94 | person.pet.append(cat)
95 | print person
96 | #
97 | # addPerson()
98 | #
99 | print 'addPersion()'
100 | result = client.service.addPerson(person)
101 | sent = client.last_sent()
102 | rcvd = client.last_received()
103 | print '\nreply(\n%s\n)\n' % result
104 | #
105 | # create a new name object used to update the person
106 | #
107 | newname = client.factory.create('name')
108 | newname.first = 'Todd'
109 | newname.last = None
110 | #
111 | # update the person's name (using the webservice)
112 | #
113 | print 'updatePersion()'
114 | result = client.service.updatePerson(person, newname)
115 | print '\nreply(\n%s\n)\n' % str(result)
116 | result = client.service.updatePerson(person, None)
117 | print '\nreply(\n%s\n)\n' % str(result)
118 | except WebFault, f:
119 | errors += 1
120 | print f
121 | print f.fault
122 | except Exception, e:
123 | errors += 1
124 | print e
125 | tb.print_exc()
126 |
127 | try:
128 | print "echo('this is cool')"
129 | result = client.service.echo('this is cool')
130 | print '\nreply( %s )\n' % str(result)
131 | print 'echo(None)'
132 | result = client.service.echo(None)
133 | print '\nreply( %s )\n' % str(result)
134 | except WebFault, f:
135 | errors += 1
136 | print f
137 | print f.fault
138 | except Exception, e:
139 | errors += 1
140 | print e
141 | tb.print_exc()
142 |
143 | try:
144 | print 'hello()'
145 | result = client.service.hello()
146 | print '\nreply( %s )\n' % str(result)
147 | except WebFault, f:
148 | errors += 1
149 | print f
150 | print f.fault
151 | except Exception, e:
152 | errors += 1
153 | print e
154 | tb.print_exc()
155 |
156 | try:
157 | print 'testVoid()'
158 | result = client.service.testVoid()
159 | print '\nreply( %s )\n' % str(result)
160 | except WebFault, f:
161 | errors += 1
162 | print f
163 | print f.fault
164 | except Exception, e:
165 | errors += 1
166 | print e
167 | tb.print_exc()
168 |
169 | try:
170 | mylist = ['my', 'dog', 'likes', 'steak']
171 | print 'testListArgs(%s)' % mylist
172 | result = client.service.testListArg(mylist)
173 | print '\nreply( %s )\n' % str(result)
174 | except WebFault, f:
175 | errors += 1
176 | print f
177 | print f.fault
178 | except Exception, e:
179 | errors += 1
180 | print e
181 | tb.print_exc()
182 |
183 | try:
184 | s = 'hello'
185 | for n in range(0, 3):
186 | print 'getList(%s, %d)' % (s, n)
187 | result = client.service.getList(s, n)
188 | print '\nreply( %s )\n' % str(result)
189 | if len(result) != n:
190 | errors += 1
191 | print 'expected (%d), reply (%d)' % (n, len(result))
192 | except WebFault, f:
193 | errors += 1
194 | print f
195 | print f.fault
196 | except Exception, e:
197 | errors += 1
198 | print e
199 | tb.print_exc()
200 |
201 | try:
202 | print 'testExceptions()'
203 | result = client.service.testExceptions()
204 | print '\nreply( %s )\n' % tostr(result)
205 | raise Exception('Fault expected and not raised')
206 | except WebFault, f:
207 | print f
208 | print f.fault
209 | print f.document
210 | except Exception, e:
211 | errors += 1
212 | print e
213 | tb.print_exc()
214 |
215 |
216 | if __name__ == '__main__':
217 | errors = 0
218 | rhqTest()
219 | print '\nFinished: errors=%d' % errors
220 |
--------------------------------------------------------------------------------
/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, Attribute
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 | elif pd[1].isattr():
61 | value = kwargs.get('_' + pd[0])
62 | else:
63 | value = kwargs.get(pd[0])
64 | n += 1
65 | if pd[1].isattr() and value is not None:
66 | root.attributes.append(Attribute(pd[0], value))
67 | continue
68 | # Skip non-existing by-choice arguments.
69 | # Implementation notes:
70 | # * This functionality might be better placed inside the mkparam()
71 | # function but to do that we would first need to understand more
72 | # thoroughly how different Binding subclasses in suds work and
73 | # how they would be affected by this change.
74 | # * If caller actually wishes to pass an empty choice parameter he
75 | # can specify its value explicitly as an empty string.
76 | if pd[2] and value is None:
77 | continue
78 | p = self.mkparam(method, pd, value)
79 | if p is None:
80 | continue
81 | if not wrapped:
82 | ns = pd[1].namespace('ns0')
83 | p.setPrefix(ns[0], ns[1])
84 | root.append(p)
85 | return root
86 |
87 | def replycontent(self, method, body):
88 | wrapped = method.soap.output.body.wrapped
89 | if wrapped:
90 | return body[0].children
91 | else:
92 | return body.children
93 |
94 | def document(self, wrapper):
95 | """
96 | Get the document root. For I{document/literal}, this is the
97 | name of the wrapper element qualifed by the schema tns.
98 | @param wrapper: The method name.
99 | @type wrapper: L{xsd.sxbase.SchemaObject}
100 | @return: A root element.
101 | @rtype: L{Element}
102 | """
103 | tag = wrapper[1].name
104 | ns = wrapper[1].namespace('ns0')
105 | d = Element(tag, ns=ns)
106 | return d
107 |
108 | def mkparam(self, method, pdef, object):
109 | #
110 | # Expand list parameters into individual parameters
111 | # each with the type information. This is because in document
112 | # arrays are simply multi-occurrence elements.
113 | #
114 | if isinstance(object, (list, tuple)):
115 | tags = []
116 | for item in object:
117 | tags.append(self.mkparam(method, pdef, item))
118 | return tags
119 | else:
120 | return Binding.mkparam(self, method, pdef, object)
121 |
122 | def param_defs(self, method):
123 | #
124 | # Get parameter definitions for document literal.
125 | # The I{wrapped} vs I{bare} style is detected in 2 ways.
126 | # If there is 2+ parts in the message then it is I{bare}.
127 | # If there is only (1) part and that part resolves to a builtin then
128 | # it is I{bare}. Otherwise, it is I{wrapped}.
129 | #
130 | pts = self.bodypart_types(method)
131 | wrapped = method.soap.input.body.wrapped
132 | if not wrapped:
133 | return pts
134 | result = []
135 | # wrapped
136 | for p in pts:
137 | resolved = p[1].resolve()
138 | for child, ancestry in resolved:
139 | result.append((child.name, child, self.bychoice(ancestry)))
140 | return result
141 |
142 | def returned_types(self, method):
143 | result = []
144 | wrapped = method.soap.output.body.wrapped
145 | rts = self.bodypart_types(method, input=False)
146 | if wrapped:
147 | for pt in rts:
148 | resolved = pt.resolve(nobuiltin=True)
149 | for child, ancestry in resolved:
150 | result.append(child)
151 | break
152 | else:
153 | result += rts
154 | return result
155 |
156 | def bychoice(self, ancestry):
157 | """
158 | The ancestry contains a
159 | @param ancestry: A list of ancestors.
160 | @type ancestry: list
161 | @return: True if contains
162 | @rtype: boolean
163 | """
164 | for x in ancestry:
165 | if x.choice():
166 | return True
167 | return False
168 |
--------------------------------------------------------------------------------
/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
22 | import base64
23 | import socket
24 | import sys
25 | from suds.transport import *
26 | from suds.properties import Unskin
27 | from urlparse import urlparse
28 | from cookielib import CookieJar
29 | from logging import getLogger
30 |
31 | log = getLogger(__name__)
32 |
33 |
34 | class HttpTransport(Transport):
35 | """
36 | HTTP transport using urllib2. Provided basic http transport
37 | that provides for cookies, proxies but no authentication.
38 | """
39 |
40 | def __init__(self, **kwargs):
41 | """
42 | @param kwargs: Keyword arguments.
43 | - B{proxy} - An http proxy to be specified on requests.
44 | The proxy is defined as {protocol:proxy,}
45 | - type: I{dict}
46 | - default: {}
47 | - B{timeout} - Set the url open timeout (seconds).
48 | - type: I{float}
49 | - default: 90
50 | """
51 | Transport.__init__(self)
52 | Unskin(self.options).update(kwargs)
53 | self.cookiejar = CookieJar()
54 | self.proxy = {}
55 | self.urlopener = None
56 |
57 | def open(self, request):
58 | try:
59 | url = request.url
60 | log.debug('opening (%s)', url)
61 | u2request = urllib2.Request(url)
62 | self.proxy = self.options.proxy
63 | return self.u2open(u2request)
64 | except urllib2.HTTPError, e:
65 | raise TransportError(str(e), e.code, e.fp)
66 |
67 | def send(self, request):
68 | result = None
69 | url = request.url
70 | msg = request.message
71 | headers = request.headers
72 | try:
73 | u2request = urllib2.Request(url, msg, headers)
74 | self.addcookies(u2request)
75 | self.proxy = self.options.proxy
76 | request.headers.update(u2request.headers)
77 | log.debug('sending:\n%s', request)
78 | fp = self.u2open(u2request)
79 | self.getcookies(fp, u2request)
80 | headers = (fp.headers.dict if sys.version_info < (3, 0)
81 | else fp.headers)
82 | result = Reply(200, headers, fp.read())
83 | log.debug('received:\n%s', result)
84 | except urllib2.HTTPError, e:
85 | if e.code in (202,204):
86 | result = None
87 | else:
88 | raise TransportError(e.msg, e.code, e.fp)
89 | return result
90 |
91 | def addcookies(self, u2request):
92 | """
93 | Add cookies in the cookiejar to the request.
94 | @param u2request: A urllib2 request.
95 | @rtype: u2request: urllib2.Requet.
96 | """
97 | self.cookiejar.add_cookie_header(u2request)
98 |
99 | def getcookies(self, fp, u2request):
100 | """
101 | Add cookies in the request to the cookiejar.
102 | @param u2request: A urllib2 request.
103 | @rtype: u2request: urllib2.Requet.
104 | """
105 | self.cookiejar.extract_cookies(fp, u2request)
106 |
107 | def u2open(self, u2request):
108 | """
109 | Open a connection.
110 | @param u2request: A urllib2 request.
111 | @type u2request: urllib2.Requet.
112 | @return: The opened file-like urllib2 object.
113 | @rtype: fp
114 | """
115 | tm = self.options.timeout
116 | url = self.u2opener()
117 | if (sys.version_info < (3, 0)) and (self.u2ver() < 2.6):
118 | socket.setdefaulttimeout(tm)
119 | return url.open(u2request)
120 | else:
121 | return url.open(u2request, timeout=tm)
122 |
123 | def u2opener(self):
124 | """
125 | Create a urllib opener.
126 | @return: An opener.
127 | @rtype: I{OpenerDirector}
128 | """
129 | if self.urlopener is None:
130 | return urllib2.build_opener(*self.u2handlers())
131 | else:
132 | return self.urlopener
133 |
134 | def u2handlers(self):
135 | """
136 | Get a collection of urllib handlers.
137 | @return: A list of handlers to be installed in the opener.
138 | @rtype: [Handler,...]
139 | """
140 | handlers = []
141 | handlers.append(urllib2.ProxyHandler(self.proxy))
142 | return handlers
143 |
144 | def u2ver(self):
145 | """
146 | Get the major/minor version of the urllib2 lib.
147 | @return: The urllib2 version.
148 | @rtype: float
149 | """
150 | try:
151 | part = urllib2.__version__.split('.', 1)
152 | n = float('.'.join(part))
153 | return n
154 | except Exception, e:
155 | log.exception(e)
156 | return 0
157 |
158 | def __deepcopy__(self, memo={}):
159 | clone = self.__class__()
160 | p = Unskin(self.options)
161 | cp = Unskin(clone.options)
162 | cp.update(p)
163 | return clone
164 |
165 |
166 | class HttpAuthenticated(HttpTransport):
167 | """
168 | Provides basic http authentication for servers that don't follow
169 | the specified challenge / response model. This implementation
170 | appends the I{Authorization} http header with base64 encoded
171 | credentials on every http request.
172 | """
173 |
174 | def open(self, request):
175 | self.addcredentials(request)
176 | return HttpTransport.open(self, request)
177 |
178 | def send(self, request):
179 | self.addcredentials(request)
180 | return HttpTransport.send(self, request)
181 |
182 | def addcredentials(self, request):
183 | credentials = self.credentials()
184 | if not (None in credentials):
185 | encoded = base64.encodestring(':'.join(credentials))
186 | basic = 'Basic %s' % encoded[:-1]
187 | request.headers['Authorization'] = basic
188 |
189 | def credentials(self):
190 | return (self.options.username, self.options.password)
191 |
--------------------------------------------------------------------------------
/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 Namespace
24 | from suds.sax.element import Element
25 | from suds.plugin import DocumentPlugin, DocumentContext
26 |
27 | log = getLogger(__name__)
28 |
29 |
30 | class Doctor:
31 | """
32 | Schema Doctor.
33 | """
34 | def examine(self, root):
35 | """
36 | Examine and repair the schema (if necessary).
37 | @param root: A schema root element.
38 | @type root: L{Element}
39 | """
40 | pass
41 |
42 |
43 | class Practice(Doctor):
44 | """
45 | A collection of doctors.
46 | @ivar doctors: A list of doctors.
47 | @type doctors: list
48 | """
49 |
50 | def __init__(self):
51 | self.doctors = []
52 |
53 | def add(self, doctor):
54 | """
55 | Add a doctor to the practice
56 | @param doctor: A doctor to add.
57 | @type doctor: L{Doctor}
58 | """
59 | self.doctors.append(doctor)
60 |
61 | def examine(self, root):
62 | for d in self.doctors:
63 | d.examine(root)
64 | return root
65 |
66 |
67 | class TnsFilter:
68 | """
69 | Target Namespace filter.
70 | @ivar tns: A list of target namespaces.
71 | @type tns: [str,...]
72 | """
73 |
74 | def __init__(self, *tns):
75 | """
76 | @param tns: A list of target namespaces.
77 | @type tns: [str,...]
78 | """
79 | self.tns = []
80 | self.add(*tns)
81 |
82 | def add(self, *tns):
83 | """
84 | Add I{targetNamesapces} to be added.
85 | @param tns: A list of target namespaces.
86 | @type tns: [str,...]
87 | """
88 | self.tns += tns
89 |
90 | def match(self, root, ns):
91 | """
92 | Match by I{targetNamespace} excluding those that
93 | are equal to the specified namespace to prevent
94 | adding an import to itself.
95 | @param root: A schema root.
96 | @type root: L{Element}
97 | """
98 | tns = root.get('targetNamespace')
99 | if len(self.tns):
100 | matched = ( tns in self.tns )
101 | else:
102 | matched = 1
103 | itself = ( ns == tns )
104 | return ( matched and not itself )
105 |
106 |
107 | class Import:
108 | """
109 | An to be applied.
110 | @cvar xsdns: The XSD namespace.
111 | @type xsdns: (p,u)
112 | @ivar ns: An import namespace.
113 | @type ns: str
114 | @ivar location: An optional I{schemaLocation}.
115 | @type location: str
116 | @ivar filter: A filter used to restrict application to
117 | a particular schema.
118 | @type filter: L{TnsFilter}
119 | """
120 |
121 | xsdns = Namespace.xsdns
122 |
123 | def __init__(self, ns, location=None):
124 | """
125 | @param ns: An import namespace.
126 | @type ns: str
127 | @param location: An optional I{schemaLocation}.
128 | @type location: str
129 | """
130 | self.ns = ns
131 | self.location = location
132 | self.filter = TnsFilter()
133 |
134 | def setfilter(self, filter):
135 | """
136 | Set the filter.
137 | @param filter: A filter to set.
138 | @type filter: L{TnsFilter}
139 | """
140 | self.filter = filter
141 |
142 | def apply(self, root):
143 | """
144 | Apply the import (rule) to the specified schema.
145 | If the schema does not already contain an import for the
146 | I{namespace} specified here, it is added.
147 | @param root: A schema root.
148 | @type root: L{Element}
149 | """
150 | if not self.filter.match(root, self.ns):
151 | return
152 | if self.exists(root):
153 | return
154 | node = Element('import', ns=self.xsdns)
155 | node.set('namespace', self.ns)
156 | if self.location is not None:
157 | node.set('schemaLocation', self.location)
158 | log.debug('inserting: %s', node)
159 | root.insert(node)
160 |
161 | def add(self, root):
162 | """
163 | Add an to the specified schema root.
164 | @param root: A schema root.
165 | @type root: L{Element}
166 | """
167 | node = Element('import', ns=self.xsdns)
168 | node.set('namespace', self.ns)
169 | if self.location is not None:
170 | node.set('schemaLocation', self.location)
171 | log.debug('%s inserted', node)
172 | root.insert(node)
173 |
174 | def exists(self, root):
175 | """
176 | Check to see if the already exists
177 | in the specified schema root by matching I{namesapce}.
178 | @param root: A schema root.
179 | @type root: L{Element}
180 | """
181 | for node in root.children:
182 | if node.name != 'import':
183 | continue
184 | ns = node.get('namespace')
185 | if self.ns == ns:
186 | return 1
187 | return 0
188 |
189 |
190 | class ImportDoctor(Doctor, DocumentPlugin):
191 | """
192 | Doctor used to fix missing imports.
193 | @ivar imports: A list of imports to apply.
194 | @type imports: [L{Import},...]
195 | """
196 |
197 | def __init__(self, *imports):
198 | """
199 | """
200 | self.imports = []
201 | self.add(*imports)
202 |
203 | def add(self, *imports):
204 | """
205 | Add a namesapce to be checked.
206 | @param imports: A list of L{Import} objects.
207 | @type imports: [L{Import},..]
208 | """
209 | self.imports += imports
210 |
211 | def examine(self, node):
212 | for imp in self.imports:
213 | imp.apply(node)
214 |
215 | def parsed(self, context):
216 | node = context.document
217 | # xsd root
218 | if node.name == 'schema' and Namespace.xsd(node.namespace()):
219 | self.examine(node)
220 | return
221 | # look deeper
222 | context = DocumentContext()
223 | for child in node:
224 | context.document = child
225 | self.parsed(context)
226 |
--------------------------------------------------------------------------------
/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
209 |
--------------------------------------------------------------------------------
/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 os
20 | import os.path
21 | import sys
22 |
23 | import pkg_resources
24 | from setuptools import setup, find_packages
25 |
26 | # Setup documentation incorrectly states that it will search for packages
27 | # relative to the setup script folder by default when in fact it will search
28 | # for them relative to the current working folder. It seems avoiding this
29 | # problem cleanly and making the setup script runnable with any current working
30 | # folder would require better setup() support.
31 | # Attempted alternatives:
32 | # * Changing the current working folder internally makes any passed path
33 | # parameters be interpreted relative to the setup script folder when they
34 | # should be interpreted relative to the initial current working folder.
35 | # * Passing the script folder as setup() & find_packages() function
36 | # parameters makes the final installed distribution contain the absolute
37 | # package source location information and not include some other meta-data
38 | # package information as well.
39 | script_folder = os.path.abspath(os.path.dirname(__file__))
40 | current_folder = os.path.abspath(os.getcwd())
41 | if script_folder != current_folder:
42 | print("ERROR: Suds library setup script needs to be run from the folder "
43 | "containing it.")
44 | print()
45 | print("Current folder: {}".format(current_folder))
46 | print("Script folder: {}".format(script_folder))
47 | sys.exit(-2)
48 |
49 | # Load the suds library version information directly into this module without
50 | # having to import the whole suds library itself. Importing the suds package
51 | # would have caused problems like the following:
52 | # * Forcing the imported package module to be Python 3 compatible without any
53 | # lib2to3 fixers first being run on it (since such fixers get run only
54 | # later as a part of the setup procedure).
55 | # * Making the setup module depend on the package module's dependencies, thus
56 | # forcing the user to install them manually (since the setup procedure that
57 | # is supposed to install them automatically will not be able to run unless
58 | # they are already installed).
59 | exec(open(os.path.join("suds", "version.py"), "rt").read())
60 |
61 | extra = {}
62 | if sys.version_info >= (3,0):
63 | extra["use_2to3"] = True
64 |
65 | # Teach Python's urllib lib2to3 fixer that the old urllib2.__version__
66 | # data member is now stored in the urllib.request module.
67 | import lib2to3.fixes.fix_urllib
68 | for x in lib2to3.fixes.fix_urllib.MAPPING["urllib2"]:
69 | if x[0] == "urllib.request":
70 | x[1].append("__version__")
71 | break;
72 |
73 | # Wrap long_description at 72 characters since PKG-INFO package distribution
74 | # metadata file stores this text with an 8 space indentation.
75 | long_description = """
76 | ---------------------------------------
77 | Lightweight SOAP client (Jurko's fork).
78 | ---------------------------------------
79 |
80 | Based on the original 'suds' project by Jeff Ortel (jortel at redhat
81 | dot com) hosted at 'https://fedorahosted.org/suds'.
82 |
83 | 'Suds' is a lightweight SOAP-based web service client for Python licensed
84 | under LGPL (see the LICENSE.txt file included in the distribution).
85 |
86 | This is hopefully just a temporary fork of the original suds Python library
87 | project created because the original project development seems to have stalled.
88 | Should be reintegrated back into the original project if it ever gets revived
89 | again.
90 |
91 | """
92 |
93 | package_name = "suds-jurko"
94 | version_tag = pkg_resources.safe_version(__version__)
95 | project_url = "https://bitbucket.org/jurko/suds"
96 | base_download_url = project_url + "/downloads"
97 | download_distribution_name = "{}-{}.tar.bz2".format(package_name, version_tag)
98 | download_url = "{}/{}".format(base_download_url, download_distribution_name)
99 | packages_excluded_from_build = []
100 |
101 | # We generally do not want the tests package or any of its subpackages
102 | # included in our non-source package builds (source distribution content gets
103 | # specified separately via the MANIFEST.ini configuration file). Comment out
104 | # the following line to include the test code anyway, e.g. if you want to run
105 | # Python 3 based tests from the package build folder.
106 | packages_excluded_from_build += ["tests", "tests.*"]
107 |
108 | setup(
109 | name=package_name,
110 | version=__version__,
111 | description="Lightweight SOAP client (Jurko's fork)",
112 | long_description=long_description,
113 | keywords=["SOAP", "web", "service", "client"],
114 | url=project_url,
115 | download_url=download_url,
116 | obsoletes=["suds"],
117 | setup_requires=["distribute"],
118 | tests_require=["pytest"],
119 | packages=find_packages(exclude=packages_excluded_from_build),
120 |
121 | # 'maintainer' will be listed as the distribution package author.
122 | # Warning: Due to a 'distribute' package defect when used with Python 3
123 | # (verified using 'distribute' package version 0.6.25), given strings must
124 | # be given using ASCII characters only. This is needed because 'distribute'
125 | # stores the strings by doing a simple write to a PKG-INFO file opened as a
126 | # 'default text file' thus attempting to encode the given characters using
127 | # the user's default system code-page, e.g. typically CP1250 on eastern
128 | # European Windows, CP1252 on western European Windows, UTF-8 on Linux or
129 | # any other.
130 | author="Jeff Ortel",
131 | author_email="jortel@redhat.com",
132 | maintainer="Jurko Gospodnetic",
133 | maintainer_email="jurko.gospodnetic@pke.hr",
134 |
135 | # See PEP-301 for the classifier specification. For a complete list of
136 | # available classifiers see
137 | # 'http://pypi.python.org/pypi?%3Aaction=list_classifiers'.
138 | classifiers=["Development Status :: 5 - Production/Stable",
139 | "Intended Audience :: Developers",
140 | "License :: OSI Approved :: "
141 | "GNU Library or Lesser General Public License (LGPL)",
142 | "Natural Language :: English",
143 | "Operating System :: OS Independent",
144 | "Programming Language :: Python",
145 | "Programming Language :: Python :: 2",
146 | "Programming Language :: Python :: 2.4",
147 | "Programming Language :: Python :: 2.5",
148 | "Programming Language :: Python :: 2.6",
149 | "Programming Language :: Python :: 2.7",
150 | "Programming Language :: Python :: 3",
151 | "Programming Language :: Python :: 3.0",
152 | "Programming Language :: Python :: 3.1",
153 | "Programming Language :: Python :: 3.2",
154 | "Topic :: Internet"],
155 |
156 | # PEP-314 states that if possible license & plaform should be specified
157 | # using 'classifiers'.
158 | license="(specified using classifiers)",
159 | platforms=["(specified using classifiers)"],
160 |
161 | **extra
162 | )
163 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/suds/plugin.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 plugin module provides classes for implementation
19 | of suds plugins.
20 | """
21 |
22 | from suds import *
23 | from logging import getLogger
24 |
25 | log = getLogger(__name__)
26 |
27 |
28 | class Context(object):
29 | """
30 | Plugin context.
31 | """
32 | pass
33 |
34 |
35 | class InitContext(Context):
36 | """
37 | Init Context.
38 | @ivar wsdl: The wsdl.
39 | @type wsdl: L{wsdl.Definitions}
40 | """
41 | pass
42 |
43 |
44 | class DocumentContext(Context):
45 | """
46 | The XML document load context.
47 | @ivar url: The URL.
48 | @type url: str
49 | @ivar document: Either the XML text or the B{parsed} document root.
50 | @type document: (str|L{sax.element.Element})
51 | """
52 | pass
53 |
54 |
55 | class MessageContext(Context):
56 | """
57 | The context for sending the SOAP envelope.
58 | @ivar envelope: The SOAP envelope to be sent.
59 | @type envelope: (str|L{sax.element.Element})
60 | @ivar reply: The reply.
61 | @type reply: (str|L{sax.element.Element}|object)
62 | """
63 | pass
64 |
65 |
66 | class Plugin:
67 | """
68 | Plugin base.
69 | """
70 | pass
71 |
72 |
73 | class InitPlugin(Plugin):
74 | """
75 | The base class for suds I{init} plugins.
76 | """
77 |
78 | def initialized(self, context):
79 | """
80 | Suds client initialization.
81 | Called after wsdl the has been loaded. Provides the plugin
82 | with the opportunity to inspect/modify the WSDL.
83 | @param context: The init context.
84 | @type context: L{InitContext}
85 | """
86 | pass
87 |
88 |
89 | class DocumentPlugin(Plugin):
90 | """
91 | The base class for suds I{document} plugins.
92 | """
93 |
94 | def loaded(self, context):
95 | """
96 | Suds has loaded a WSDL/XSD document. Provides the plugin
97 | with an opportunity to inspect/modify the unparsed document.
98 | Called after each WSDL/XSD document is loaded.
99 | @param context: The document context.
100 | @type context: L{DocumentContext}
101 | """
102 | pass
103 |
104 | def parsed(self, context):
105 | """
106 | Suds has parsed a WSDL/XSD document. Provides the plugin
107 | with an opportunity to inspect/modify the parsed document.
108 | Called after each WSDL/XSD document is parsed.
109 | @param context: The document context.
110 | @type context: L{DocumentContext}
111 | """
112 | pass
113 |
114 |
115 | class MessagePlugin(Plugin):
116 | """
117 | The base class for suds I{SOAP message} plugins.
118 | """
119 |
120 | def marshalled(self, context):
121 | """
122 | Suds will send the specified soap envelope.
123 | Provides the plugin with the opportunity to inspect/modify
124 | the envelope Document before it is sent.
125 | @param context: The send context.
126 | The I{envelope} is the envelope document.
127 | @type context: L{MessageContext}
128 | """
129 | pass
130 |
131 | def sending(self, context):
132 | """
133 | Suds will send the specified SOAP envelope.
134 | Provides the plugin with the opportunity to inspect/modify
135 | the message text it is sent.
136 | @param context: The send context.
137 | The I{envelope} is the envelope text.
138 | @type context: L{MessageContext}
139 | """
140 | pass
141 |
142 | def received(self, context):
143 | """
144 | Suds has received the specified reply.
145 | Provides the plugin with the opportunity to inspect/modify
146 | the received XML text before it is SAX parsed.
147 | @param context: The reply context.
148 | The I{reply} is the raw text.
149 | @type context: L{MessageContext}
150 | """
151 | pass
152 |
153 | def parsed(self, context):
154 | """
155 | Suds has sax parsed the received reply.
156 | Provides the plugin with the opportunity to inspect/modify
157 | the sax parsed DOM tree for the reply before it is unmarshalled.
158 | @param context: The reply context.
159 | The I{reply} is DOM tree.
160 | @type context: L{MessageContext}
161 | """
162 | pass
163 |
164 | def unmarshalled(self, context):
165 | """
166 | Suds has unmarshalled the received reply.
167 | Provides the plugin with the opportunity to inspect/modify
168 | the unmarshalled reply object before it is returned.
169 | @param context: The reply context.
170 | The I{reply} is unmarshalled suds object.
171 | @type context: L{MessageContext}
172 | """
173 | pass
174 |
175 |
176 | class PluginContainer:
177 | """
178 | Plugin container provides easy method invocation.
179 | @ivar plugins: A list of plugin objects.
180 | @type plugins: [L{Plugin},]
181 | @cvar ctxclass: A dict of plugin method / context classes.
182 | @type ctxclass: dict
183 | """
184 |
185 | domains = {\
186 | 'init': (InitContext, InitPlugin),
187 | 'document': (DocumentContext, DocumentPlugin),
188 | 'message': (MessageContext, MessagePlugin ),
189 | }
190 |
191 | def __init__(self, plugins):
192 | """
193 | @param plugins: A list of plugin objects.
194 | @type plugins: [L{Plugin},]
195 | """
196 | self.plugins = plugins
197 |
198 | def __getattr__(self, name):
199 | domain = self.domains.get(name)
200 | if domain:
201 | plugins = []
202 | ctx, pclass = domain
203 | for p in self.plugins:
204 | if isinstance(p, pclass):
205 | plugins.append(p)
206 | return PluginDomain(ctx, plugins)
207 | else:
208 | raise Exception, 'plugin domain (%s), invalid' % name
209 |
210 |
211 | class PluginDomain:
212 | """
213 | The plugin domain.
214 | @ivar ctx: A context.
215 | @type ctx: L{Context}
216 | @ivar plugins: A list of plugins (targets).
217 | @type plugins: list
218 | """
219 |
220 | def __init__(self, ctx, plugins):
221 | self.ctx = ctx
222 | self.plugins = plugins
223 |
224 | def __getattr__(self, name):
225 | return Method(name, self)
226 |
227 |
228 | class Method:
229 | """
230 | Plugin method.
231 | @ivar name: The method name.
232 | @type name: str
233 | @ivar domain: The plugin domain.
234 | @type domain: L{PluginDomain}
235 | """
236 |
237 | def __init__(self, name, domain):
238 | """
239 | @param name: The method name.
240 | @type name: str
241 | @param domain: A plugin domain.
242 | @type domain: L{PluginDomain}
243 | """
244 | self.name = name
245 | self.domain = domain
246 |
247 | def __call__(self, **kwargs):
248 | ctx = self.domain.ctx()
249 | ctx.__dict__.update(kwargs)
250 | for plugin in self.domain.plugins:
251 | try:
252 | method = getattr(plugin, self.name, None)
253 | if method and callable(method):
254 | method(ctx)
255 | except Exception, pe:
256 | log.exception(pe)
257 | return ctx
258 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------