├── .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 &lt; 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 | --------------------------------------------------------------------------------