zeep-2.5.0/0000755000076500000240000000000013224162010014436 5ustar mvantellingenstaff00000000000000zeep-2.5.0/CHANGES0000644000076500000240000005403013224161177015450 0ustar mvantellingenstaff000000000000002.5.0 (2018-01-06) ------------------ - Fix AnyType value rendering by guessing the xsd type for the value (#552) - Fix AnySimpleType.xmlvalue() not implemented exception (#644) - Add __dir__ method to value objects returned by Zeep - Don't require content for 201 and 202 status codes (#613) - Fix wheel package by cleaning the build directory correctly (#634) - Handle Nil values on complexType with SimpleContent elements (#604) - Add Client.namespaces method to list all namespaces available - Improve support for auto-completion (#537) 2.4.0 (2017-08-26) ------------------ - Add support for tornado async transport via gen.coroutine (#530, Kateryna Burda) - Check if soap:address is defined in the service port instead of raising an exception (#527) - Update packaging (stop using find_packages()) (#529) - Properly handle None values when rendering complex types (#526) - Fix generating signature for empty wsdl messages (#542) - Support passing strings to xsd:Time objects (#540) 2.3.0 (2017-08-06) ------------------ - The XML send to the server is no longer using ``pretty_print=True`` (#484) - Refactor of the multiref support to fix issues with child elements (#489) - Add workaround to support negative durations (#486) - Fix creating XML documents for operations without aguments (#479) - Fix xsd:extension on xsd:group elements (#523) 2.2.0 (2017-06-19) ------------------ - Automatically import the soap-encoding schema if it is required (#473) - Add support for XOP messages (this is a rewrite of #325 by vashek) 2.1.1 (2017-06-11) ------------------ - Fix previous release, it contained an incorrect dependency (Mock 2.1.) due to bumpversion :-( 2.1.0 (2017-06-11) ------------------ - Fix recursion error while creating the signature for a global element when it references itself (via ref attribute). - Update Client.create_message() to apply plugins and wsse (#465) - Fix handling unknown xsi types when parsing elements using xsd:anyType (#455) 2.0.0 (2017-05-22) ------------------ This is a major release, and contains a number of backwards incompatible changes to the API. - Default values of optional elements are not set by default anymore (#423) - Refactor the implementation of wsdl:arrayType too make the API more pythonic (backwards incompatible). - The call signature for Client.create_message() was changed. It now requires the service argument: ``Client.create_message(service, operation_name, *args, **kwargs)`` - Choice elements now only work with keyword arguments and raise an exception if positional arguments are passed (#439) - Implement initial multiref support for SOAP RPC (#326). This was done using really good real-world tests from vstoykov (thanks!) - Fix exception on empty SOAP response (#442) - Fix XSD default values for boolean types (Bartek Wójcicki, #386) 1.6.0 (2017-04-27) ------------------ - Implement ValueObject.__json__ for json serialization (#258) - Improve handling of unexpected elements for soap:header (#378) - Accept unexpected elements in complexTypes when strict is False - Fix elementFormDefault/attributeFormDefault for xsd:includes (#426) 1.5.0 (2017-04-22) ------------------ - Fix issue where values of indicators (sequence/choice/all) would write to the same internal dict. (#425) - Set default XML parse mode to strict as was intended (#332) - Add support for pickling value objects (#417) - Add explicit Nil value via ``zeep.xsd.Nil`` (#424) - Add xml_huge_tree kwarg to the Client() to enable lxml's huge_tree mode, this is disabled by default (#332) - Add support to pass base-types to type extensions (#416) - Handle wsdl errors more gracefully by disabling the invalid operations instead of raising an exception (#407, #387) 1.4.1 (2017-04-01) ------------------ - The previous release (1.4.0) contained an incorrect dependency due to bumpversion moving all 1.3.0 versions to 1.4.0. This fixes it. 1.4.0 (2017-04-01) ------------------ - Hardcode the xml prefix to the xml namespace as defined in the specs (#367) - Fix parsing of unbound sequences within xsd choices (#380) - Use logger.debug() for debug related logging (#369) - Add the ``Client.raw_response`` option to let zeep return the raw transport response (requests.Response) instead of trying to parse it. - Handle minOccurs/maxOccurs properlhy for xsd:Group elements. This also fixes a bug in the xsd:Choice handling for multiple elements (#374, #410) - Fix raising XMLSyntaxError when loading invalid XML (Antanas Sinica, #396) 1.3.0 (2017-03-14) ------------------ - Add support for nested xsd:choice elements (#370) - Fix unresolved elements for xsd:extension, this was a regression introduced in 1.2.0 (#377) 1.2.0 (2017-03-12) ------------------ - Add flag to disable strict mode in the Client. This allows zeep to better work with non standard compliant SOAP Servers. See the documentation for usage and potential downsides. - Minor refactor of resolving of elements for improved performance - Support the SOAP 1.2 'http://www.w3.org/2003/05/soap/bindings/HTTP/' transport uri (#355) - Fallback to matching wsdl lookups to matching when the target namespace is empty (#356) - Improve the handling of xsd:includes, the default namespace of the parent schema is now also used during resolving of the included schema. (#360) - Properly propagate the global flag for complex types when an xsd:restriction is used (#360) - Filter out duplicate types and elements when dump the wsdl schema (#360) - Add ``zeep.CachingClient()`` which enables the SqliteCache by default 1.1.0 (2017-02-18) ------------------ - Fix an attribute error when an complexType used xsd:anyType as base restriction (#352) - Update asyncio transport to return requests.Response objects (#335) 1.0.0 (2017-01-31) ------------------ - Use cgi.parse_header() to extract media_type for multipart/related checks (#327) - Don't ignore nil elements, instead return None when parsing xml (#328) - Fix regression when using WSA with an older lxml version (#197) 0.27.0 (2017-01-28) ------------------- - Add support for SOAP attachments (multipart responses). (Dave Wapstra, #302) - Update xsd:anyType to return the xml elements when no type is given via the xsi:type attribute (#284) - Fix parsing Any elements when a restriction is used (soap-enc:array) (#322) 0.26.0 (2017-01-26) ------------------- This release again introduces some backwords incompatibilties. The next release will hopefully be 1.0 which will introduce semver. - **backwards-incompatible**: The Transport class now accepts a ``requests.Session()`` object instead of ``http_auth`` and ``verify``. This allows for more flexibility. - **backwards-incompatible**: Zeep no longer sets a default cache backend. Please see http://docs.python-zeep.org/en/master/transport.html#caching for information about how to configure a cache. - Add ``zeep.xsd.SkipValue`` which instructs the serialize to ignore the element. - Support duplicate target namespaces in the wsdl definition (#320) - Fix resolving element/types for xsd schema's with duplicate tns (#319) 0.25.0 (2017-01-23) ------------------- - **Important:** Add basic validation against the xsd. It currently will only validate the minOccurs/maxOccurs but this will be extended in the future. - Add support for duplicate namespace definitions. Previously imports for namespaces which were already imported were ignored. It will now search through all matching schemas with the tns to find a specific object (#204) - Fix xsd:extension for sequence -> choice. (#257) - Improve serializing attributes when the values were passed as a dict (#125) 0.24.0 (2016-12-16) ------------------- - Don't fail the parsing of responses if an xsi:type references an non-existing type. Instead log a message (#273) - Fix serializing etree.Element instances in the helpers.serialize function (#255) - Fix infinite loop during parsing of xsd.Sequence where max_occurs is unbounded (#256) - Make the xsd.Element name kwarg required - Improve handling of the xsd:anyType element when passed instances of complexType's (#252) - Silently ignore unsupported binding transports instead of an hard error (#277) - Support microseconds for xsd.dateTime and xsd.Time (#280) - Don't mutate passed values to the zeep operations (#280) 0.23.0 (2016-11-24) ------------------- - Add Client.set_default_soapheaders() to set soapheaders which are to be used on all operations done via the client object. - Add basic support for asyncio using aiohttp. Many thanks to chrisimcevoy for the initial implementation! Please see https://github.com/mvantellingen/python-zeep/pull/207 and https://github.com/mvantellingen/python-zeep/pull/251 for more information - Fix recursion error when generating the call signature (jaceksnet, #264) 0.22.1 (2016-11-22) ------------------- - Fix reversed() error (jaceksnet) (#260) - Better error message when unexpected xml elements are encountered in sequences. 0.22.0 (2016-11-13) ------------------- - Force the soap:address / http:address to HTTPS when the wsdl is loaded from a https url (#228) - Improvements to the xsd:union handling. The matching base class is now used for serializing/deserializing the values. If there is no matching base class then the raw value is returned. (#195) - Fix handling of xsd:any with maxOccurs > 1 in xsd:choice elements (#253) - Add workaround for schema's importing the xsd from http://www.w3.org/XML/1998/namespace (#220) - Add new Client.type_factory(namespace) method which returns a factory to simplify creation of types. 0.21.0 (2016-11-02) ------------------- - Don't error on empty xml namespaces declarations in inline schema's (#186) - Wrap importing of sqlite3 in try..except for Google App Engine (#243) - Don't use pkg_resources to determine the zeep version, use __version__ instead (#243). - Fix SOAP arrays by wrapping children in the appropriate element (joeribekker, #236) - Add ``operation_timeout`` kwarg to the Transport class to set timeouts for operations. The default is still no timeout (#140) - Introduce client.options context manager to temporarily override various options (only timeout for now) (#140) - Wrap the parsing of xml values in a try..except block and log an error instead of throwing an exception (#137) - Fix xsd:choice xml rendering with nested choice/sequence structure (#221) - Correctly resolve header elements of which the message part defines the type instead of element. (#199) 0.20.0 (2016-10-24) ------------------- - Major performance improvements / lower memory usage. Zeep now no longer copies data and alters it in place but instead uses a set to keep track of modified data. - Fix parsing empty soap response (#223) - Major refactor of the xsd:extension / xsd:restriction implementation. - Better support for xsd:anyType, by re-using the xsd.AnyObject (#229) - Deserialize SOAP response without message elements correctly (#237) 0.19.0 (2016-10-18) ------------------- - **backwards-incompatible**: If the WSDL defines that the endpoint returns soap:header elements and/or multple soap:body messages then the return signature of the operation is changed. You can now explcitly access the body and header elements. - Fix parsing HTTP bindings when there are no message elements (#185) - Fix deserializing RPC responses (#219 - Add support for SOAP 1.2 Fault subcodes (#210, vashek) - Don't alter the _soapheaders elements during rendering, instead create a deepcopy first. (#188) - Add the SOAPAction to the Content-Type header in SOAP 1.2 bindings (#211) - Fix issue when mixing elements and any elements in a choice type (#192) - Improving parsing of results for union types (#192) - Make ws-addressing work with lxml < 3.5 (#209) - Fix recursion error when xsi:type='anyType' is given. (#198) 0.18.1 (2016-09-23) ------------------- - PyPi release error 0.18.0 (2016-09-23) ------------------- - Fix parsing Any elements by using the namespace map of the response node instead of the namespace map of the wsdl. (#184, #164) - Improve handling of nested choice elements (choice>sequence>choice) 0.17.0 (2016-09-12) ------------------- - Add support for xsd:notation (#183) - Add improvements to resolving phase so that all objects are resolved. - Improve implementation of xsd.attributeGroup and xsd.UniqueType - Create a deepcopy of the args and kwargs passed to objects so that the original are unmodified. - Improve handling of wsdl:arrayType 0.16.0 (2016-09-06) ------------------- - Fix error when rendering choice elements with have sequences as children, see #150 - Re-use credentials passed to python -mzeep (#130) - Workaround invalid usage of qualified vs non-qualified element tags in the response handling (#176) - Fix regression when importing xsd:schema's via wsdl:import statements (#179) 0.15.0 (2016-09-04) ------------------- - All wsdl documents and xsd schemas are now globally available for eachother. While this is not correct according to the (messy) soap specifications, it does make zeep more compatible with all the invalid wsdl documents out there. (#159) - Implement support for attributeGroup (#160) - Add experimental support for ws-addressing (#92) - Fix handling of Mime messages with no parts (#168) - Workaround an issue where soap servers don't qualify references (#170) - Correctly process attributes which are passed as a dictionary. (#125) - Add support for plugins, see documentation for examples. - Fix helpers.serialize_object for lists of objects (#123). - Add HistoryPlugin which ofers last_sent and last_received properties (#93). 0.14.0 (2016-08-03) ------------------- - Global attributes are now always correctly handled as qualified. (#129) - Fix parsing xml data containing simpleContent types (#136). - Set xsi:nil attribute when serializing objects to xml (#141) - Fix rendering choice elements when the element is mixed with other elements in a sequence (#150) - Fix maximum recursion error for recursive xsd:include elements - Make wsdl:import statements transitive. (#149) - Merge xsd:schema's which are spread around imported wsdl objects. (#146) - Don't raise exception when no value is given for AnyAttribute (#152) 0.13.0 (2016-07-17) ------------------- - Use warnings.warn() for duplicate target namespaces instead of raising an exception. This better matches with what lxml does. - **backwards-incompatible**: The ``persistent`` kwarg is removed from the SqliteCache.__init__() call. Use the new InMemoryCache() instead when you don't want to persist data. This was required to make the SqliteCache backend thread-safe since we now open/close the db when writing/reading from it (with an additional lock). - Fix zeep.helpers.serialize_object() for nested objects (#123) - Remove fallback between soap 1.1 and soap 1.2 namespaces during the parsing of the wsdl. This should not be required. 0.12.0 (2016-07-09) ------------------- - **backwards-incompatible**: Choice elements are now unwrapped if maxOccurs=1. This results in easier operation definitions when choices are used. - **backwards-incompatible**: The _soapheader kwarg is renamed to _soapheaders and now requires a nested dictionary with the header name as key or a list of values (value object or lxml.etree.Element object). Please see the call signature of the function using ``python -mzeep ``. - Support the element ref's to xsd:schema elements. - Improve the signature() output of element and type definitions - Accept lxml.etree.Element objects as value for Any elements. - And various other fixes 0.11.0 (2016-07-03) ------------------- - **backwards-incompatible**: The kwarg name for Any and Choice elements are renamed to generic ``_value_N`` names. - **backwards-incompatible**: Client.set_address() is replaced with the Client.create_service() call - Auto-load the http://schemas.xmlsoap.org/soap/encoding/ schema if it is referenced but not imported. Too many XSD's assume that the schema is always available. - Major refactoring of the XSD handling to correctly support nested xsd:sequence elements. - Add ``logger.debug()`` calls around Transport.post() to allow capturing the content send/received from the server - Add proper support for default values on attributes and elements. 0.10.0 (2016-06-22) ------------------- - Make global elements / types truly global by refactoring the Schema parsing. Previously the lookups where non-transitive, but this should only be the case during parsing of the xml schema. - Properly unwrap XML responses in soap.DocumentMessage when a choice is the root element. (#80) - Update exceptions structure, all zeep exceptions are now using zeep.exceptions.Error() as base class. 0.9.1 (2016-06-17) ------------------ - Quote the SOAPAction header value (Derek Harland) - Undo fallback for SOAPAction if it is empty (#83) 0.9.0 (2016-06-14) ------------------ - Use the appdirs module to retrieve the OS cache path. Note that this results in an other default cache path then previous releases! See https://github.com/ActiveState/appdirs for more information. - Fix regression when initializing soap objects with invalid kwargs. - Update wsse.UsernameToken to set encoding type on nonce (Antonio Cuni) - Remove assert statement in soap error handling (Eric Waller) - Add '--no-verify' to the command line interface. (#63) - Correctly xsi:type attributes on unbounded elements. (nicholjy) (#68) - Re-implement xsd:list handling - Refactor logic to open files from filesystem. - Refactor the xsd:choice implementation (serializing/deserializing) - Implement parsing of xsd:any elements. 0.8.1 (2016-06-08) ------------------ - Use the operation name for the xml element which wraps the parameters in for soap RPC messages (#60) 0.8.0 (2016-06-07) ------------------ - Add ability to override the soap endpoint via `Client.set_address()` - Fix parsing ComplexTypes which have no child elements (#50) - Handle xsi:type attributes on anyType's correctly when deserializing responses (#17) - Fix xsd:restriction on xsd:simpleType's when the base type wasn't defined yet. (#59) - Add xml declaration to the generate xml strings (#60) - Fix xsd:import statements without schemaLocation (#58) 0.7.1 (2016-06-01) ------------------ - Fix regression with handling wsdl:import statements for messages (#47) 0.7.0 (2016-05-31) ------------------ - Add support HTTP authentication (mcordes). This adds a new attribute to the Transport client() which passes the http_auth value to requests. (#31) - Fix issue where setting cache=None to Transport class didn't disable caching. - Refactor handling of wsdl:imports, don't merge definitions but instead lookup values in child definitions. (#40) - Remove unused namespace declarations from the generated SOAP messages. - Update requirement of six>=1.0.0 to six>=1.9.0 (#39) - Fix handling of xsd:choice, xsd:group and xsd:attribute (#30) - Improve error messages - Fix generating soap messages when sub types are used via xsd extensions (#36) - Improve handling of custom soap headers (#33) 0.6.0 (2016-05-21) ------------------ - Add missing `name` attributes to xsd.QName and xsd.NOTATION (#15) - Various fixes related to the Choice element - Support xsd:include - Experimental support for HTTP bindings - Removed `Client.get_port()`, use `Client.bind()`. 0.5.0 (2015-05-08) ------------------ - Handle attributes during parsing of the response values> - Don't create empty soap objects when the root element is empty. - Implement support for WSSE usernameToken profile including passwordText/passwordDigest. - Improve XSD date/time related builtins. - Various minor XSD handling fixes - Use the correct soap-envelope XML namespace for the Soap 1.2 binding - Use `application/soap+xml` as content-type in the Soap 1.2 binding - **backwards incompatible**: Make cache part of the transport object instead of the client. This changes the call signature of the Client() class. (Marek Wywiał) - Add the `verify` kwarg to the Transport object to disable ssl certificate verification. (Marek Wywiał) 0.4.0 (2016-04-17) ------------------ - Add defusedxml module for XML security issues - Add support for choice elements - Fix documentation example for complex types (Falk Schuetzenmeister) 0.3.0 (2016-04-10) ------------------ - Correctly handle recursion in WSDL and XSD files - Add support for the XSD Any element - Allow usage of shorthand prefixes when creating elements and types - And more various improvements 0.2.5 (2016-04-05) ------------------ - Temporarily disable the HTTP binding support until it works properly - Fix an issue with parsing SOAP responses with optional elements 0.2.4 (2016-04-03) ------------------ - Improve xsd.DateTime, xsd.Date and xsd.Time implementations by using the isodate module. - Implement xsd.Duration 0.2.3 (2016-04-03) ------------------ - Fix xsd.DateTime, xsd.Date and xsd.Time implementations - Handle NIL values correctly for simpletypes 0.2.2 (2016-04-03) ------------------ - Fix issue with initializing value objects (ListElements) - Add new `zeep.helpers.serialize_object()` method - Rename type attribute on value objects to `_xsd_type` to remove potential attribute conflicts 0.2.1 (2016-04-03) ------------------ - Support minOccurs 0 (optional elements) - Automatically convert python datastructures to zeep objects for requests. - Set default values for new zeep objects to None / [] (Element, ListElement) - Add `Client.get_element()` to create custom objects 0.2.0 (2016-04-03) ------------------ - Proper support for XSD element and attribute forms (qualified/unqualified) - Improved XSD handling - Separate bindings for Soap 1.1 and Soap 1.2 - And again various other fixes 0.1.1 (2016-03-20) ------------------ - Various fixes to make the HttpBinding not throw errors during parsing - More built-in xsd types - Add support for `python -mzeep ` - Various other fixes 0.1.0 (2016-03-20) ------------------ Preview / Proof-of-concept release. Probably not suitable for production use :) zeep-2.5.0/CONTRIBUTORS.rst0000644000076500000240000000141013224161177017136 0ustar mvantellingenstaff00000000000000Authors ======= * Michael van Tellingen Contributors ============ * Kateryna Burda * Alexey Stepanov * Marco Vellinga * jaceksnet * Andrew Serong * vashek * Seppo Yli-Olli * Sam Denton * Dani Möller * Julien Delasoie * Christian González * bjarnagin * mcordes * Joeri Bekker * Bartek Wójcicki * jhorman * fiebiga * David Baumgold * Antonio Cuni * Alexandre de Mari * Nicolas Evrard * Eric Wong * Jason Vertrees * Falldog * Matt Grimm (mgrimm) * Marek Wywiał * btmanm * Caleb Salt * Ondřej Lanč * Jan Murre * Stefano Parmesan * Julien Marechal * Dave Wapstra * Mike Fiedler * Derek Harland * Bruno Duyé * Christoph Heuel * Ben Tucker * Eric Waller * Falk Schuetzenmeister * Jon Jenkins * OrangGeeGee * Raymond Piller * Zoltan Benedek * Øyvind Heddeland Instefjord zeep-2.5.0/examples/0000755000076500000240000000000013224162010016254 5ustar mvantellingenstaff00000000000000zeep-2.5.0/examples/async_client.py0000644000076500000240000000265113224161177021322 0ustar mvantellingenstaff00000000000000import asyncio import time import zeep from zeep.asyncio import AsyncTransport def run_async(): print("async example") print("=============") result = [] def handle_future(future): result.extend(future.result()) loop = asyncio.get_event_loop() transport = AsyncTransport(loop, cache=None) client = zeep.Client('http://localhost:8000/?wsdl', transport=transport) tasks = [ client.service.slow_request('request-1'), # takes 1 sec client.service.slow_request('request-2'), # takes 1 sec ] future = asyncio.gather(*tasks, return_exceptions=True) result = [] future.add_done_callback(handle_future) st = time.time() loop.run_until_complete(future) loop.run_until_complete(transport.session.close()) print("time: %.2f" % (time.time() - st)) print("result: %s", result) print("") return result def run_sync(): print("sync example") print("============") transport = zeep.Transport(cache=None) client = zeep.Client('http://localhost:8000/?wsdl', transport=transport) st = time.time() result = [ client.service.slow_request('request-1'), # takes 1 sec client.service.slow_request('request-2'), # takes 1 sec ] print("Time: %.2f" % (time.time() - st)) print("result: %s", result) print("\n") return result if __name__ == '__main__': print("") run_async() run_sync() zeep-2.5.0/examples/code39.py0000644000076500000240000000035113224161177017730 0ustar mvantellingenstaff00000000000000from __future__ import print_function import zeep client = zeep.Client( wsdl='http://www.webservicex.net/barcode.asmx?WSDL') response = client.service.Code39('1234', 20, ShowCodeString=True, Title='ZEEP') print(repr(response)) zeep-2.5.0/examples/echo_services.py0000644000076500000240000000025313224161177021464 0ustar mvantellingenstaff00000000000000from zeep.client import Client # RPC style soap service client = Client('http://www.soapclient.com/xml/soapresponder.wsdl') print(client.service.Method1('zeep', 'soap')) zeep-2.5.0/examples/eu_vat_service.py0000644000076500000240000000031213224161177021642 0ustar mvantellingenstaff00000000000000from __future__ import print_function import zeep client = zeep.Client( wsdl='http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl') print(client.service.checkVat('NL', '123456789B01')) zeep-2.5.0/examples/http_basic_auth.py0000644000076500000240000000072113224161177022004 0ustar mvantellingenstaff00000000000000from __future__ import print_function from requests import Session from requests.auth import HTTPBasicAuth import zeep from zeep.transports import Transport # Example using basic authentication with a webservice session = Session() session.auth = HTTPBasicAuth('username', 'password') transport_with_basic_auth = Transport(session=session) client = zeep.Client( wsdl='http://nonexistent?WSDL', transport=transport_with_basic_auth ) client.wsdl.dump() zeep-2.5.0/examples/km_to_miles.py0000644000076500000240000000077613224161177021157 0ustar mvantellingenstaff00000000000000from __future__ import print_function import zeep client = zeep.Client( wsdl='http://www.webservicex.net/ConvertSpeed.asmx?WSDL') client.wsdl.dump() print (client.service.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour')) http_get = client.bind('ConvertSpeeds', 'ConvertSpeedsHttpGet') http_post = client.bind('ConvertSpeeds', 'ConvertSpeedsHttpPost') print(http_get.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour')) print(http_post.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour')) zeep-2.5.0/examples/soap_server.py0000644000076500000240000000217313224161177021176 0ustar mvantellingenstaff00000000000000""" Example soap server using spyne. Run with uwsgi --http :8000 \ --wsgi-file soap_server.py \ --virtualenv ~/.pyenv/versions/3.5.2/envs/zeep \ -p 10 """ import time from spyne import Application, ServiceBase, Unicode, rpc from spyne.protocol.soap import Soap11 from spyne.server.wsgi import WsgiApplication class ExampleService(ServiceBase): @rpc(Unicode, _returns=Unicode) def slow_request(ctx, request_id): time.sleep(1) return u'Request: %s' % request_id application = Application( services=[ExampleService], tns='http://tests.python-zeep.org/', in_protocol=Soap11(validator='lxml'), out_protocol=Soap11()) application = WsgiApplication(application) if __name__ == '__main__': import logging from wsgiref.simple_server import make_server logging.basicConfig(level=logging.DEBUG) logging.getLogger('spyne.protocol.xml').setLevel(logging.DEBUG) logging.info("listening to http://127.0.0.1:8000") logging.info("wsdl is at: http://localhost:8000/?wsdl") server = make_server('127.0.0.1', 8000, application) server.serve_forever() zeep-2.5.0/LICENSE0000644000076500000240000001055213224161177015463 0ustar mvantellingenstaff00000000000000The MIT License (MIT) Copyright (c) 2016-2017 Michael van Tellingen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -- Parts of the XSD handling are heavily inspired by soapfish, see: https://github.com/FlightDataServices/soapfish Copyright (c) 2011-2014, soapfish contributors All rights reserved. For the exact contribution history, see the git revision log. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- The support for BinarySecurityToken is from py-wsse, see: https://github.com/orcasgit/py-wsse Copyright (c) 2015 ORCAS, Inc Some portions from py-soap-wsse (c) Michael van Tellingen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. zeep-2.5.0/MANIFEST.in0000644000076500000240000000046713224161177016220 0ustar mvantellingenstaff00000000000000# Exclude everything by default exclude * recursive-exclude * * include MANIFEST.in include CHANGES include CONTRIBUTORS.rst include LICENSE include README.rst include setup.cfg include setup.py graft examples graft src graft tests global-exclude __pycache__ global-exclude *.py[co] global-exclude .DS_Store zeep-2.5.0/PKG-INFO0000644000076500000240000000527413224162010015543 0ustar mvantellingenstaff00000000000000Metadata-Version: 1.1 Name: zeep Version: 2.5.0 Summary: A modern/fast Python SOAP client based on lxml / requests Home-page: http://docs.python-zeep.org Author: Michael van Tellingen Author-email: michaelvantellingen@gmail.com License: MIT Description: ======================== Zeep: Python SOAP client ======================== A fast and modern Python SOAP client Highlights: * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6 and PyPy * Build on top of lxml and requests * Support for Soap 1.1, Soap 1.2 and HTTP bindings * Support for WS-Addressing headers * Support for WSSE (UserNameToken / x.509 signing) * Support for tornado async transport via gen.coroutine (Python 2.7+) * Support for asyncio via aiohttp (Python 3.5+) * Experimental support for XOP messages Please see for more information the documentation at http://docs.python-zeep.org/ Installation ------------ .. code-block:: bash pip install zeep Usage ----- .. code-block:: python from zeep import Client client = Client('tests/wsdl_files/example.rst') client.service.ping() To quickly inspect a WSDL file use:: python -m zeep Please see the documentation at http://docs.python-zeep.org for more information. Support ======= If you want to report a bug then please first read http://docs.python-zeep.org/en/master/reporting_bugs.html I'm also able to offer commercial support. As in contracting work. Please contact me at info@mvantellingen.nl for more information. Note that asking questions or reporting bugs via this e-mail address will be ignored. Pleae use the appropriate channels for that (e.g. stackoverflow) Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy zeep-2.5.0/README.rst0000644000076500000240000000444413224161177016150 0ustar mvantellingenstaff00000000000000======================== Zeep: Python SOAP client ======================== A fast and modern Python SOAP client Highlights: * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6 and PyPy * Build on top of lxml and requests * Support for Soap 1.1, Soap 1.2 and HTTP bindings * Support for WS-Addressing headers * Support for WSSE (UserNameToken / x.509 signing) * Support for tornado async transport via gen.coroutine (Python 2.7+) * Support for asyncio via aiohttp (Python 3.5+) * Experimental support for XOP messages Please see for more information the documentation at http://docs.python-zeep.org/ .. start-no-pypi Status ------ .. image:: https://readthedocs.org/projects/python-zeep/badge/?version=latest :target: https://readthedocs.org/projects/python-zeep/ .. image:: https://travis-ci.org/mvantellingen/python-zeep.svg?branch=master :target: https://travis-ci.org/mvantellingen/python-zeep .. image:: https://ci.appveyor.com/api/projects/status/im609ng9h29vt89r?svg=true :target: https://ci.appveyor.com/project/mvantellingen/python-zeep .. image:: http://codecov.io/github/mvantellingen/python-zeep/coverage.svg?branch=master :target: http://codecov.io/github/mvantellingen/python-zeep?branch=master .. image:: https://img.shields.io/pypi/v/zeep.svg :target: https://pypi.python.org/pypi/zeep/ .. image:: https://requires.io/github/mvantellingen/python-zeep/requirements.svg?branch=master :target: https://requires.io/github/mvantellingen/python-zeep/requirements/?branch=master .. end-no-pypi Installation ------------ .. code-block:: bash pip install zeep Usage ----- .. code-block:: python from zeep import Client client = Client('tests/wsdl_files/example.rst') client.service.ping() To quickly inspect a WSDL file use:: python -m zeep Please see the documentation at http://docs.python-zeep.org for more information. Support ======= If you want to report a bug then please first read http://docs.python-zeep.org/en/master/reporting_bugs.html I'm also able to offer commercial support. As in contracting work. Please contact me at info@mvantellingen.nl for more information. Note that asking questions or reporting bugs via this e-mail address will be ignored. Pleae use the appropriate channels for that (e.g. stackoverflow) zeep-2.5.0/setup.cfg0000644000076500000240000000110013224162010016247 0ustar mvantellingenstaff00000000000000[bumpversion] current_version = 2.5.0 commit = true tag = true tag_name = {new_version} [tool:pytest] minversion = 3.0 strict = true testpaths = tests [wheel] universal = 1 [flake8] max-line-length = 99 [bumpversion:file:setup.py] [bumpversion:file:docs/conf.py] [bumpversion:file:docs/index.rst] [bumpversion:file:src/zeep/__init__.py] [coverage:run] branch = True source = zeep [coverage:paths] source = src/zeep .tox/*/lib/python*/site-packages/zeep .tox/pypy*/site-packages/zeep [coverage:report] show_missing = True [egg_info] tag_build = tag_date = 0 zeep-2.5.0/setup.py0000755000076500000240000000447513224161177016202 0ustar mvantellingenstaff00000000000000import re import sys from setuptools import find_packages, setup install_requires = [ 'appdirs>=1.4.0', 'cached-property>=1.3.0', 'defusedxml>=0.4.1', 'isodate>=0.5.4', 'lxml>=3.0.0', 'requests>=2.7.0', 'requests-toolbelt>=0.7.1', 'six>=1.9.0', 'pytz', ] docs_require = [ 'sphinx>=1.4.0', ] tornado_require = [ 'tornado>=4.0.2' ] async_require = [] # see below xmlsec_require = [ 'xmlsec>=0.6.1', ] tests_require = [ 'freezegun==0.3.8', 'mock==2.0.0', 'pretend==1.0.8', 'pytest-cov==2.5.1', 'pytest==3.1.3', 'requests_mock>=0.7.0', 'pytest-tornado==0.4.5', # Linting 'isort==4.2.15', 'flake8==3.3.0', 'flake8-blind-except==0.1.1', 'flake8-debugger==1.4.0', 'flake8-imports==0.1.1', ] if sys.version_info > (3, 4, 2): async_require.append('aiohttp>=1.0') tests_require.append('aioresponses>=0.1.3') with open('README.rst') as fh: long_description = re.sub( '^.. start-no-pypi.*^.. end-no-pypi', '', fh.read(), flags=re.M | re.S) setup( name='zeep', version='2.5.0', description='A modern/fast Python SOAP client based on lxml / requests', long_description=long_description, author="Michael van Tellingen", author_email="michaelvantellingen@gmail.com", url='http://docs.python-zeep.org', install_requires=install_requires, tests_require=tests_require, extras_require={ 'docs': docs_require, 'test': tests_require, 'async': async_require, 'tornado': tornado_require, 'xmlsec': xmlsec_require, }, entry_points={}, package_dir={'': 'src'}, packages=['zeep'], include_package_data=True, license='MIT', classifiers=[ 'Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], zip_safe=False, ) zeep-2.5.0/src/0000755000076500000240000000000013224162010015225 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/0000755000076500000240000000000013224162010016170 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/__init__.py0000644000076500000240000000032713224161177020320 0ustar mvantellingenstaff00000000000000from zeep.client import CachingClient, Client # noqa from zeep.transports import Transport # noqa from zeep.plugins import Plugin # noqa from zeep.xsd.valueobjects import AnyObject # noqa __version__ = '2.5.0' zeep-2.5.0/src/zeep/__main__.py0000644000076500000240000000505113224161177020300 0ustar mvantellingenstaff00000000000000from __future__ import absolute_import, print_function import argparse import logging import logging.config import time import requests from six.moves.urllib.parse import urlparse from zeep.cache import SqliteCache from zeep.client import Client from zeep.transports import Transport logger = logging.getLogger('zeep') def parse_arguments(args=None): parser = argparse.ArgumentParser(description='Zeep: The SOAP client') parser.add_argument( 'wsdl_file', type=str, help='Path or URL to the WSDL file', default=None) parser.add_argument( '--cache', action='store_true', help='Enable cache') parser.add_argument( '--no-verify', action='store_true', help='Disable SSL verification') parser.add_argument( '--verbose', action='store_true', help='Enable verbose output') parser.add_argument( '--profile', help="Enable profiling and save output to given file") parser.add_argument( '--no-strict', action='store_true', default=False, help="Disable strict mode") return parser.parse_args(args) def main(args): if args.verbose: logging.config.dictConfig({ 'version': 1, 'formatters': { 'verbose': { 'format': '%(name)20s: %(message)s' } }, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'verbose', }, }, 'loggers': { 'zeep': { 'level': 'DEBUG', 'propagate': True, 'handlers': ['console'], }, } }) if args.profile: import cProfile profile = cProfile.Profile() profile.enable() cache = SqliteCache() if args.cache else None session = requests.Session() if args.no_verify: session.verify = False result = urlparse(args.wsdl_file) if result.username or result.password: session.auth = (result.username, result.password) transport = Transport(cache=cache, session=session) st = time.time() strict = not args.no_strict client = Client(args.wsdl_file, transport=transport, strict=strict) logger.debug("Loading WSDL took %sms", (time.time() - st) * 1000) if args.profile: profile.disable() profile.dump_stats(args.profile) client.wsdl.dump() if __name__ == '__main__': args = parse_arguments() main(args) zeep-2.5.0/src/zeep/asyncio/0000755000076500000240000000000013224162010017635 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/asyncio/__init__.py0000644000076500000240000000010113224161177021753 0ustar mvantellingenstaff00000000000000from .transport import * # noqa from .bindings import * # noqa zeep-2.5.0/src/zeep/asyncio/bindings.py0000644000076500000240000000130513224161177022020 0ustar mvantellingenstaff00000000000000from zeep.wsdl import bindings __all__ = ['AsyncSoap11Binding', 'AsyncSoap12Binding'] class AsyncSoapBinding(object): async def send(self, client, options, operation, args, kwargs): envelope, http_headers = self._create( operation, args, kwargs, client=client, options=options) response = await client.transport.post_xml( options['address'], envelope, http_headers) operation_obj = self.get(operation) return self.process_reply(client, operation_obj, response) class AsyncSoap11Binding(AsyncSoapBinding, bindings.Soap11Binding): pass class AsyncSoap12Binding(AsyncSoapBinding, bindings.Soap12Binding): pass zeep-2.5.0/src/zeep/asyncio/transport.py0000644000076500000240000000642413224161177022266 0ustar mvantellingenstaff00000000000000""" Adds asyncio support to Zeep. Contains Python 3.5+ only syntax! """ import asyncio import logging import aiohttp from requests import Response from zeep.asyncio import bindings from zeep.exceptions import TransportError from zeep.transports import Transport from zeep.utils import get_version from zeep.wsdl.utils import etree_to_string __all__ = ['AsyncTransport'] class AsyncTransport(Transport): """Asynchronous Transport class using aiohttp.""" binding_classes = [ bindings.AsyncSoap11Binding, bindings.AsyncSoap12Binding, ] def __init__(self, loop, cache=None, timeout=300, operation_timeout=None, session=None): self.loop = loop if loop else asyncio.get_event_loop() self.cache = cache self.load_timeout = timeout self.operation_timeout = operation_timeout self.logger = logging.getLogger(__name__) self.session = session or aiohttp.ClientSession(loop=self.loop) self._close_session = session is None self.session._default_headers['User-Agent'] = ( 'Zeep/%s (www.python-zeep.org)' % (get_version())) def __del__(self): if self._close_session: self.session.close() def _load_remote_data(self, url): result = None async def _load_remote_data_async(): nonlocal result with aiohttp.Timeout(self.load_timeout): response = await self.session.get(url) result = await response.read() try: response.raise_for_status() except aiohttp.ClientError as exc: raise TransportError( message=str(exc), status_code=response.status, content=result ).with_traceback(exc.__traceback__) from exc # Block until we have the data self.loop.run_until_complete(_load_remote_data_async()) return result async def post(self, address, message, headers): self.logger.debug("HTTP Post to %s:\n%s", address, message) with aiohttp.Timeout(self.operation_timeout): response = await self.session.post( address, data=message, headers=headers) self.logger.debug( "HTTP Response from %s (status: %d):\n%s", address, response.status, await response.read()) return response async def post_xml(self, address, envelope, headers): message = etree_to_string(envelope) response = await self.post(address, message, headers) return await self.new_response(response) async def get(self, address, params, headers): with aiohttp.Timeout(self.operation_timeout): response = await self.session.get( address, params=params, headers=headers) return await self.new_response(response) async def new_response(self, response): """Convert an aiohttp.Response object to a requests.Response object""" new = Response() new._content = await response.read() new.status_code = response.status new.headers = response.headers new.cookies = response.cookies new.encoding = response.charset return new zeep-2.5.0/src/zeep/cache.py0000644000076500000240000001114513224161177017624 0ustar mvantellingenstaff00000000000000import base64 import datetime import errno import logging import os import threading from contextlib import contextmanager import appdirs import pytz import six # The sqlite3 is not available on Google App Engine so we handle the # ImportError here and set the sqlite3 var to None. # See https://github.com/mvantellingen/python-zeep/issues/243 try: import sqlite3 except ImportError: sqlite3 = None logger = logging.getLogger(__name__) class Base(object): def add(self, url, content): raise NotImplemented() def get(self, url): raise NotImplemented() class InMemoryCache(Base): """Simple in-memory caching using dict lookup with support for timeouts""" _cache = {} # global cache, thread-safe by default def __init__(self, timeout=3600): self._timeout = timeout def add(self, url, content): logger.debug("Caching contents of %s", url) self._cache[url] = (datetime.datetime.utcnow(), content) def get(self, url): try: created, content = self._cache[url] except KeyError: pass else: if not _is_expired(created, self._timeout): logger.debug("Cache HIT for %s", url) return content logger.debug("Cache MISS for %s", url) return None class SqliteCache(Base): """Cache contents via an sqlite database on the filesystem""" _version = '1' def __init__(self, path=None, timeout=3600): if sqlite3 is None: raise RuntimeError("sqlite3 module is required for the SqliteCache") # No way we can support this when we want to achieve thread safety if path == ':memory:': raise ValueError( "The SqliteCache doesn't support :memory: since it is not " + "thread-safe. Please use zeep.cache.InMemoryCache()") self._lock = threading.RLock() self._timeout = timeout self._db_path = path if path else _get_default_cache_path() # Initialize db with self.db_connection() as conn: cursor = conn.cursor() cursor.execute( """ CREATE TABLE IF NOT EXISTS request (created timestamp, url text, content text) """) conn.commit() @contextmanager def db_connection(self): with self._lock: connection = sqlite3.connect( self._db_path, detect_types=sqlite3.PARSE_DECLTYPES) yield connection connection.close() def add(self, url, content): logger.debug("Caching contents of %s", url) data = self._encode_data(content) with self.db_connection() as conn: cursor = conn.cursor() cursor.execute("DELETE FROM request WHERE url = ?", (url,)) cursor.execute( "INSERT INTO request (created, url, content) VALUES (?, ?, ?)", (datetime.datetime.utcnow(), url, data)) conn.commit() def get(self, url): with self.db_connection() as conn: cursor = conn.cursor() cursor.execute( "SELECT created, content FROM request WHERE url=?", (url, )) rows = cursor.fetchall() if rows: created, data = rows[0] if not _is_expired(created, self._timeout): logger.debug("Cache HIT for %s", url) return self._decode_data(data) logger.debug("Cache MISS for %s", url) def _encode_data(self, data): data = base64.b64encode(data) if six.PY2: return buffer(self._version_string + data) # noqa return self._version_string + data def _decode_data(self, data): if six.PY2: data = str(data) if data.startswith(self._version_string): return base64.b64decode(data[len(self._version_string):]) @property def _version_string(self): prefix = u'$ZEEP:%s$' % self._version return bytes(prefix.encode('ascii')) def _is_expired(value, timeout): """Return boolean if the value is expired""" if timeout is None: return False now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) max_age = value.replace(tzinfo=pytz.utc) max_age += datetime.timedelta(seconds=timeout) return now > max_age def _get_default_cache_path(): path = appdirs.user_cache_dir('zeep', False) try: os.makedirs(path) except OSError as exc: if exc.errno == errno.EEXIST and os.path.isdir(path): pass else: raise return os.path.join(path, 'cache.db') zeep-2.5.0/src/zeep/client.py0000644000076500000240000002361213224161177020041 0ustar mvantellingenstaff00000000000000import copy import logging from contextlib import contextmanager from zeep.transports import Transport from zeep.wsdl import Document from zeep.xsd.const import NotSet logger = logging.getLogger(__name__) class OperationProxy(object): def __init__(self, service_proxy, operation_name): self._proxy = service_proxy self._op_name = operation_name @property def __doc__(self): return str(self._proxy._binding._operations[self._op_name]) def __call__(self, *args, **kwargs): """Call the operation with the given args and kwargs. :rtype: zeep.xsd.CompoundValue """ if self._proxy._client._default_soapheaders: op_soapheaders = kwargs.get('_soapheaders') if op_soapheaders: soapheaders = copy.deepcopy(self._proxy._client._default_soapheaders) if type(op_soapheaders) != type(soapheaders): raise ValueError("Incompatible soapheaders definition") if isinstance(soapheaders, list): soapheaders.extend(op_soapheaders) else: soapheaders.update(op_soapheaders) else: soapheaders = self._proxy._client._default_soapheaders kwargs['_soapheaders'] = soapheaders return self._proxy._binding.send( self._proxy._client, self._proxy._binding_options, self._op_name, args, kwargs) class ServiceProxy(object): def __init__(self, client, binding, **binding_options): self._client = client self._binding_options = binding_options self._binding = binding def __getattr__(self, key): """Return the OperationProxy for the given key. :rtype: OperationProxy() """ return self[key] def __getitem__(self, key): """Return the OperationProxy for the given key. :rtype: OperationProxy() """ try: self._binding.get(key) except ValueError: raise AttributeError('Service has no operation %r' % key) return OperationProxy(self, key) def __dir__(self): """ Return the names of the operations. """ return list(dir(super(ServiceProxy, self)) + list(self.__dict__) + list(self._binding.port_type.operations)) # using list() on the dicts for Python 3 compatibility class Factory(object): def __init__(self, types, kind, namespace): self._method = getattr(types, 'get_%s' % kind) if namespace in types.namespaces: self._ns = namespace else: self._ns = types.get_ns_prefix(namespace) def __getattr__(self, key): """Return the complexType or simpleType for the given localname. :rtype: zeep.xsd.ComplexType or zeep.xsd.AnySimpleType """ return self[key] def __getitem__(self, key): """Return the complexType or simpleType for the given localname. :rtype: zeep.xsd.ComplexType or zeep.xsd.AnySimpleType """ return self._method('{%s}%s' % (self._ns, key)) class Client(object): """The zeep Client. :param wsdl: :param wsse: :param transport: Custom transport class. :param service_name: The service name for the service binding. Defaults to the first service in the WSDL document. :param port_name: The port name for the default binding. Defaults to the first port defined in the service element in the WSDL document. :param plugins: a list of Plugin instances :param xml_huge_tree: disable lxml/libxml2 security restrictions and support very deep trees and very long text content """ def __init__(self, wsdl, wsse=None, transport=None, service_name=None, port_name=None, plugins=None, strict=True, xml_huge_tree=False): if not wsdl: raise ValueError("No URL given for the wsdl") self.transport = transport if transport is not None else Transport() self.wsdl = Document(wsdl, self.transport, strict=strict) self.wsse = wsse self.plugins = plugins if plugins is not None else [] self.xml_huge_tree = xml_huge_tree # options self.raw_response = False self._default_service = None self._default_service_name = service_name self._default_port_name = port_name self._default_soapheaders = None @property def namespaces(self): return self.wsdl.types.prefix_map @property def service(self): """The default ServiceProxy instance :rtype: ServiceProxy """ if self._default_service: return self._default_service self._default_service = self.bind( service_name=self._default_service_name, port_name=self._default_port_name) if not self._default_service: raise ValueError( "There is no default service defined. This is usually due to " "missing wsdl:service definitions in the WSDL") return self._default_service @contextmanager def options(self, timeout=NotSet, raw_response=NotSet): """Context manager to temporarily overrule various options. :param timeout: Set the timeout for POST/GET operations (not used for loading external WSDL or XSD documents) To for example set the timeout to 10 seconds use:: client = zeep.Client('foo.wsdl') with client.options(timeout=10): client.service.fast_call() """ if raw_response is not NotSet: # Store current options old_raw_response = self.raw_response # Set new options self.raw_response = raw_response if timeout is not NotSet: timeout_ctx = self.transport._options(timeout=timeout) timeout_ctx.__enter__() yield if raw_response is not NotSet: self.raw_response = old_raw_response if timeout is not NotSet: timeout_ctx.__exit__(None, None, None) def bind(self, service_name=None, port_name=None): """Create a new ServiceProxy for the given service_name and port_name. The default ServiceProxy instance (`self.service`) always referes to the first service/port in the wsdl Document. Use this when a specific port is required. """ if not self.wsdl.services: return service = self._get_service(service_name) port = self._get_port(service, port_name) return ServiceProxy(self, port.binding, **port.binding_options) def create_service(self, binding_name, address): """Create a new ServiceProxy for the given binding name and address. :param binding_name: The QName of the binding :param address: The address of the endpoint """ try: binding = self.wsdl.bindings[binding_name] except KeyError: raise ValueError( "No binding found with the given QName. Available bindings " "are: %s" % (', '.join(self.wsdl.bindings.keys()))) return ServiceProxy(self, binding, address=address) def create_message(self, service, operation_name, *args, **kwargs): """Create the payload for the given operation. :rtype: lxml.etree._Element """ envelope, http_headers = service._binding._create( operation_name, args, kwargs, client=self) return envelope def type_factory(self, namespace): """Return a type factory for the given namespace. Example:: factory = client.type_factory('ns0') user = factory.User(name='John') :rtype: Factory """ return Factory(self.wsdl.types, 'type', namespace) def get_type(self, name): """Return the type for the given qualified name. :rtype: zeep.xsd.ComplexType or zeep.xsd.AnySimpleType """ return self.wsdl.types.get_type(name) def get_element(self, name): """Return the element for the given qualified name. :rtype: zeep.xsd.Element """ return self.wsdl.types.get_element(name) def set_ns_prefix(self, prefix, namespace): """Set a shortcut for the given namespace. """ self.wsdl.types.set_ns_prefix(prefix, namespace) def set_default_soapheaders(self, headers): """Set the default soap headers which will be automatically used on all calls. Note that if you pass custom soapheaders using a list then you will also need to use that during the operations. Since mixing these use cases isn't supported (yet). """ self._default_soapheaders = headers def _get_port(self, service, name): if name: port = service.ports.get(name) if not port: raise ValueError("Port not found") else: port = list(service.ports.values())[0] return port def _get_service(self, name): if name: service = self.wsdl.services.get(name) if not service: raise ValueError("Service not found") else: service = next(iter(self.wsdl.services.values()), None) return service class CachingClient(Client): """Shortcut to create a caching client, for the lazy people. This enables the SqliteCache by default in the transport as was the default in earlier versions of zeep. """ def __init__(self, *args, **kwargs): # Don't use setdefault since we want to lazily init the Transport cls from zeep.cache import SqliteCache kwargs['transport'] = ( kwargs.get('transport') or Transport(cache=SqliteCache())) super(CachingClient, self).__init__(*args, **kwargs) zeep-2.5.0/src/zeep/exceptions.py0000644000076500000240000000453613224161177020750 0ustar mvantellingenstaff00000000000000class Error(Exception): def __init__(self, message=''): super(Exception, self).__init__(message) self.message = message def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.message) class XMLSyntaxError(Error): def __init__(self, *args, **kwargs): self.content = kwargs.pop('content', None) super(XMLSyntaxError, self).__init__(*args, **kwargs) class XMLParseError(Error): def __init__(self, *args, **kwargs): self.filename = kwargs.pop('filename', None) self.sourceline = kwargs.pop('sourceline', None) super(XMLParseError, self).__init__(*args, **kwargs) def __str__(self): location = None if self.filename and self.sourceline: location = '%s:%s' % (self.filename, self.sourceline) if location: return '%s (%s)' % (self.message, location) return self.message class UnexpectedElementError(Error): pass class WsdlSyntaxError(Error): pass class TransportError(Error): def __init__(self, message='', status_code=0, content=None): super(TransportError, self).__init__(message) self.status_code = status_code self.content = content class LookupError(Error): def __init__(self, *args, **kwargs): self.qname = kwargs.pop('qname', None) self.item_name = kwargs.pop('item_name', None) self.location = kwargs.pop('location', None) super(LookupError, self).__init__(*args, **kwargs) class NamespaceError(Error): pass class Fault(Error): def __init__(self, message, code=None, actor=None, detail=None, subcodes=None): super(Fault, self).__init__(message) self.message = message self.code = code self.actor = actor self.detail = detail self.subcodes = subcodes class ZeepWarning(RuntimeWarning): pass class ValidationError(Error): def __init__(self, *args, **kwargs): self.path = kwargs.pop('path', []) super(ValidationError, self).__init__(*args, **kwargs) def __str__(self): if self.path: path = '.'.join(str(x) for x in self.path) return '%s (%s)' % (self.message, path) return self.message class SignatureVerificationFailed(Error): pass class IncompleteMessage(Error): pass class IncompleteOperation(Error): pass zeep-2.5.0/src/zeep/helpers.py0000644000076500000240000000377013224161177020230 0ustar mvantellingenstaff00000000000000import datetime from collections import OrderedDict from lxml import etree from zeep import xsd from zeep.xsd.valueobjects import CompoundValue def serialize_object(obj, target_cls=OrderedDict): """Serialize zeep objects to native python data structures""" if isinstance(obj, list): return [serialize_object(sub, target_cls) for sub in obj] if isinstance(obj, (dict, CompoundValue)): result = target_cls() for key in obj: result[key] = serialize_object(obj[key], target_cls) return result return obj def create_xml_soap_map(values): """Create an http://xml.apache.org/xml-soap#Map value.""" Map = xsd.ComplexType( xsd.Sequence([ xsd.Element( 'item', xsd.AnyType(), min_occurs=1, max_occurs="unbounded"), ]), qname=etree.QName('{http://xml.apache.org/xml-soap}Map')) KeyValueData = xsd.Element( '{http://xml.apache.org/xml-soap}KeyValueData', xsd.ComplexType( xsd.Sequence([ xsd.Element( 'key', xsd.AnyType(), ), xsd.Element( 'value', xsd.AnyType(), ), ]), ), ) return Map(item=[ KeyValueData( xsd.AnyObject(xsd.String(), key), xsd.AnyObject(guess_xsd_type(value), value) ) for key, value in values.items() ]) def guess_xsd_type(obj): """Return the XSD Type for the given object""" if isinstance(obj, bool): return xsd.Boolean() if isinstance(obj, int): return xsd.Integer() if isinstance(obj, float): return xsd.Float() if isinstance(obj, datetime.datetime): return xsd.DateTime() if isinstance(obj, datetime.date): return xsd.Date() return xsd.String() def Nil(): """Return an xsi:nil element""" return xsd.AnyObject(None, None) zeep-2.5.0/src/zeep/loader.py0000644000076500000240000000703113224161177020026 0ustar mvantellingenstaff00000000000000import os.path from defusedxml.lxml import fromstring from lxml import etree from six.moves.urllib.parse import urljoin, urlparse from zeep.exceptions import XMLSyntaxError class ImportResolver(etree.Resolver): """Custom lxml resolve to use the transport object""" def __init__(self, transport): self.transport = transport def resolve(self, url, pubid, context): if urlparse(url).scheme in ('http', 'https'): content = self.transport.load(url) return self.resolve_string(content, context) def parse_xml(content, transport, base_url=None, strict=True, xml_huge_tree=False): """Parse an XML string and return the root Element. :param content: The XML string :type content: str :param transport: The transport instance to load imported documents :type transport: zeep.transports.Transport :param base_url: The base url of the document, used to make relative lookups absolute. :type base_url: str :param strict: boolean to indicate if the lxml should be parsed a 'strict'. If false then the recover mode is enabled which tries to parse invalid XML as best as it can. :param xml_huge_tree: boolean to indicate if lxml should process very large XML content. :type strict: boolean :returns: The document root :rtype: lxml.etree._Element """ recover = not strict parser = etree.XMLParser(remove_comments=True, resolve_entities=False, recover=recover, huge_tree=xml_huge_tree) parser.resolvers.add(ImportResolver(transport)) try: return fromstring(content, parser=parser, base_url=base_url) except etree.XMLSyntaxError as exc: raise XMLSyntaxError( "Invalid XML content received (%s)" % exc.msg, content=content ) def load_external(url, transport, base_url=None, strict=True): """Load an external XML document. :param url: :param transport: :param base_url: :param strict: boolean to indicate if the lxml should be parsed a 'strict'. If false then the recover mode is enabled which tries to parse invalid XML as best as it can. :type strict: boolean """ if hasattr(url, 'read'): content = url.read() else: if base_url: url = absolute_location(url, base_url) content = transport.load(url) return parse_xml(content, transport, base_url, strict=strict) def absolute_location(location, base): """Make an url absolute (if it is optional) via the passed base url. :param location: The (relative) url :type location: str :param base: The base location :type base: str :returns: An absolute URL :rtype: str """ if location == base: return location if urlparse(location).scheme in ('http', 'https', 'file'): return location if base and urlparse(base).scheme in ('http', 'https', 'file'): return urljoin(base, location) else: if os.path.isabs(location): return location if base: return os.path.realpath( os.path.join(os.path.dirname(base), location)) return location def is_relative_path(value): """Check if the given value is a relative path :param value: The value :type value: str :returns: Boolean indicating if the url is relative. If it is absolute then False is returned. :rtype: boolean """ if urlparse(value).scheme in ('http', 'https', 'file'): return False return not os.path.isabs(value) zeep-2.5.0/src/zeep/ns.py0000644000076500000240000000137113224161177017201 0ustar mvantellingenstaff00000000000000 SOAP_11 = 'http://schemas.xmlsoap.org/wsdl/soap/' SOAP_12 = 'http://schemas.xmlsoap.org/wsdl/soap12/' SOAP_ENV_11 = 'http://schemas.xmlsoap.org/soap/envelope/' SOAP_ENV_12 = 'http://www.w3.org/2003/05/soap-envelope' XSI = 'http://www.w3.org/2001/XMLSchema-instance' XSD = 'http://www.w3.org/2001/XMLSchema' WSDL = 'http://schemas.xmlsoap.org/wsdl/' HTTP = 'http://schemas.xmlsoap.org/wsdl/http/' MIME = 'http://schemas.xmlsoap.org/wsdl/mime/' WSA = 'http://www.w3.org/2005/08/addressing' DS = 'http://www.w3.org/2000/09/xmldsig#' WSSE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' WSU = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' NAMESPACE_TO_PREFIX = { XSD: 'xsd', } zeep-2.5.0/src/zeep/plugins.py0000644000076500000240000000436513224161177020250 0ustar mvantellingenstaff00000000000000from collections import deque class Plugin(object): """Base plugin""" def ingress(self, envelope, http_headers, operation): """Override to update the envelope or http headers when receiving a message. :param envelope: The envelope as XML node :param http_headers: Dict with the HTTP headers """ return envelope, http_headers def egress(self, envelope, http_headers, operation, binding_options): """Override to update the envelope or http headers when sending a message. :param envelope: The envelope as XML node :param http_headers: Dict with the HTTP headers :param operation: The associated Operation instance :param binding_options: Binding specific options for the operation """ return envelope, http_headers def apply_egress(client, envelope, http_headers, operation, binding_options): for plugin in client.plugins: result = plugin.egress( envelope, http_headers, operation, binding_options) if result is not None: envelope, http_headers = result return envelope, http_headers def apply_ingress(client, envelope, http_headers, operation): for plugin in client.plugins: result = plugin.ingress(envelope, http_headers, operation) if result is not None: envelope, http_headers = result return envelope, http_headers class HistoryPlugin(object): def __init__(self, maxlen=1): self._buffer = deque([], maxlen) @property def last_sent(self): last_tx = self._buffer[-1] if last_tx: return last_tx['sent'] @property def last_received(self): last_tx = self._buffer[-1] if last_tx: return last_tx['received'] def ingress(self, envelope, http_headers, operation): last_tx = self._buffer[-1] last_tx['received'] = { 'envelope': envelope, 'http_headers': http_headers, } def egress(self, envelope, http_headers, operation, binding_options): self._buffer.append({ 'received': None, 'sent': { 'envelope': envelope, 'http_headers': http_headers, }, }) zeep-2.5.0/src/zeep/tornado/0000755000076500000240000000000013224162010017636 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/tornado/__init__.py0000644000076500000240000000010113224161177021754 0ustar mvantellingenstaff00000000000000from .transport import * # noqa from .bindings import * # noqa zeep-2.5.0/src/zeep/tornado/bindings.py0000644000076500000240000000136613224161177022030 0ustar mvantellingenstaff00000000000000from tornado import gen from zeep.wsdl import bindings __all__ = ['AsyncSoap11Binding', 'AsyncSoap12Binding'] class AsyncSoapBinding(object): @gen.coroutine def send(self, client, options, operation, args, kwargs): envelope, http_headers = self._create( operation, args, kwargs, client=client, options=options) response = yield client.transport.post_xml( options['address'], envelope, http_headers) operation_obj = self.get(operation) raise gen.Return(self.process_reply(client, operation_obj, response)) class AsyncSoap11Binding(AsyncSoapBinding, bindings.Soap11Binding): pass class AsyncSoap12Binding(AsyncSoapBinding, bindings.Soap12Binding): pass zeep-2.5.0/src/zeep/tornado/transport.py0000644000076500000240000001037613224161177022270 0ustar mvantellingenstaff00000000000000""" Adds async tornado.gen support to Zeep. """ import logging import urllib from requests import Response, Session from requests.auth import HTTPBasicAuth, HTTPDigestAuth from tornado import gen, httpclient from zeep.tornado import bindings from zeep.transports import Transport from zeep.utils import get_version from zeep.wsdl.utils import etree_to_string __all__ = ['TornadoAsyncTransport'] class TornadoAsyncTransport(Transport): """Asynchronous Transport class using tornado gen.""" binding_classes = [ bindings.AsyncSoap11Binding, bindings.AsyncSoap12Binding] def __init__(self, cache=None, timeout=300, operation_timeout=None, session=None): self.cache = cache self.load_timeout = timeout self.operation_timeout = operation_timeout self.logger = logging.getLogger(__name__) self.session = session or Session() self.session.headers['User-Agent'] = ( 'Zeep/%s (www.python-zeep.org)' % (get_version())) def _load_remote_data(self, url): client = httpclient.HTTPClient() kwargs = { 'method': 'GET', 'request_timeout': self.load_timeout } http_req = httpclient.HTTPRequest(url, **kwargs) response = client.fetch(http_req) return response.body @gen.coroutine def post(self, address, message, headers): response = yield self.fetch(address, 'POST', headers, message) raise gen.Return(response) @gen.coroutine def post_xml(self, address, envelope, headers): message = etree_to_string(envelope) response = yield self.post(address, message, headers) raise gen.Return(response) @gen.coroutine def get(self, address, params, headers): if params: address += '?' + urllib.urlencode(params) response = yield self.fetch(address, 'GET', headers) raise gen.Return(response) @gen.coroutine def fetch(self, address, method, headers, message=None): async_client = httpclient.AsyncHTTPClient() # extracting auth auth_username = None auth_password = None auth_mode = None if self.session.auth: if type(self.session.auth) is tuple: auth_username = self.session.auth[0] auth_password = self.session.auth[1] auth_mode = 'basic' elif type(self.session.auth) is HTTPBasicAuth: auth_username = self.session.username auth_password = self.session.password auth_mode = 'basic' elif type(self.session.auth) is HTTPDigestAuth: auth_username = self.session.username auth_password = self.session.password auth_mode = 'digest' else: raise Exception('Not supported authentication.') # extracting client cert client_cert = None client_key = None if self.session.cert: if type(self.session.cert) is str: client_cert = self.session.cert elif type(self.session.cert) is tuple: client_cert = self.session.cert[0] client_key = self.session.cert[1] session_headers = dict(self.session.headers.items()) kwargs = { 'method': method, 'request_timeout': self.operation_timeout, 'headers': dict(headers, **session_headers), 'auth_username': auth_username, 'auth_password': auth_password, 'auth_mode': auth_mode, 'validate_cert': self.session.verify is not None, 'ca_certs': self.session.verify, 'client_key': client_key, 'client_cert': client_cert } if message: kwargs['body'] = message http_req = httpclient.HTTPRequest(address, **kwargs) response = yield async_client.fetch(http_req) raise gen.Return(self.new_response(response)) def new_response(self, response): """Convert an tornado.HTTPResponse object to a requests.Response object""" new = Response() new._content = response.body new.status_code = response.code new.headers = dict(response.headers.get_all()) return new zeep-2.5.0/src/zeep/transports.py0000644000076500000240000001123113224161177020774 0ustar mvantellingenstaff00000000000000import logging import os from contextlib import contextmanager import requests from six.moves.urllib.parse import urlparse from zeep.utils import get_media_type, get_version from zeep.wsdl.utils import etree_to_string class Transport(object): """The transport object handles all communication to the SOAP server. :param cache: The cache object to be used to cache GET requests :param timeout: The timeout for loading wsdl and xsd documents. :param operation_timeout: The timeout for operations (POST/GET). By default this is None (no timeout). :param session: A :py:class:`request.Session()` object (optional) """ def __init__(self, cache=None, timeout=300, operation_timeout=None, session=None): self.cache = cache self.load_timeout = timeout self.operation_timeout = operation_timeout self.logger = logging.getLogger(__name__) self.session = session or requests.Session() self.session.headers['User-Agent'] = ( 'Zeep/%s (www.python-zeep.org)' % (get_version())) def get(self, address, params, headers): """Proxy to requests.get() :param address: The URL for the request :param params: The query parameters :param headers: a dictionary with the HTTP headers. """ response = self.session.get( address, params=params, headers=headers, timeout=self.operation_timeout) return response def post(self, address, message, headers): """Proxy to requests.posts() :param address: The URL for the request :param message: The content for the body :param headers: a dictionary with the HTTP headers. """ if self.logger.isEnabledFor(logging.DEBUG): log_message = message if isinstance(log_message, bytes): log_message = log_message.decode('utf-8') self.logger.debug("HTTP Post to %s:\n%s", address, log_message) response = self.session.post( address, data=message, headers=headers, timeout=self.operation_timeout) if self.logger.isEnabledFor(logging.DEBUG): media_type = get_media_type( response.headers.get('Content-Type', 'text/xml')) if media_type == 'multipart/related': log_message = response.content else: log_message = response.content if isinstance(log_message, bytes): log_message = log_message.decode('utf-8') self.logger.debug( "HTTP Response from %s (status: %d):\n%s", address, response.status_code, log_message) return response def post_xml(self, address, envelope, headers): """Post the envelope xml element to the given address with the headers. This method is intended to be overriden if you want to customize the serialization of the xml element. By default the body is formatted and encoded as utf-8. See ``zeep.wsdl.utils.etree_to_string``. """ message = etree_to_string(envelope) return self.post(address, message, headers) def load(self, url): """Load the content from the given URL""" if not url: raise ValueError("No url given to load") scheme = urlparse(url).scheme if scheme in ('http', 'https'): if self.cache: response = self.cache.get(url) if response: return bytes(response) content = self._load_remote_data(url) if self.cache: self.cache.add(url, content) return content elif scheme == 'file': if url.startswith('file://'): url = url[7:] with open(os.path.expanduser(url), 'rb') as fh: return fh.read() def _load_remote_data(self, url): response = self.session.get(url, timeout=self.load_timeout) response.raise_for_status() return response.content @contextmanager def _options(self, timeout=None): """Context manager to temporarily overrule options. Example:: client = zeep.Client('foo.wsdl') with client.options(timeout=10): client.service.fast_call() :param timeout: Set the timeout for POST/GET operations (not used for loading external WSDL or XSD documents) """ old_timeout = self.operation_timeout self.operation_timeout = timeout yield self.operation_timeout = old_timeout zeep-2.5.0/src/zeep/utils.py0000644000076500000240000000443413224161177017724 0ustar mvantellingenstaff00000000000000import cgi import inspect from lxml import etree from zeep.exceptions import XMLParseError from zeep.ns import XSD def qname_attr(node, attr_name, target_namespace=None): value = node.get(attr_name) if value is not None: return as_qname(value, node.nsmap, target_namespace) def as_qname(value, nsmap, target_namespace=None): """Convert the given value to a QName""" if ':' in value: prefix, local = value.split(':') # The xml: prefix is always bound to the XML namespace, see # https://www.w3.org/TR/xml-names/ if prefix == 'xml': namespace = 'http://www.w3.org/XML/1998/namespace' else: namespace = nsmap.get(prefix) if not namespace: raise XMLParseError( "No namespace defined for %r (%r)" % (prefix, value)) # Workaround for https://github.com/mvantellingen/python-zeep/issues/349 if not local: return etree.QName(XSD, 'anyType') return etree.QName(namespace, local) if target_namespace: return etree.QName(target_namespace, value) if nsmap.get(None): return etree.QName(nsmap[None], value) return etree.QName(value) def findall_multiple_ns(node, name, namespace_sets): result = [] for nsmap in namespace_sets: result.extend(node.findall(name, namespaces=nsmap)) return result def get_version(): from zeep import __version__ # cyclic import return __version__ def get_base_class(objects): """Return the best base class for multiple objects. Implementation is quick and dirty, might be done better.. ;-) """ bases = [inspect.getmro(obj.__class__)[::-1] for obj in objects] num_objects = len(objects) max_mro = max(len(mro) for mro in bases) base_class = None for i in range(max_mro): try: if len({bases[j][i] for j in range(num_objects)}) > 1: break except IndexError: break base_class = bases[0][i] return base_class def detect_soap_env(envelope): root_tag = etree.QName(envelope) return root_tag.namespace def get_media_type(value): """Parse a HTTP content-type header and return the media-type""" main_value, parameters = cgi.parse_header(value) return main_value zeep-2.5.0/src/zeep/wsa.py0000644000076500000240000000224413224161177017353 0ustar mvantellingenstaff00000000000000import uuid from lxml import etree from lxml.builder import ElementMaker from zeep import ns from zeep.plugins import Plugin from zeep.wsdl.utils import get_or_create_header WSA = ElementMaker(namespace=ns.WSA, nsmap={'wsa': ns.WSA}) class WsAddressingPlugin(Plugin): nsmap = { 'wsa': ns.WSA } def egress(self, envelope, http_headers, operation, binding_options): """Apply the ws-addressing headers to the given envelope.""" wsa_action = operation.input.abstract.wsa_action if not wsa_action: wsa_action = operation.soapaction header = get_or_create_header(envelope) headers = [ WSA.Action(wsa_action), WSA.MessageID('urn:uuid:' + str(uuid.uuid4())), WSA.To(binding_options['address']), ] header.extend(headers) # the top_nsmap kwarg was added in lxml 3.5.0 if etree.LXML_VERSION[:2] >= (3, 5): etree.cleanup_namespaces( header, keep_ns_prefixes=header.nsmap, top_nsmap=self.nsmap) else: etree.cleanup_namespaces(header) return envelope, http_headers zeep-2.5.0/src/zeep/wsdl/0000755000076500000240000000000013224162010017141 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/wsdl/__init__.py0000644000076500000240000000064013224161177021267 0ustar mvantellingenstaff00000000000000""" zeep.wsdl --------- The wsdl module is responsible for parsing the WSDL document. This includes the bindings and messages. The structure and naming of the modules and classses closely follows the WSDL 1.1 specification. The serialization and deserialization of the SOAP/HTTP messages is done by the zeep.wsdl.messages modules. """ from zeep.wsdl.wsdl import Document # noqa zeep-2.5.0/src/zeep/wsdl/attachments.py0000644000076500000240000000407513224161177022051 0ustar mvantellingenstaff00000000000000"""Basic implementation to support SOAP-Attachments See https://www.w3.org/TR/SOAP-attachments """ import base64 from cached_property import cached_property from requests.structures import CaseInsensitiveDict class MessagePack(object): def __init__(self, parts): self._parts = parts def __repr__(self): return '' % ( ', '.join(repr(a) for a in self.attachments)) @property def root(self): return self._root def _set_root(self, root): self._root = root @cached_property def attachments(self): """Return a list of attachments. :rtype: list of Attachment """ return [Attachment(part) for part in self._parts] def get_by_content_id(self, content_id): """get_by_content_id :param content_id: The content-id to return :type content_id: str :rtype: Attachment """ for attachment in self.attachments: if attachment.content_id == content_id: return attachment class Attachment(object): def __init__(self, part): encoding = part.encoding or 'utf-8' self.headers = CaseInsensitiveDict({ k.decode(encoding): v.decode(encoding) for k, v in part.headers.items() }) self.content_type = self.headers.get('Content-Type', None) self.content_id = self.headers.get('Content-ID', None) self.content_location = self.headers.get('Content-Location', None) self._part = part def __repr__(self): return '' % (self.content_id, self.content_type) @cached_property def content(self): """Return the content of the attachment :rtype: bytes or str """ encoding = self.headers.get('Content-Transfer-Encoding', None) content = self._part.content if encoding == 'base64': return base64.b64decode(content) elif encoding == 'binary': return content.strip(b'\r\n') else: return content zeep-2.5.0/src/zeep/wsdl/bindings/0000755000076500000240000000000013224162010020736 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/wsdl/bindings/__init__.py0000644000076500000240000000016113224161177023062 0ustar mvantellingenstaff00000000000000from .soap import Soap11Binding, Soap12Binding # noqa from .http import HttpGetBinding, HttpPostBinding # noqa zeep-2.5.0/src/zeep/wsdl/bindings/http.py0000644000076500000240000001432213224161177022306 0ustar mvantellingenstaff00000000000000import logging import six from lxml import etree from zeep import ns from zeep.exceptions import Fault from zeep.utils import qname_attr from zeep.wsdl import messages from zeep.wsdl.definitions import Binding, Operation logger = logging.getLogger(__name__) NSMAP = { 'http': ns.HTTP, 'wsdl': ns.WSDL, 'mime': ns.MIME, } class HttpBinding(Binding): def create_message(self, operation, *args, **kwargs): if isinstance(operation, six.string_types): operation = self.get(operation) if not operation: raise ValueError("Operation not found") return operation.create(*args, **kwargs) def process_service_port(self, xmlelement, force_https=False): address_node = xmlelement.find('http:address', namespaces=NSMAP) if address_node is None: raise ValueError("No `http:address` node found") # Force the usage of HTTPS when the force_https boolean is true location = address_node.get('location') if force_https and location and location.startswith('http://'): logger.warning("Forcing http:address location to HTTPS") location = 'https://' + location[8:] return { 'address': location } @classmethod def parse(cls, definitions, xmlelement): name = qname_attr(xmlelement, 'name', definitions.target_namespace) port_name = qname_attr(xmlelement, 'type', definitions.target_namespace) obj = cls(definitions.wsdl, name, port_name) for node in xmlelement.findall('wsdl:operation', namespaces=NSMAP): operation = HttpOperation.parse(definitions, node, obj) obj._operation_add(operation) return obj def process_reply(self, client, operation, response): if response.status_code != 200: return self.process_error(response.content) raise NotImplementedError("No error handling yet!") return operation.process_reply(response.content) def process_error(self, doc): raise Fault(message=doc) class HttpPostBinding(HttpBinding): def send(self, client, options, operation, args, kwargs): """Called from the service""" operation_obj = self.get(operation) if not operation_obj: raise ValueError("Operation %r not found" % operation) serialized = operation_obj.create(*args, **kwargs) url = options['address'] + serialized.path response = client.transport.post( url, serialized.content, headers=serialized.headers) return self.process_reply(client, operation_obj, response) @classmethod def match(cls, node): """Check if this binding instance should be used to parse the given node. :param node: The node to match against :type node: lxml.etree._Element """ http_node = node.find(etree.QName(NSMAP['http'], 'binding')) return http_node is not None and http_node.get('verb') == 'POST' class HttpGetBinding(HttpBinding): def send(self, client, options, operation, args, kwargs): """Called from the service""" operation_obj = self.get(operation) if not operation_obj: raise ValueError("Operation %r not found" % operation) serialized = operation_obj.create(*args, **kwargs) url = options['address'] + serialized.path response = client.transport.get( url, serialized.content, headers=serialized.headers) return self.process_reply(client, operation_obj, response) @classmethod def match(cls, node): """Check if this binding instance should be used to parse the given node. :param node: The node to match against :type node: lxml.etree._Element """ http_node = node.find(etree.QName(ns.HTTP, 'binding')) return http_node is not None and http_node.get('verb') == 'GET' class HttpOperation(Operation): def __init__(self, name, binding, location): super(HttpOperation, self).__init__(name, binding) self.location = location def process_reply(self, envelope): return self.output.deserialize(envelope) @classmethod def parse(cls, definitions, xmlelement, binding): """ """ name = xmlelement.get('name') http_operation = xmlelement.find('http:operation', namespaces=NSMAP) location = http_operation.get('location') obj = cls(name, binding, location) for node in xmlelement.getchildren(): tag_name = etree.QName(node.tag).localname if tag_name not in ('input', 'output'): continue # XXX Multiple mime types may be declared as alternatives message_node = None if len(node.getchildren()) > 0: message_node = node.getchildren()[0] message_class = None if message_node is not None: if message_node.tag == etree.QName(ns.HTTP, 'urlEncoded'): message_class = messages.UrlEncoded elif message_node.tag == etree.QName(ns.HTTP, 'urlReplacement'): message_class = messages.UrlReplacement elif message_node.tag == etree.QName(ns.MIME, 'content'): message_class = messages.MimeContent elif message_node.tag == etree.QName(ns.MIME, 'mimeXml'): message_class = messages.MimeXML if message_class: msg = message_class.parse(definitions, node, obj) assert msg setattr(obj, tag_name, msg) return obj def resolve(self, definitions): super(HttpOperation, self).resolve(definitions) if self.output: self.output.resolve(definitions, self.abstract.output_message) if self.input: self.input.resolve(definitions, self.abstract.input_message) zeep-2.5.0/src/zeep/wsdl/bindings/soap.py0000644000076500000240000003734013224161177022276 0ustar mvantellingenstaff00000000000000import logging from lxml import etree from requests_toolbelt.multipart.decoder import MultipartDecoder from zeep import ns, plugins, wsa from zeep.exceptions import Fault, TransportError, XMLSyntaxError from zeep.loader import parse_xml from zeep.utils import as_qname, get_media_type, qname_attr from zeep.wsdl.attachments import MessagePack from zeep.wsdl.definitions import Binding, Operation from zeep.wsdl.messages import DocumentMessage, RpcMessage from zeep.wsdl.messages.xop import process_xop from zeep.wsdl.utils import etree_to_string, url_http_to_https logger = logging.getLogger(__name__) class SoapBinding(Binding): """Soap 1.1/1.2 binding""" def __init__(self, wsdl, name, port_name, transport, default_style): """The SoapBinding is the base class for the Soap11Binding and Soap12Binding. :param wsdl: :type wsdl: :param name: :type name: string :param port_name: :type port_name: string :param transport: :type transport: zeep.transports.Transport :param default_style: """ super(SoapBinding, self).__init__(wsdl, name, port_name) self.transport = transport self.default_style = default_style @classmethod def match(cls, node): """Check if this binding instance should be used to parse the given node. :param node: The node to match against :type node: lxml.etree._Element """ soap_node = node.find('soap:binding', namespaces=cls.nsmap) return soap_node is not None def create_message(self, operation, *args, **kwargs): envelope, http_headers = self._create(operation, args, kwargs) return envelope def _create(self, operation, args, kwargs, client=None, options=None): """Create the XML document to send to the server. Note that this generates the soap envelope without the wsse applied. """ operation_obj = self.get(operation) if not operation_obj: raise ValueError("Operation %r not found" % operation) # Create the SOAP envelope serialized = operation_obj.create(*args, **kwargs) self._set_http_headers(serialized, operation_obj) envelope = serialized.content http_headers = serialized.headers # Apply ws-addressing if client: if not options: options = client.service._binding_options if operation_obj.abstract.input_message.wsa_action: envelope, http_headers = wsa.WsAddressingPlugin().egress( envelope, http_headers, operation_obj, options) # Apply plugins envelope, http_headers = plugins.apply_egress( client, envelope, http_headers, operation_obj, options) # Apply WSSE if client.wsse: envelope, http_headers = client.wsse.apply(envelope, http_headers) return envelope, http_headers def send(self, client, options, operation, args, kwargs): """Called from the service :param client: The client with which the operation was called :type client: zeep.client.Client :param options: The binding options :type options: dict :param operation: The operation object from which this is a reply :type operation: zeep.wsdl.definitions.Operation :param args: The args to pass to the operation :type args: tuple :param kwargs: The kwargs to pass to the operation :type kwargs: dict """ envelope, http_headers = self._create( operation, args, kwargs, client=client, options=options) response = client.transport.post_xml( options['address'], envelope, http_headers) operation_obj = self.get(operation) # If the client wants to return the raw data then let's do that. if client.raw_response: return response return self.process_reply(client, operation_obj, response) def process_reply(self, client, operation, response): """Process the XML reply from the server. :param client: The client with which the operation was called :type client: zeep.client.Client :param operation: The operation object from which this is a reply :type operation: zeep.wsdl.definitions.Operation :param response: The response object returned by the remote server :type response: requests.Response """ if response.status_code in (201, 202) and not response.content: return None elif response.status_code != 200 and not response.content: raise TransportError( u'Server returned HTTP status %d (no content available)' % response.status_code, status_code=response.status_code) content_type = response.headers.get('Content-Type', 'text/xml') media_type = get_media_type(content_type) message_pack = None # If the reply is a multipart/related then we need to retrieve all the # parts if media_type == 'multipart/related': decoder = MultipartDecoder( response.content, content_type, response.encoding or 'utf-8') content = decoder.parts[0].content if len(decoder.parts) > 1: message_pack = MessagePack(parts=decoder.parts[1:]) else: content = response.content try: doc = parse_xml( content, self.transport, strict=client.wsdl.strict, xml_huge_tree=client.xml_huge_tree) except XMLSyntaxError: raise TransportError( 'Server returned HTTP status %d (%s)' % (response.status_code, response.content), status_code=response.status_code, content=response.content) # Check if this is an XOP message which we need to decode first if message_pack: if process_xop(doc, message_pack): message_pack = None if client.wsse: client.wsse.verify(doc) doc, http_headers = plugins.apply_ingress( client, doc, response.headers, operation) # If the response code is not 200 or if there is a Fault node available # then assume that an error occured. fault_node = doc.find( 'soap-env:Body/soap-env:Fault', namespaces=self.nsmap) if response.status_code != 200 or fault_node is not None: return self.process_error(doc, operation) result = operation.process_reply(doc) if message_pack: message_pack._set_root(result) return message_pack return result def process_error(self, doc, operation): raise NotImplementedError def process_service_port(self, xmlelement, force_https=False): address_node = xmlelement.find('soap:address', namespaces=self.nsmap) if address_node is None: logger.debug("No valid soap:address found for service") return # Force the usage of HTTPS when the force_https boolean is true location = address_node.get('location') if force_https and location: location = url_http_to_https(location) if location != address_node.get('location'): logger.warning("Forcing soap:address location to HTTPS") return { 'address': location } @classmethod def parse(cls, definitions, xmlelement): """ Definition:: * <-- extensibility element (1) --> * * <-- extensibility element (2) --> * ? <-- extensibility element (3) --> ? <-- extensibility element (4) --> * * <-- extensibility element (5) --> * """ name = qname_attr(xmlelement, 'name', definitions.target_namespace) port_name = qname_attr(xmlelement, 'type', definitions.target_namespace) # The soap:binding element contains the transport method and # default style attribute for the operations. soap_node = xmlelement.find('soap:binding', namespaces=cls.nsmap) transport = soap_node.get('transport') supported_transports = [ 'http://schemas.xmlsoap.org/soap/http', 'http://www.w3.org/2003/05/soap/bindings/HTTP/', ] if transport not in supported_transports: raise NotImplementedError( "The binding transport %s is not supported (only soap/http)" % ( transport)) default_style = soap_node.get('style', 'document') obj = cls(definitions.wsdl, name, port_name, transport, default_style) for node in xmlelement.findall('wsdl:operation', namespaces=cls.nsmap): operation = SoapOperation.parse(definitions, node, obj, nsmap=cls.nsmap) obj._operation_add(operation) return obj class Soap11Binding(SoapBinding): nsmap = { 'soap': ns.SOAP_11, 'soap-env': ns.SOAP_ENV_11, 'wsdl': ns.WSDL, 'xsd': ns.XSD, } def process_error(self, doc, operation): fault_node = doc.find( 'soap-env:Body/soap-env:Fault', namespaces=self.nsmap) if fault_node is None: raise Fault( message='Unknown fault occured', code=None, actor=None, detail=etree_to_string(doc)) def get_text(name): child = fault_node.find(name) if child is not None: return child.text raise Fault( message=get_text('faultstring'), code=get_text('faultcode'), actor=get_text('faultactor'), detail=fault_node.find('detail')) def _set_http_headers(self, serialized, operation): serialized.headers['Content-Type'] = 'text/xml; charset=utf-8' class Soap12Binding(SoapBinding): nsmap = { 'soap': ns.SOAP_12, 'soap-env': ns.SOAP_ENV_12, 'wsdl': ns.WSDL, 'xsd': ns.XSD, } def process_error(self, doc, operation): fault_node = doc.find( 'soap-env:Body/soap-env:Fault', namespaces=self.nsmap) if fault_node is None: raise Fault( message='Unknown fault occured', code=None, actor=None, detail=etree_to_string(doc)) def get_text(name): child = fault_node.find(name) if child is not None: return child.text message = fault_node.findtext('soap-env:Reason/soap-env:Text', namespaces=self.nsmap) code = fault_node.findtext('soap-env:Code/soap-env:Value', namespaces=self.nsmap) # Extract the fault subcodes. These can be nested, as in subcodes can # also contain other subcodes. subcodes = [] subcode_element = fault_node.find('soap-env:Code/soap-env:Subcode', namespaces=self.nsmap) while subcode_element is not None: subcode_value_element = subcode_element.find('soap-env:Value', namespaces=self.nsmap) subcode_qname = as_qname(subcode_value_element.text, subcode_value_element.nsmap, None) subcodes.append(subcode_qname) subcode_element = subcode_element.find('soap-env:Subcode', namespaces=self.nsmap) # TODO: We should use the fault message as defined in the wsdl. detail_node = fault_node.find('soap-env:Detail', namespaces=self.nsmap) raise Fault( message=message, code=code, actor=None, detail=detail_node, subcodes=subcodes) def _set_http_headers(self, serialized, operation): serialized.headers['Content-Type'] = '; '.join([ 'application/soap+xml', 'charset=utf-8', 'action="%s"' % operation.soapaction ]) class SoapOperation(Operation): """Represent's an operation within a specific binding.""" def __init__(self, name, binding, nsmap, soapaction, style): super(SoapOperation, self).__init__(name, binding) self.nsmap = nsmap self.soapaction = soapaction self.style = style def process_reply(self, envelope): envelope_qname = etree.QName(self.nsmap['soap-env'], 'Envelope') if envelope.tag != envelope_qname: raise XMLSyntaxError(( "The XML returned by the server does not contain a valid " + "{%s}Envelope root element. The root element found is %s " ) % (envelope_qname.namespace, envelope.tag)) if self.output: return self.output.deserialize(envelope) @classmethod def parse(cls, definitions, xmlelement, binding, nsmap): """ Definition:: * ? ? ? <-- extensibility element (4) --> * * <-- extensibility element (5) --> * Example:: """ name = xmlelement.get('name') # The soap:operation element is required for soap/http bindings # and may be omitted for other bindings. soap_node = xmlelement.find('soap:operation', namespaces=binding.nsmap) action = None if soap_node is not None: action = soap_node.get('soapAction') style = soap_node.get('style', binding.default_style) else: style = binding.default_style obj = cls(name, binding, nsmap, action, style) if style == 'rpc': message_class = RpcMessage else: message_class = DocumentMessage for node in xmlelement.getchildren(): tag_name = etree.QName(node.tag).localname if tag_name not in ('input', 'output', 'fault'): continue msg = message_class.parse( definitions=definitions, xmlelement=node, operation=obj, nsmap=nsmap, type=tag_name) if tag_name == 'fault': obj.faults[msg.name] = msg else: setattr(obj, tag_name, msg) return obj def resolve(self, definitions): super(SoapOperation, self).resolve(definitions) for name, fault in self.faults.items(): if name in self.abstract.fault_messages: fault.resolve(definitions, self.abstract.fault_messages[name]) if self.output: self.output.resolve(definitions, self.abstract.output_message) if self.input: self.input.resolve(definitions, self.abstract.input_message) zeep-2.5.0/src/zeep/wsdl/definitions.py0000644000076500000240000002063713224161177022053 0ustar mvantellingenstaff00000000000000""" zeep.wsdl.definitions ~~~~~~~~~~~~~~~~~~~~~ A WSDL document exists out of a number of definitions. There are 6 major definitions, these are: - types - message - portType - binding - port - service This module defines the definitions which occur within a WSDL document, """ import warnings from collections import OrderedDict, namedtuple from six import python_2_unicode_compatible from zeep.exceptions import IncompleteOperation MessagePart = namedtuple('MessagePart', ['element', 'type']) class AbstractMessage(object): """Messages consist of one or more logical parts. Each part is associated with a type from some type system using a message-typing attribute. The set of message-typing attributes is extensible. WSDL defines several such message-typing attributes for use with XSD: - element: Refers to an XSD element using a QName. - type: Refers to an XSD simpleType or complexType using a QName. """ def __init__(self, name): self.name = name self.parts = OrderedDict() def __repr__(self): return '<%s(name=%r)>' % (self.__class__.__name__, self.name.text) def resolve(self, definitions): pass def add_part(self, name, element): self.parts[name] = element class AbstractOperation(object): """Abstract operations are defined in the wsdl's portType elements.""" def __init__(self, name, input_message=None, output_message=None, fault_messages=None, parameter_order=None): """Initialize the abstract operation. :param name: The name of the operation :type name: str :param input_message: Message to generate the request XML :type input_message: AbstractMessage :param output_message: Message to process the response XML :type output_message: AbstractMessage :param fault_messages: Dict of messages to handle faults :type fault_messages: dict of str: AbstractMessage """ self.name = name self.input_message = input_message self.output_message = output_message self.fault_messages = fault_messages self.parameter_order = parameter_order class PortType(object): def __init__(self, name, operations): self.name = name self.operations = operations def __repr__(self): return '<%s(name=%r)>' % ( self.__class__.__name__, self.name.text) def resolve(self, definitions): pass @python_2_unicode_compatible class Binding(object): """Base class for the various bindings (SoapBinding / HttpBinding) .. raw:: ascii Binding | +-> Operation | +-> ConcreteMessage | +-> AbstractMessage """ def __init__(self, wsdl, name, port_name): """Binding :param wsdl: :type wsdl: :param name: :type name: string :param port_name: :type port_name: string """ self.name = name self.port_name = port_name self.port_type = None self.wsdl = wsdl self._operations = {} def resolve(self, definitions): self.port_type = definitions.get('port_types', self.port_name.text) for name, operation in list(self._operations.items()): try: operation.resolve(definitions) except IncompleteOperation as exc: warnings.warn(str(exc)) del self._operations[name] def _operation_add(self, operation): # XXX: operation name is not unique self._operations[operation.name] = operation def __str__(self): return '%s: %s' % (self.__class__.__name__, self.name.text) def __repr__(self): return '<%s(name=%r, port_type=%r)>' % ( self.__class__.__name__, self.name.text, self.port_type) def get(self, key): try: return self._operations[key] except KeyError: raise ValueError("No such operation %r on %s" % (key, self.name)) @classmethod def match(cls, node): raise NotImplementedError() @classmethod def parse(cls, definitions, xmlelement): raise NotImplementedError() @python_2_unicode_compatible class Operation(object): """Concrete operation Contains references to the concrete messages """ def __init__(self, name, binding): self.name = name self.binding = binding self.abstract = None self.style = None self.input = None self.output = None self.faults = {} def resolve(self, definitions): try: self.abstract = self.binding.port_type.operations[self.name] except KeyError: raise IncompleteOperation( "The wsdl:operation %r was not found in the wsdl:portType %r" % ( self.name, self.binding.port_type.name.text)) def __repr__(self): return '<%s(name=%r, style=%r)>' % ( self.__class__.__name__, self.name, self.style) def __str__(self): if not self.input: return u'%s(missing input message)' % (self.name) retval = u'%s(%s)' % (self.name, self.input.signature()) if self.output: retval += u' -> %s' % (self.output.signature(as_output=True)) return retval def create(self, *args, **kwargs): return self.input.serialize(*args, **kwargs) def process_reply(self, envelope): raise NotImplementedError() @classmethod def parse(cls, wsdl, xmlelement, binding): """ Definition:: * <-- extensibility element (2) --> * ? <-- extensibility element (3) --> ? <-- extensibility element (4) --> * * <-- extensibility element (5) --> * """ raise NotImplementedError() @python_2_unicode_compatible class Port(object): """Specifies an address for a binding, thus defining a single communication endpoint. """ def __init__(self, name, binding_name, xmlelement): self.name = name self._resolve_context = { 'binding_name': binding_name, 'xmlelement': xmlelement, } # Set during resolve() self.binding = None self.binding_options = None def __repr__(self): return '<%s(name=%r, binding=%r, %r)>' % ( self.__class__.__name__, self.name, self.binding, self.binding_options) def __str__(self): return u'Port: %s (%s)' % (self.name, self.binding) def resolve(self, definitions): if self._resolve_context is None: return try: self.binding = definitions.get( 'bindings', self._resolve_context['binding_name'].text) except IndexError: return False if definitions.location: force_https = definitions.location.startswith('https') else: force_https = False self.binding_options = self.binding.process_service_port( self._resolve_context['xmlelement'], force_https) self._resolve_context = None return True @python_2_unicode_compatible class Service(object): """Used to aggregate a set of related ports. """ def __init__(self, name): self.ports = OrderedDict() self.name = name self._is_resolved = False def __str__(self): return u'Service: %s' % self.name def __repr__(self): return '<%s(name=%r, ports=%r)>' % ( self.__class__.__name__, self.name, self.ports) def resolve(self, definitions): if self._is_resolved: return unresolved = [] for name, port in self.ports.items(): is_resolved = port.resolve(definitions) if not is_resolved: unresolved.append(name) # Remove unresolved bindings (http etc) for name in unresolved: del self.ports[name] self._is_resolved = True def add_port(self, port): self.ports[port.name] = port zeep-2.5.0/src/zeep/wsdl/messages/0000755000076500000240000000000013224162010020750 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/wsdl/messages/__init__.py0000644000076500000240000000112113224161177023071 0ustar mvantellingenstaff00000000000000""" zeep.wsdl.messages ~~~~~~~~~~~~~~~~~~ The messages are responsible for serializing and deserializing .. inheritance-diagram:: zeep.wsdl.messages.soap.DocumentMessage zeep.wsdl.messages.soap.RpcMessage zeep.wsdl.messages.http.UrlEncoded zeep.wsdl.messages.http.UrlReplacement zeep.wsdl.messages.mime.MimeContent zeep.wsdl.messages.mime.MimeXML zeep.wsdl.messages.mime.MimeMultipart :parts: 1 """ from .http import * # noqa from .mime import * # noqa from .soap import * # noqa zeep-2.5.0/src/zeep/wsdl/messages/base.py0000644000076500000240000000320713224161177022253 0ustar mvantellingenstaff00000000000000""" zeep.wsdl.messages.base ~~~~~~~~~~~~~~~~~~~~~~~ """ from collections import namedtuple from zeep import xsd SerializedMessage = namedtuple( 'SerializedMessage', ['path', 'headers', 'content']) class ConcreteMessage(object): """Represents the wsdl:binding -> wsdl:operation -> input/ouput node""" def __init__(self, wsdl, name, operation): assert wsdl assert operation self.wsdl = wsdl self.namespace = {} self.operation = operation self.name = name def serialize(self, *args, **kwargs): raise NotImplementedError() def deserialize(self, node): raise NotImplementedError() def signature(self, as_output=False): if not self.body: return None if as_output: if isinstance(self.body.type, xsd.ComplexType): try: if len(self.body.type.elements) == 1: return self.body.type.elements[0][1].type.signature( schema=self.wsdl.types, standalone=False) except AttributeError: return None return self.body.type.signature(schema=self.wsdl.types, standalone=False) parts = [self.body.type.signature(schema=self.wsdl.types, standalone=False)] if getattr(self, 'header', None): parts.append('_soapheaders={%s}' % self.header.signature( schema=self.wsdl.types), standalone=False) return ', '.join(part for part in parts if part) @classmethod def parse(cls, wsdl, xmlelement, abstract_message, operation): raise NotImplementedError() zeep-2.5.0/src/zeep/wsdl/messages/http.py0000644000076500000240000000662713224161177022331 0ustar mvantellingenstaff00000000000000""" zeep.wsdl.messages.http ~~~~~~~~~~~~~~~~~~~~~~~ """ from zeep import xsd from zeep.wsdl.messages.base import ConcreteMessage, SerializedMessage __all__ = [ 'UrlEncoded', 'UrlReplacement', ] class HttpMessage(ConcreteMessage): """Base class for HTTP Binding messages""" def resolve(self, definitions, abstract_message): self.abstract = abstract_message children = [] for name, message in self.abstract.parts.items(): if message.element: elm = message.element.clone(name) else: elm = xsd.Element(name, message.type) children.append(elm) self.body = xsd.Element( self.operation.name, xsd.ComplexType(xsd.Sequence(children))) class UrlEncoded(HttpMessage): """The urlEncoded element indicates that all the message parts are encoded into the HTTP request URI using the standard URI-encoding rules (name1=value&name2=value...). The names of the parameters correspond to the names of the message parts. Each value contributed by the part is encoded using a name=value pair. This may be used with GET to specify URL encoding, or with POST to specify a FORM-POST. For GET, the "?" character is automatically appended as necessary. """ def serialize(self, *args, **kwargs): params = {key: None for key in self.abstract.parts.keys()} params.update(zip(self.abstract.parts.keys(), args)) params.update(kwargs) headers = {'Content-Type': 'text/xml; charset=utf-8'} return SerializedMessage( path=self.operation.location, headers=headers, content=params) @classmethod def parse(cls, definitions, xmlelement, operation): name = xmlelement.get('name') obj = cls(definitions.wsdl, name, operation) return obj class UrlReplacement(HttpMessage): """The http:urlReplacement element indicates that all the message parts are encoded into the HTTP request URI using a replacement algorithm. - The relative URI value of http:operation is searched for a set of search patterns. - The search occurs before the value of the http:operation is combined with the value of the location attribute from http:address. - There is one search pattern for each message part. The search pattern string is the name of the message part surrounded with parenthesis "(" and ")". - For each match, the value of the corresponding message part is substituted for the match at the location of the match. - Matches are performed before any values are replaced (replaced values do not trigger additional matches). Message parts MUST NOT have repeating values. """ def serialize(self, *args, **kwargs): params = {key: None for key in self.abstract.parts.keys()} params.update(zip(self.abstract.parts.keys(), args)) params.update(kwargs) headers = {'Content-Type': 'text/xml; charset=utf-8'} path = self.operation.location for key, value in params.items(): path = path.replace('(%s)' % key, value if value is not None else '') return SerializedMessage(path=path, headers=headers, content='') @classmethod def parse(cls, definitions, xmlelement, operation): name = xmlelement.get('name') obj = cls(definitions.wsdl, name, operation) return obj zeep-2.5.0/src/zeep/wsdl/messages/mime.py0000644000076500000240000001570413224161177022275 0ustar mvantellingenstaff00000000000000""" zeep.wsdl.messages.mime ~~~~~~~~~~~~~~~~~~~~~~~ """ import six from defusedxml.lxml import fromstring from lxml import etree from zeep import ns, xsd from zeep.helpers import serialize_object from zeep.wsdl.messages.base import ConcreteMessage, SerializedMessage from zeep.wsdl.utils import etree_to_string __all__ = [ 'MimeContent', 'MimeXML', 'MimeMultipart', ] class MimeMessage(ConcreteMessage): _nsmap = { 'mime': ns.MIME, } def __init__(self, wsdl, name, operation, part_name): super(MimeMessage, self).__init__(wsdl, name, operation) self.part_name = part_name def resolve(self, definitions, abstract_message): """Resolve the body element The specs are (again) not really clear how to handle the message parts in relation the message element vs type. The following strategy is chosen, which seem to work: - If the message part has a name and it maches then set it as body - If the message part has a name but it doesn't match but there are no other message parts, then just use that one. - If the message part has no name then handle it like an rpc call, in other words, each part is an argument. """ self.abstract = abstract_message if self.part_name and self.abstract.parts: if self.part_name in self.abstract.parts: message = self.abstract.parts[self.part_name] elif len(self.abstract.parts) == 1: message = list(self.abstract.parts.values())[0] else: raise ValueError( "Multiple parts for message %r while no matching part found" % self.part_name) if message.element: self.body = message.element else: elm = xsd.Element(self.part_name, message.type) self.body = xsd.Element( self.operation.name, xsd.ComplexType(xsd.Sequence([elm]))) else: children = [] for name, message in self.abstract.parts.items(): if message.element: elm = message.element.clone(name) else: elm = xsd.Element(name, message.type) children.append(elm) self.body = xsd.Element( self.operation.name, xsd.ComplexType(xsd.Sequence(children))) class MimeContent(MimeMessage): """WSDL includes a way to bind abstract types to concrete messages in some MIME format. Bindings for the following MIME types are defined: - multipart/related - text/xml - application/x-www-form-urlencoded - Others (by specifying the MIME type string) The set of defined MIME types is both large and evolving, so it is not a goal for WSDL to exhaustively define XML grammar for each MIME type. :param wsdl: The main wsdl document :type wsdl: zeep.wsdl.wsdl.Document :param name: :param operation: The operation to which this message belongs :type operation: zeep.wsdl.bindings.soap.SoapOperation :param part_name: :type type: str """ def __init__(self, wsdl, name, operation, content_type, part_name): super(MimeContent, self).__init__(wsdl, name, operation, part_name) self.content_type = content_type def serialize(self, *args, **kwargs): value = self.body(*args, **kwargs) headers = { 'Content-Type': self.content_type } data = '' if self.content_type == 'application/x-www-form-urlencoded': items = serialize_object(value) data = six.moves.urllib.parse.urlencode(items) elif self.content_type == 'text/xml': document = etree.Element('root') self.body.render(document, value) data = etree_to_string(document.getchildren()[0]) return SerializedMessage( path=self.operation.location, headers=headers, content=data) def deserialize(self, node): node = fromstring(node) part = list(self.abstract.parts.values())[0] return part.type.parse_xmlelement(node) @classmethod def parse(cls, definitions, xmlelement, operation): name = xmlelement.get('name') part_name = content_type = None content_node = xmlelement.find('mime:content', namespaces=cls._nsmap) if content_node is not None: content_type = content_node.get('type') part_name = content_node.get('part') obj = cls(definitions.wsdl, name, operation, content_type, part_name) return obj class MimeXML(MimeMessage): """To specify XML payloads that are not SOAP compliant (do not have a SOAP Envelope), but do have a particular schema, the mime:mimeXml element may be used to specify that concrete schema. The part attribute refers to a message part defining the concrete schema of the root XML element. The part attribute MAY be omitted if the message has only a single part. The part references a concrete schema using the element attribute for simple parts or type attribute for composite parts :param wsdl: The main wsdl document :type wsdl: zeep.wsdl.wsdl.Document :param name: :param operation: The operation to which this message belongs :type operation: zeep.wsdl.bindings.soap.SoapOperation :param part_name: :type type: str """ def serialize(self, *args, **kwargs): raise NotImplementedError() def deserialize(self, node): node = fromstring(node) part = next(iter(self.abstract.parts.values()), None) return part.element.parse(node, self.wsdl.types) @classmethod def parse(cls, definitions, xmlelement, operation): name = xmlelement.get('name') part_name = None content_node = xmlelement.find('mime:mimeXml', namespaces=cls._nsmap) if content_node is not None: part_name = content_node.get('part') obj = cls(definitions.wsdl, name, operation, part_name) return obj class MimeMultipart(MimeMessage): """The multipart/related MIME type aggregates an arbitrary set of MIME formatted parts into one message using the MIME type "multipart/related". The mime:multipartRelated element describes the concrete format of such a message:: * <-- mime element --> The mime:part element describes each part of a multipart/related message. MIME elements appear within mime:part to specify the concrete MIME type for the part. If more than one MIME element appears inside a mime:part, they are alternatives. :param wsdl: The main wsdl document :type wsdl: zeep.wsdl.wsdl.Document :param name: :param operation: The operation to which this message belongs :type operation: zeep.wsdl.bindings.soap.SoapOperation :param part_name: :type type: str """ pass zeep-2.5.0/src/zeep/wsdl/messages/multiref.py0000644000076500000240000000750713224161177023177 0ustar mvantellingenstaff00000000000000import re from lxml import etree def process_multiref(node): """Iterate through the tree and replace the referened elements. This method replaces the nodes with an href attribute and replaces it with the elements it's referencing to (which have an id attribute).abs """ multiref_objects = { elm.attrib['id']: elm for elm in node.xpath('*[@id]') } if not multiref_objects: return used_nodes = [] def process(node): """Recursive""" # TODO (In Soap 1.2 this is 'ref') href = node.attrib.get('href') if href and href.startswith('#'): obj = multiref_objects.get(href[1:]) if obj is not None: used_nodes.append(obj) node = _dereference_element(obj, node) for child in node: process(child) process(node) # Remove the old dereferenced nodes from the tree for node in used_nodes: parent = node.getparent() if parent is not None: parent.remove(node) def _dereference_element(source, target): """Move the referenced node (source) in the main response tree (target) :type source: lxml.etree._Element :type target: lxml.etree._Element :rtype target: lxml.etree._Element """ specific_nsmap = { k: v for k, v in source.nsmap.items() if k not in target.nsmap } new = _clone_element(source, target.tag, specific_nsmap) # Replace the node with the new dereferenced node parent = target.getparent() parent.insert(parent.index(target), new) parent.remove(target) # Update all descendants for obj in new.iter(): _prefix_node(obj) return new def _clone_element(node, tag_name=None, nsmap=None): """Clone the given node and return it. This is a recursive call since we want to clone the children the same way. :type source: lxml.etree._Element :type tag_name: str :type nsmap: dict :rtype source: lxml.etree._Element """ tag_name = tag_name or node.tag nsmap = node.nsmap if nsmap is None else nsmap new = etree.Element(tag_name, nsmap=nsmap) for child in node: new_child = _clone_element(child) new.append(new_child) new.text = node.text for key, value in _get_attributes(node): new.set(key, value) return new def _prefix_node(node): """Translate the internal attribute values back to prefixed tokens. This reverses the translation done in _get_attributes For example:: { 'foo:type': '{http://example.com}string' } will be converted to: { 'foo:type': 'example:string' } :type node: lxml.etree._Element """ reverse_nsmap = {v: k for k, v in node.nsmap.items()} prefix_re = re.compile('^{([^}]+)}(.*)') for key, value in node.attrib.items(): if value.startswith('{'): match = prefix_re.match(value) namespace, localname = match.groups() if namespace in reverse_nsmap: value = '%s:%s' % (reverse_nsmap.get(namespace), localname) node.set(key, value) def _get_attributes(node): """Return the node attributes where prefixed values are dereferenced. For example the following xml:: will return the dict:: { 'foo:type': '{http://example.com}string' } :type node: lxml.etree._Element """ nsmap = node.nsmap result = {} for key, value in node.attrib.items(): if value.count(':') == 1: prefix, localname = value.split(':') if prefix in nsmap: namespace = nsmap[prefix] value = '{%s}%s' % (namespace, localname) result[key] = value return list(result.items()) zeep-2.5.0/src/zeep/wsdl/messages/soap.py0000644000076500000240000004274613224161177022316 0ustar mvantellingenstaff00000000000000""" zeep.wsdl.messages.soap ~~~~~~~~~~~~~~~~~~~~~~~ """ import copy from collections import OrderedDict from lxml import etree from lxml.builder import ElementMaker from zeep import exceptions, xsd from zeep.utils import as_qname from zeep.wsdl.messages.base import ConcreteMessage, SerializedMessage from zeep.wsdl.messages.multiref import process_multiref __all__ = [ 'DocumentMessage', 'RpcMessage', ] class SoapMessage(ConcreteMessage): """Base class for the SOAP Document and RPC messages :param wsdl: The main wsdl document :type wsdl: zeep.wsdl.Document :param name: :param operation: The operation to which this message belongs :type operation: zeep.wsdl.bindings.soap.SoapOperation :param type: 'input' or 'output' :type type: str :param nsmap: The namespace mapping :type nsmap: dict """ def __init__(self, wsdl, name, operation, type, nsmap): super(SoapMessage, self).__init__(wsdl, name, operation) self.nsmap = nsmap self.abstract = None # Set during resolve() self.type = type self.body = None self.header = None self.envelope = None def serialize(self, *args, **kwargs): """Create a SerializedMessage for this message""" nsmap = { 'soap-env': self.nsmap['soap-env'] } nsmap.update(self.wsdl.types._prefix_map_custom) soap = ElementMaker(namespace=self.nsmap['soap-env'], nsmap=nsmap) # Create the soap:header element headers_value = kwargs.pop('_soapheaders', None) header = self._serialize_header(headers_value, nsmap) # Create the soap:body element body = soap.Body() if self.body: body_value = self.body(*args, **kwargs) self.body.render(body, body_value) # Create the soap:envelope envelope = soap.Envelope() if header is not None: envelope.append(header) envelope.append(body) # XXX: This is only used in Soap 1.1 so should be moved to the the # Soap11Binding._set_http_headers(). But let's keep it like this for # now. headers = { 'SOAPAction': '"%s"' % self.operation.soapaction } return SerializedMessage( path=None, headers=headers, content=envelope) def deserialize(self, envelope): """Deserialize the SOAP:Envelope and return a CompoundValue with the result. """ if not self.envelope: return None body = envelope.find('soap-env:Body', namespaces=self.nsmap) body_result = self._deserialize_body(body) header = envelope.find('soap-env:Header', namespaces=self.nsmap) headers_result = self._deserialize_headers(header) kwargs = body_result kwargs.update(headers_result) result = self.envelope(**kwargs) # If the message if self.header.type._element: return result result = result.body if result is None or len(result) == 0: return None elif len(result) > 1: return result # Check if we can remove the wrapping object to make the return value # easier to use. result = next(iter(result.__values__.values())) if isinstance(result, xsd.CompoundValue): children = result._xsd_type.elements attributes = result._xsd_type.attributes if len(children) == 1 and len(attributes) == 0: item_name, item_element = children[0] retval = getattr(result, item_name) return retval return result def signature(self, as_output=False): if not self.envelope: return None if as_output: if isinstance(self.envelope.type, xsd.ComplexType): try: if len(self.envelope.type.elements) == 1: return self.envelope.type.elements[0][1].type.signature( schema=self.wsdl.types, standalone=False) except AttributeError: return None return self.envelope.type.signature(schema=self.wsdl.types, standalone=False) if self.body: parts = [self.body.type.signature(schema=self.wsdl.types, standalone=False)] else: parts = [] if self.header.type._element: parts.append('_soapheaders={%s}' % self.header.type.signature( schema=self.wsdl.types, standalone=False)) return ', '.join(part for part in parts if part) @classmethod def parse(cls, definitions, xmlelement, operation, type, nsmap): """Parse a wsdl:binding/wsdl:operation/wsdl:operation for the SOAP implementation. Each wsdl:operation can contain three child nodes: - input - output - fault Definition for input/output:: * * And the definition for fault:: """ name = xmlelement.get('name') obj = cls(definitions.wsdl, name, operation, nsmap=nsmap, type=type) body_data = None header_data = None # After some profiling it turns out that .find() and .findall() in this # case are twice as fast as the xpath method body = xmlelement.find('soap:body', namespaces=operation.binding.nsmap) if body is not None: body_data = cls._parse_body(body) # Parse soap:header (multiple) elements = xmlelement.findall( 'soap:header', namespaces=operation.binding.nsmap) header_data = cls._parse_header( elements, definitions.target_namespace, operation) obj._resolve_info = { 'body': body_data, 'header': header_data } return obj @classmethod def _parse_body(cls, xmlelement): """Parse soap:body and return a dict with data to resolve it. """ return { 'part': xmlelement.get('part'), 'use': xmlelement.get('use', 'literal'), 'encodingStyle': xmlelement.get('encodingStyle'), 'namespace': xmlelement.get('namespace'), } @classmethod def _parse_header(cls, xmlelements, tns, operation): """Parse the soap:header and optionally included soap:headerfault elements * The header can optionally contain one ore more soap:headerfault elements which can contain the same attributes as the soap:header:: * """ result = [] for xmlelement in xmlelements: data = cls._parse_header_element(xmlelement, tns) # Add optional soap:headerfault elements data['faults'] = [] fault_elements = xmlelement.findall( 'soap:headerfault', namespaces=operation.binding.nsmap) for fault_element in fault_elements: fault_data = cls._parse_header_element(fault_element, tns) data['faults'].append(fault_data) result.append(data) return result @classmethod def _parse_header_element(cls, xmlelement, tns): attributes = xmlelement.attrib message_qname = as_qname( attributes['message'], xmlelement.nsmap, tns) try: return { 'message': message_qname, 'part': attributes['part'], 'use': attributes['use'], 'encodingStyle': attributes.get('encodingStyle'), 'namespace': attributes.get('namespace'), } except KeyError: raise exceptions.WsdlSyntaxError("Invalid soap:header(fault)") def resolve(self, definitions, abstract_message): """Resolve the data in the self._resolve_info dict (set via parse()) This creates three xsd.Element objects: - self.header - self.body - self.envelope (combination of headers and body) XXX headerfaults are not implemented yet. """ info = self._resolve_info del self._resolve_info # If this message has no parts then we have nothing to do. This might # happen for output messages which don't return anything. if not abstract_message.parts and self.type != 'input': return self.abstract = abstract_message parts = OrderedDict(self.abstract.parts) self.header = self._resolve_header(info['header'], definitions, parts) self.body = self._resolve_body(info['body'], definitions, parts) self.envelope = self._create_envelope_element() def _create_envelope_element(self): """Create combined `envelope` complexType which contains both the elements from the body and the headers. """ all_elements = xsd.Sequence([]) if self.header.type._element: all_elements.append( xsd.Element('{%s}header' % self.nsmap['soap-env'], self.header.type)) all_elements.append( xsd.Element( '{%s}body' % self.nsmap['soap-env'], self.body.type if self.body else None)) return xsd.Element('{%s}envelope' % self.nsmap['soap-env'], xsd.ComplexType(all_elements)) def _serialize_header(self, headers_value, nsmap): if not headers_value: return headers_value = copy.deepcopy(headers_value) soap = ElementMaker(namespace=self.nsmap['soap-env'], nsmap=nsmap) header = soap.Header() if isinstance(headers_value, list): for header_value in headers_value: if hasattr(header_value, '_xsd_elm'): header_value._xsd_elm.render(header, header_value) elif hasattr(header_value, '_xsd_type'): header_value._xsd_type.render(header, header_value) elif isinstance(header_value, etree._Element): header.append(header_value) else: raise ValueError("Invalid value given to _soapheaders") elif isinstance(headers_value, dict): if not self.header: raise ValueError( "_soapheaders only accepts a dictionary if the wsdl " "defines the headers.") # Only render headers for which we have a value headers_value = self.header(**headers_value) for name, elm in self.header.type.elements: if name in headers_value and headers_value[name] is not None: elm.render(header, headers_value[name], ['header', name]) else: raise ValueError("Invalid value given to _soapheaders") return header def _deserialize_headers(self, xmlelement): """Deserialize the values in the SOAP:Header element""" if not self.header or xmlelement is None: return {} result = self.header.parse(xmlelement, self.wsdl.types) if result is not None: return {'header': result} return {} def _resolve_header(self, info, definitions, parts): name = etree.QName(self.nsmap['soap-env'], 'Header') container = xsd.All(consume_other=True) if not info: return xsd.Element(name, xsd.ComplexType(container)) for item in info: message_name = item['message'].text part_name = item['part'] message = definitions.get('messages', message_name) if message == self.abstract: del parts[part_name] part = message.parts[part_name] if part.element: element = part.element.clone() element.attr_name = part_name else: element = xsd.Element(part_name, part.type) container.append(element) return xsd.Element(name, xsd.ComplexType(container)) class DocumentMessage(SoapMessage): """In the document message there are no additional wrappers, and the message parts appear directly under the SOAP Body element. .. inheritance-diagram:: zeep.wsdl.messages.soap.DocumentMessage :parts: 1 :param wsdl: The main wsdl document :type wsdl: zeep.wsdl.Document :param name: :param operation: The operation to which this message belongs :type operation: zeep.wsdl.bindings.soap.SoapOperation :param type: 'input' or 'output' :type type: str :param nsmap: The namespace mapping :type nsmap: dict """ def __init__(self, *args, **kwargs): super(DocumentMessage, self).__init__(*args, **kwargs) self._is_body_wrapped = False def _deserialize_body(self, xmlelement): if self._is_body_wrapped: result = self.body.parse(xmlelement, self.wsdl.types) else: # For now we assume that the body only has one child since only # one part is specified in the wsdl. This should be handled way # better # XXX xmlelement = xmlelement.getchildren()[0] result = self.body.parse(xmlelement, self.wsdl.types) return {'body': result} def _resolve_body(self, info, definitions, parts): name = etree.QName(self.nsmap['soap-env'], 'Body') if not info or not parts: return None # If the part name is omitted then all parts are available under # the soap:body tag. Otherwise only the part with the given name. if info['part']: part_name = info['part'] sub_elements = [parts[part_name].element] else: sub_elements = [] for part_name, part in parts.items(): element = part.element.clone() element.attr_name = part_name or element.name sub_elements.append(element) if len(sub_elements) > 1: self._is_body_wrapped = True return xsd.Element(name, xsd.ComplexType(xsd.All(sub_elements))) else: self._is_body_wrapped = False return sub_elements[0] class RpcMessage(SoapMessage): """In RPC messages each part is a parameter or a return value and appears inside a wrapper element within the body. The wrapper element is named identically to the operation name and its namespace is the value of the namespace attribute. Each message part (parameter) appears under the wrapper, represented by an accessor named identically to the corresponding parameter of the call. Parts are arranged in the same order as the parameters of the call. .. inheritance-diagram:: zeep.wsdl.messages.soap.DocumentMessage :parts: 1 :param wsdl: The main wsdl document :type wsdl: zeep.wsdl.Document :param name: :param operation: The operation to which this message belongs :type operation: zeep.wsdl.bindings.soap.SoapOperation :param type: 'input' or 'output' :type type: str :param nsmap: The namespace mapping :type nsmap: dict """ def _resolve_body(self, info, definitions, parts): """Return an XSD element for the SOAP:Body. Each part is a parameter or a return value and appears inside a wrapper element within the body named identically to the operation name and its namespace is the value of the namespace attribute. """ if not info: return None namespace = info['namespace'] if self.type == 'input': tag_name = etree.QName(namespace, self.operation.name) else: tag_name = etree.QName(namespace, self.abstract.name.localname) # Create the xsd element to create/parse the response. Each part # is a sub element of the root node (which uses the operation name) elements = [] for name, msg in parts.items(): if msg.element: elements.append(msg.element) else: elements.append(xsd.Element(name, msg.type)) return xsd.Element(tag_name, xsd.ComplexType(xsd.Sequence(elements))) def _deserialize_body(self, body_element): """The name of the wrapper element is not defined. The WS-I defines that it should be the operation name with the 'Response' string as suffix. But lets just do it really stupid for now and use the first element. """ process_multiref(body_element) response_element = body_element.getchildren()[0] if self.body: result = self.body.parse(response_element, self.wsdl.types) return {'body': result} return {'body': None} zeep-2.5.0/src/zeep/wsdl/messages/xop.py0000644000076500000240000000137113224161177022147 0ustar mvantellingenstaff00000000000000import base64 def process_xop(document, message_pack): """Iterate through the tree and replace the xop:include elements.""" xop_nodes = document.xpath('//xop:Include', namespaces={ 'xop': 'http://www.w3.org/2004/08/xop/include' }) num_replaced = 0 for xop_node in xop_nodes: href = xop_node.get('href') if href.startswith('cid:'): href = '<%s>' % href[4:] value = message_pack.get_by_content_id(href) if not value: raise ValueError("No part found for: %r" % xop_node.get('href')) num_replaced += 1 xop_parent = xop_node.getparent() xop_parent.remove(xop_node) xop_parent.text = base64.b64encode(value.content) return num_replaced > 0 zeep-2.5.0/src/zeep/wsdl/parse.py0000644000076500000240000001524013224161177020644 0ustar mvantellingenstaff00000000000000""" zeep.wsdl.parse ~~~~~~~~~~~~~~~ """ from lxml import etree from zeep.exceptions import IncompleteMessage, LookupError, NamespaceError from zeep.utils import qname_attr from zeep.wsdl import definitions NSMAP = { 'wsdl': 'http://schemas.xmlsoap.org/wsdl/', 'wsaw': 'http://www.w3.org/2006/05/addressing/wsdl', } def parse_abstract_message(wsdl, xmlelement): """Create an AbstractMessage object from a xml element. Definition:: * * :param wsdl: The parent definition instance :type wsdl: zeep.wsdl.wsdl.Definition :param xmlelement: The XML node :type xmlelement: lxml.etree._Element :rtype: zeep.wsdl.definitions.AbstractMessage """ tns = wsdl.target_namespace message_name = qname_attr(xmlelement, 'name', tns) parts = [] for part in xmlelement.findall('wsdl:part', namespaces=NSMAP): part_name = part.get('name') part_element = qname_attr(part, 'element', tns) part_type = qname_attr(part, 'type', tns) try: if part_element is not None: part_element = wsdl.types.get_element(part_element) if part_type is not None: part_type = wsdl.types.get_type(part_type) except (NamespaceError, LookupError): raise IncompleteMessage(( "The wsdl:message for %r contains an invalid part (%r): " "invalid xsd type or elements" ) % (message_name.text, part_name)) part = definitions.MessagePart(part_element, part_type) parts.append((part_name, part)) # Create the object, add the parts and return it msg = definitions.AbstractMessage(message_name) for part_name, part in parts: msg.add_part(part_name, part) return msg def parse_abstract_operation(wsdl, xmlelement): """Create an AbstractOperation object from a xml element. This is called from the parse_port_type function since the abstract operations are part of the port type element. Definition:: * ? ? ? ? ? * ? :param wsdl: The parent definition instance :type wsdl: zeep.wsdl.wsdl.Definition :param xmlelement: The XML node :type xmlelement: lxml.etree._Element :rtype: zeep.wsdl.definitions.AbstractOperation """ name = xmlelement.get('name') kwargs = { 'fault_messages': {} } for msg_node in xmlelement.getchildren(): tag_name = etree.QName(msg_node.tag).localname if tag_name not in ('input', 'output', 'fault'): continue param_msg = qname_attr( msg_node, 'message', wsdl.target_namespace) param_name = msg_node.get('name') try: param_value = wsdl.get('messages', param_msg.text) except IndexError: return if tag_name == 'input': kwargs['input_message'] = param_value elif tag_name == 'output': kwargs['output_message'] = param_value else: kwargs['fault_messages'][param_name] = param_value wsa_action = msg_node.get(etree.QName(NSMAP['wsaw'], 'Action')) param_value.wsa_action = wsa_action kwargs['name'] = name kwargs['parameter_order'] = xmlelement.get('parameterOrder') return definitions.AbstractOperation(**kwargs) def parse_port_type(wsdl, xmlelement): """Create a PortType object from a xml element. Definition:: * :param wsdl: The parent definition instance :type wsdl: zeep.wsdl.wsdl.Definition :param xmlelement: The XML node :type xmlelement: lxml.etree._Element :rtype: zeep.wsdl.definitions.PortType """ name = qname_attr(xmlelement, 'name', wsdl.target_namespace) operations = {} for elm in xmlelement.findall('wsdl:operation', namespaces=NSMAP): operation = parse_abstract_operation(wsdl, elm) if operation: operations[operation.name] = operation return definitions.PortType(name, operations) def parse_port(wsdl, xmlelement): """Create a Port object from a xml element. This is called via the parse_service function since ports are part of the service xml elements. Definition:: * ? <-- extensibility element --> :param wsdl: The parent definition instance :type wsdl: zeep.wsdl.wsdl.Definition :param xmlelement: The XML node :type xmlelement: lxml.etree._Element :rtype: zeep.wsdl.definitions.Port """ name = xmlelement.get('name') binding_name = qname_attr(xmlelement, 'binding', wsdl.target_namespace) return definitions.Port(name, binding_name=binding_name, xmlelement=xmlelement) def parse_service(wsdl, xmlelement): """ Definition:: * ? * ? <-- extensibility element --> <-- extensibility element --> Example:: My first service :param wsdl: The parent definition instance :type wsdl: zeep.wsdl.wsdl.Definition :param xmlelement: The XML node :type xmlelement: lxml.etree._Element :rtype: zeep.wsdl.definitions.Service """ name = xmlelement.get('name') ports = [] for port_node in xmlelement.findall('wsdl:port', namespaces=NSMAP): port = parse_port(wsdl, port_node) if port: ports.append(port) obj = definitions.Service(name) for port in ports: obj.add_port(port) return obj zeep-2.5.0/src/zeep/wsdl/utils.py0000644000076500000240000000204513224161177020671 0ustar mvantellingenstaff00000000000000""" zeep.wsdl.utils ~~~~~~~~~~~~~~~ """ from lxml import etree from six.moves.urllib.parse import urlparse, urlunparse from zeep.utils import detect_soap_env def get_or_create_header(envelope): soap_env = detect_soap_env(envelope) # look for the Header element and create it if not found header_qname = '{%s}Header' % soap_env header = envelope.find(header_qname) if header is None: header = etree.Element(header_qname) envelope.insert(0, header) return header def etree_to_string(node): return etree.tostring( node, pretty_print=False, xml_declaration=True, encoding='utf-8') def url_http_to_https(value): parts = urlparse(value) if parts.scheme != 'http': return value # Check if the url contains ':80' and remove it if that is the case netloc_parts = parts.netloc.rsplit(':', 1) if len(netloc_parts) == 2 and netloc_parts[1] == '80': netloc = netloc_parts[0] else: netloc = parts.netloc return urlunparse(('https', netloc) + parts[2:]) zeep-2.5.0/src/zeep/wsdl/wsdl.py0000644000076500000240000003514513224161177020511 0ustar mvantellingenstaff00000000000000""" zeep.wsdl.wsdl ~~~~~~~~~~~~~~ """ from __future__ import print_function import logging import operator import os import warnings from collections import OrderedDict import six from lxml import etree from zeep.exceptions import IncompleteMessage from zeep.loader import absolute_location, is_relative_path, load_external from zeep.utils import findall_multiple_ns from zeep.wsdl import parse from zeep.xsd import Schema NSMAP = { 'wsdl': 'http://schemas.xmlsoap.org/wsdl/', } logger = logging.getLogger(__name__) class Document(object): """A WSDL Document exists out of one or more definitions. There is always one 'root' definition which should be passed as the location to the Document. This definition can import other definitions. These imports are non-transitive, only the definitions defined in the imported document are available in the parent definition. This Document is mostly just a simple interface to the root definition. After all definitions are loaded the definitions are resolved. This resolves references which were not yet available during the initial parsing phase. :param location: Location of this WSDL :type location: string :param transport: The transport object to be used :type transport: zeep.transports.Transport :param base: The base location of this document :type base: str :param strict: Indicates if strict mode is enabled :type strict: bool """ def __init__(self, location, transport, base=None, strict=True): """Initialize a WSDL document. The root definition properties are exposed as entry points. """ if isinstance(location, six.string_types): if is_relative_path(location): location = os.path.abspath(location) self.location = location else: self.location = base self.transport = transport self.strict = strict # Dict with all definition objects within this WSDL self._definitions = {} self.types = Schema( node=None, transport=self.transport, location=self.location, strict=self.strict) document = self._get_xml_document(location) root_definitions = Definition(self, document, self.location) root_definitions.resolve_imports() # Make the wsdl definitions public self.messages = root_definitions.messages self.port_types = root_definitions.port_types self.bindings = root_definitions.bindings self.services = root_definitions.services def __repr__(self): return '' % self.location def dump(self): print('') print("Prefixes:") for prefix, namespace in self.types.prefix_map.items(): print(' ' * 4, '%s: %s' % (prefix, namespace)) print('') print("Global elements:") for elm_obj in sorted(self.types.elements, key=lambda k: k.qname): value = elm_obj.signature(schema=self.types) print(' ' * 4, value) print('') print("Global types:") for type_obj in sorted(self.types.types, key=lambda k: k.qname or ''): value = type_obj.signature(schema=self.types) print(' ' * 4, value) print('') print("Bindings:") for binding_obj in sorted(self.bindings.values(), key=lambda k: six.text_type(k)): print(' ' * 4, six.text_type(binding_obj)) print('') for service in self.services.values(): print(six.text_type(service)) for port in service.ports.values(): print(' ' * 4, six.text_type(port)) print(' ' * 8, 'Operations:') operations = sorted( port.binding._operations.values(), key=operator.attrgetter('name')) for operation in operations: print('%s%s' % (' ' * 12, six.text_type(operation))) print('') def _get_xml_document(self, location): """Load the XML content from the given location and return an lxml.Element object. :param location: The URL of the document to load :type location: string """ return load_external( location, self.transport, self.location, strict=self.strict) def _add_definition(self, definition): key = (definition.target_namespace, definition.location) self._definitions[key] = definition class Definition(object): """The Definition represents one wsdl:definition within a Document. :param wsdl: The wsdl """ def __init__(self, wsdl, doc, location): """fo :param wsdl: The wsdl """ logger.debug("Creating definition for %s", location) self.wsdl = wsdl self.location = location self.types = wsdl.types self.port_types = {} self.messages = {} self.bindings = {} self.services = OrderedDict() self.imports = {} self._resolved_imports = False self.target_namespace = doc.get('targetNamespace') self.wsdl._add_definition(self) self.nsmap = doc.nsmap # Process the definitions self.parse_imports(doc) self.parse_types(doc) self.messages = self.parse_messages(doc) self.port_types = self.parse_ports(doc) self.bindings = self.parse_binding(doc) self.services = self.parse_service(doc) def __repr__(self): return '' % self.location def get(self, name, key, _processed=None): container = getattr(self, name) if key in container: return container[key] # Turns out that no one knows if the wsdl import statement is # transitive or not. WSDL/SOAP specs are awesome... So lets just do it. # TODO: refactor me into something more sane _processed = _processed or set() if self.target_namespace not in _processed: _processed.add(self.target_namespace) for definition in self.imports.values(): try: return definition.get(name, key, _processed) except IndexError: # Try to see if there is an item which has no namespace # but where the localname matches. This is basically for # #356 but in the future we should also ignore mismatching # namespaces as last fallback fallback_key = etree.QName(key).localname try: return definition.get(name, fallback_key, _processed) except IndexError: pass raise IndexError("No definition %r in %r found" % (key, name)) def resolve_imports(self): """Resolve all root elements (types, messages, etc).""" # Simple guard to protect against cyclic imports if self._resolved_imports: return self._resolved_imports = True for definition in self.imports.values(): definition.resolve_imports() for message in self.messages.values(): message.resolve(self) for port_type in self.port_types.values(): port_type.resolve(self) for binding in self.bindings.values(): binding.resolve(self) for service in self.services.values(): service.resolve(self) def parse_imports(self, doc): """Import other WSDL definitions in this document. Note that imports are non-transitive, so only import definitions which are defined in the imported document and ignore definitions imported in that document. This should handle recursive imports though: A -> B -> A A -> B -> C -> A :param doc: The source document :type doc: lxml.etree._Element """ for import_node in doc.findall("wsdl:import", namespaces=NSMAP): namespace = import_node.get('namespace') location = import_node.get('location') if not location: logger.debug( "Skipping import for namespace %s (empty location)", namespace) continue location = absolute_location(location, self.location) key = (namespace, location) if key in self.wsdl._definitions: self.imports[key] = self.wsdl._definitions[key] else: document = self.wsdl._get_xml_document(location) if etree.QName(document.tag).localname == 'schema': self.types.add_documents([document], location) else: wsdl = Definition(self.wsdl, document, location) self.imports[key] = wsdl def parse_types(self, doc): """Return an xsd.Schema() instance for the given wsdl:types element. If the wsdl:types contain multiple schema definitions then a new wrapping xsd.Schema is defined with xsd:import statements linking them together. If the wsdl:types doesn't container an xml schema then an empty schema is returned instead. Definition:: * :param doc: The source document :type doc: lxml.etree._Element """ namespace_sets = [ { 'xsd': 'http://www.w3.org/2001/XMLSchema', 'wsdl': 'http://schemas.xmlsoap.org/wsdl/', }, { 'xsd': 'http://www.w3.org/1999/XMLSchema', 'wsdl': 'http://schemas.xmlsoap.org/wsdl/', }, ] # Find xsd:schema elements (wsdl:types/xsd:schema) schema_nodes = findall_multiple_ns( doc, 'wsdl:types/xsd:schema', namespace_sets) self.types.add_documents(schema_nodes, self.location) def parse_messages(self, doc): """ Definition:: * * :param doc: The source document :type doc: lxml.etree._Element """ result = {} for msg_node in doc.findall("wsdl:message", namespaces=NSMAP): try: msg = parse.parse_abstract_message(self, msg_node) except IncompleteMessage as exc: warnings.warn(str(exc)) else: result[msg.name.text] = msg logger.debug("Adding message: %s", msg.name.text) return result def parse_ports(self, doc): """Return dict with `PortType` instances as values Definition:: * :param doc: The source document :type doc: lxml.etree._Element """ result = {} for port_node in doc.findall('wsdl:portType', namespaces=NSMAP): port_type = parse.parse_port_type(self, port_node) result[port_type.name.text] = port_type logger.debug("Adding port: %s", port_type.name.text) return result def parse_binding(self, doc): """Parse the binding elements and return a dict of bindings. Currently supported bindings are Soap 1.1, Soap 1.2., HTTP Get and HTTP Post. The detection of the type of bindings is done by the bindings themselves using the introspection of the xml nodes. Definition:: * <-- extensibility element (1) --> * * <-- extensibility element (2) --> * ? <-- extensibility element (3) --> ? <-- extensibility element (4) --> * * <-- extensibility element (5) --> * :param doc: The source document :type doc: lxml.etree._Element :returns: Dictionary with binding name as key and Binding instance as value :rtype: dict """ result = {} if not getattr(self.wsdl.transport, 'binding_classes', None): from zeep.wsdl import bindings binding_classes = [ bindings.Soap11Binding, bindings.Soap12Binding, bindings.HttpGetBinding, bindings.HttpPostBinding, ] else: binding_classes = self.wsdl.transport.binding_classes for binding_node in doc.findall('wsdl:binding', namespaces=NSMAP): # Detect the binding type binding = None for binding_class in binding_classes: if binding_class.match(binding_node): try: binding = binding_class.parse(self, binding_node) except NotImplementedError as exc: logger.debug("Ignoring binding: %s", exc) continue logger.debug("Adding binding: %s", binding.name.text) result[binding.name.text] = binding break return result def parse_service(self, doc): """ Definition:: * * <-- extensibility element (1) --> :param doc: The source document :type doc: lxml.etree._Element """ result = OrderedDict() for service_node in doc.findall('wsdl:service', namespaces=NSMAP): service = parse.parse_service(self, service_node) result[service.name] = service logger.debug("Adding service: %s", service.name) return result zeep-2.5.0/src/zeep/wsse/0000755000076500000240000000000013224162010017151 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/wsse/__init__.py0000644000076500000240000000017213224161177021277 0ustar mvantellingenstaff00000000000000from .compose import Compose # noqa from .signature import Signature # noqa from .username import UsernameToken # noqa zeep-2.5.0/src/zeep/wsse/compose.py0000644000076500000240000000057313224161177021212 0ustar mvantellingenstaff00000000000000class Compose(object): def __init__(self, wsse_objects): self.wsse_objects = wsse_objects def apply(self, envelope, headers): for obj in self.wsse_objects: envelope, headers = obj.apply(envelope, headers) return envelope, headers def verify(self, envelope): for obj in self.wsse_objects: obj.verify(envelope) zeep-2.5.0/src/zeep/wsse/signature.py0000644000076500000240000002444413224161177021551 0ustar mvantellingenstaff00000000000000"""Functions for WS-Security (WSSE) signature creation and verification. Heavily based on test examples in https://github.com/mehcode/python-xmlsec as well as the xmlsec documentation at https://www.aleksey.com/xmlsec/. Reading the xmldsig, xmlenc, and ws-security standards documents, though admittedly painful, will likely assist in understanding the code in this module. """ from lxml import etree from lxml.etree import QName from zeep import ns from zeep.exceptions import SignatureVerificationFailed from zeep.utils import detect_soap_env from zeep.wsse.utils import ensure_id, get_security_header try: import xmlsec except ImportError: xmlsec = None # SOAP envelope SOAP_NS = 'http://schemas.xmlsoap.org/soap/envelope/' def _read_file(f_name): with open(f_name, "rb") as f: return f.read() def _make_sign_key(key_data, cert_data, password): key = xmlsec.Key.from_memory(key_data, xmlsec.KeyFormat.PEM, password) key.load_cert_from_memory(cert_data, xmlsec.KeyFormat.PEM) return key def _make_verify_key(cert_data): key = xmlsec.Key.from_memory(cert_data, xmlsec.KeyFormat.CERT_PEM, None) return key class MemorySignature(object): """Sign given SOAP envelope with WSSE sig using given key and cert.""" def __init__(self, key_data, cert_data, password=None): check_xmlsec_import() self.key_data = key_data self.cert_data = cert_data self.password = password def apply(self, envelope, headers): key = _make_sign_key(self.key_data, self.cert_data, self.password) _sign_envelope_with_key(envelope, key) return envelope, headers def verify(self, envelope): key = _make_verify_key(self.cert_data) _verify_envelope_with_key(envelope, key) return envelope class Signature(MemorySignature): """Sign given SOAP envelope with WSSE sig using given key file and cert file.""" def __init__(self, key_file, certfile, password=None): super(Signature, self).__init__( _read_file(key_file), _read_file(certfile), password) def check_xmlsec_import(): if xmlsec is None: raise ImportError( "The xmlsec module is required for wsse.Signature()\n" + "You can install xmlsec with: pip install xmlsec\n" + "or install zeep via: pip install zeep[xmlsec]\n" ) def sign_envelope(envelope, keyfile, certfile, password=None): """Sign given SOAP envelope with WSSE sig using given key and cert. Sign the wsu:Timestamp node in the wsse:Security header and the soap:Body; both must be present. Add a ds:Signature node in the wsse:Security header containing the signature. Use EXCL-C14N transforms to normalize the signed XML (so that irrelevant whitespace or attribute ordering changes don't invalidate the signature). Use SHA1 signatures. Expects to sign an incoming document something like this (xmlns attributes omitted for readability): 2015-06-25T21:53:25.246276+00:00 2015-06-25T21:58:25.246276+00:00 ... After signing, the sample document would look something like this (note the added wsu:Id attr on the soap:Body and wsu:Timestamp nodes, and the added ds:Signature node in the header, with ds:Reference nodes with URI attribute referencing the wsu:Id of the signed nodes): nnjjqTKxwl1hT/2RUsBuszgjTbI= qAATZaSqAr9fta9ApbGrFWDuCCQ= Hz8jtQb...bOdT6ZdTQ== MIIDnzC...Ia2qKQ== ... ... 2015-06-25T22:00:29.821700+00:00 2015-06-25T22:05:29.821700+00:00 ... """ # Load the signing key and certificate. key = _make_sign_key(_read_file(keyfile), _read_file(certfile), password) return _sign_envelope_with_key(envelope, key) def _sign_envelope_with_key(envelope, key): soap_env = detect_soap_env(envelope) # Create the Signature node. signature = xmlsec.template.create( envelope, xmlsec.Transform.EXCL_C14N, xmlsec.Transform.RSA_SHA1, ) # Add a KeyInfo node with X509Data child to the Signature. XMLSec will fill # in this template with the actual certificate details when it signs. key_info = xmlsec.template.ensure_key_info(signature) x509_data = xmlsec.template.add_x509_data(key_info) xmlsec.template.x509_data_add_issuer_serial(x509_data) xmlsec.template.x509_data_add_certificate(x509_data) # Insert the Signature node in the wsse:Security header. security = get_security_header(envelope) security.insert(0, signature) security.append(etree.Element(QName(ns.WSU, 'Timestamp'))) # Perform the actual signing. ctx = xmlsec.SignatureContext() ctx.key = key _sign_node(ctx, signature, envelope.find(QName(soap_env, 'Body'))) _sign_node(ctx, signature, security.find(QName(ns.WSU, 'Timestamp'))) ctx.sign(signature) # Place the X509 data inside a WSSE SecurityTokenReference within # KeyInfo. The recipient expects this structure, but we can't rearrange # like this until after signing, because otherwise xmlsec won't populate # the X509 data (because it doesn't understand WSSE). sec_token_ref = etree.SubElement( key_info, QName(ns.WSSE, 'SecurityTokenReference')) sec_token_ref.append(x509_data) def verify_envelope(envelope, certfile): """Verify WS-Security signature on given SOAP envelope with given cert. Expects a document like that found in the sample XML in the ``sign()`` docstring. Raise SignatureVerificationFailed on failure, silent on success. """ key = _make_verify_key(_read_file(certfile)) return _verify_envelope_with_key(envelope, key) def _verify_envelope_with_key(envelope, key): soap_env = detect_soap_env(envelope) header = envelope.find(QName(soap_env, 'Header')) if header is None: raise SignatureVerificationFailed() security = header.find(QName(ns.WSSE, 'Security')) signature = security.find(QName(ns.DS, 'Signature')) ctx = xmlsec.SignatureContext() # Find each signed element and register its ID with the signing context. refs = signature.xpath( 'ds:SignedInfo/ds:Reference', namespaces={'ds': ns.DS}) for ref in refs: # Get the reference URI and cut off the initial '#' referenced_id = ref.get('URI')[1:] referenced = envelope.xpath( "//*[@wsu:Id='%s']" % referenced_id, namespaces={'wsu': ns.WSU}, )[0] ctx.register_id(referenced, 'Id', ns.WSU) ctx.key = key try: ctx.verify(signature) except xmlsec.Error: # Sadly xmlsec gives us no details about the reason for the failure, so # we have nothing to pass on except that verification failed. raise SignatureVerificationFailed() def _sign_node(ctx, signature, target): """Add sig for ``target`` in ``signature`` node, using ``ctx`` context. Doesn't actually perform the signing; ``ctx.sign(signature)`` should be called later to do that. Adds a Reference node to the signature with URI attribute pointing to the target node, and registers the target node's ID so XMLSec will be able to find the target node by ID when it signs. """ # Ensure the target node has a wsu:Id attribute and get its value. node_id = ensure_id(target) # Unlike HTML, XML doesn't have a single standardized Id. WSSE suggests the # use of the wsu:Id attribute for this purpose, but XMLSec doesn't # understand that natively. So for XMLSec to be able to find the referenced # node by id, we have to tell xmlsec about it using the register_id method. ctx.register_id(target, 'Id', ns.WSU) # Add reference to signature with URI attribute pointing to that ID. ref = xmlsec.template.add_reference( signature, xmlsec.Transform.SHA1, uri='#' + node_id) # This is an XML normalization transform which will be performed on the # target node contents before signing. This ensures that changes to # irrelevant whitespace, attribute ordering, etc won't invalidate the # signature. xmlsec.template.add_transform(ref, xmlsec.Transform.EXCL_C14N) zeep-2.5.0/src/zeep/wsse/username.py0000644000076500000240000000707013224161177021363 0ustar mvantellingenstaff00000000000000import base64 import hashlib import os from zeep import ns from zeep.wsse import utils class UsernameToken(object): """UsernameToken Profile 1.1 https://docs.oasis-open.org/wss/v1.1/wss-v1.1-spec-os-UsernameTokenProfile.pdf Example response using PasswordText:: scott password Example using PasswordDigest:: NNK weYI3nXd8LjMNVksCKFV8t3rgHh3Rw== WScqanjCEAC4mQoBE07sAQ== 2003-07-16T01:24:32Z """ username_token_profile_ns = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0' # noqa soap_message_secutity_ns = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0' # noqa def __init__(self, username, password=None, password_digest=None, use_digest=False, nonce=None, created=None): self.username = username self.password = password self.password_digest = password_digest self.nonce = nonce self.created = created self.use_digest = use_digest def apply(self, envelope, headers): security = utils.get_security_header(envelope) # The token placeholder might already exists since it is specified in # the WSDL. token = security.find('{%s}UsernameToken' % ns.WSSE) if token is None: token = utils.WSSE.UsernameToken() security.append(token) # Create the sub elements of the UsernameToken element elements = [ utils.WSSE.Username(self.username) ] if self.password is not None or self.password_digest is not None: if self.use_digest: elements.extend(self._create_password_digest()) else: elements.extend(self._create_password_text()) token.extend(elements) return envelope, headers def verify(self, envelope): pass def _create_password_text(self): return [ utils.WSSE.Password( self.password, Type='%s#PasswordText' % self.username_token_profile_ns) ] def _create_password_digest(self): if self.nonce: nonce = self.nonce.encode('utf-8') else: nonce = os.urandom(16) timestamp = utils.get_timestamp(self.created) # digest = Base64 ( SHA-1 ( nonce + created + password ) ) if not self.password_digest: digest = base64.b64encode( hashlib.sha1( nonce + timestamp.encode('utf-8') + self.password.encode('utf-8') ).digest() ).decode('ascii') else: digest = self.password_digest return [ utils.WSSE.Password( digest, Type='%s#PasswordDigest' % self.username_token_profile_ns ), utils.WSSE.Nonce( base64.b64encode(nonce).decode('utf-8'), EncodingType='%s#Base64Binary' % self.soap_message_secutity_ns ), utils.WSU.Created(timestamp) ] zeep-2.5.0/src/zeep/wsse/utils.py0000644000076500000240000000244413224161177020704 0ustar mvantellingenstaff00000000000000import datetime from uuid import uuid4 import pytz from lxml import etree from lxml.builder import ElementMaker from zeep import ns from zeep.wsdl.utils import get_or_create_header NSMAP = { 'wsse': ns.WSSE, 'wsu': ns.WSU, } WSSE = ElementMaker(namespace=NSMAP['wsse'], nsmap={'wsse': ns.WSSE}) WSU = ElementMaker(namespace=NSMAP['wsu'], nsmap={'wsu': ns.WSU}) ID_ATTR = etree.QName(NSMAP['wsu'], 'Id') def get_security_header(doc): """Return the security header. If the header doesn't exist it will be created. """ header = get_or_create_header(doc) security = header.find('wsse:Security', namespaces=NSMAP) if security is None: security = WSSE.Security() header.append(security) return security def get_timestamp(timestamp=None): timestamp = timestamp or datetime.datetime.utcnow() timestamp = timestamp.replace(tzinfo=pytz.utc, microsecond=0) return timestamp.isoformat() def get_unique_id(): return 'id-{0}'.format(uuid4()) def ensure_id(node): """Ensure given node has a wsu:Id attribute; add unique one if not. Return found/created attribute value. """ assert node is not None id_val = node.get(ID_ATTR) if not id_val: id_val = get_unique_id() node.set(ID_ATTR, id_val) return id_val zeep-2.5.0/src/zeep/xsd/0000755000076500000240000000000013224162010016766 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/xsd/__init__.py0000644000076500000240000000044713224161177021121 0ustar mvantellingenstaff00000000000000""" zeep.xsd -------- """ from zeep.xsd.const import Nil, SkipValue # noqa from zeep.xsd.elements import * # noqa from zeep.xsd.schema import Schema # noqa from zeep.xsd.types import * # noqa from zeep.xsd.types.builtins import * # noqa from zeep.xsd.valueobjects import * # noqa zeep-2.5.0/src/zeep/xsd/const.py0000644000076500000240000000077613224161177020515 0ustar mvantellingenstaff00000000000000from lxml import etree from zeep import ns def xsi_ns(localname): return etree.QName(ns.XSI, localname) def xsd_ns(localname): return etree.QName(ns.XSD, localname) class _StaticIdentity(object): def __init__(self, val): self.__value__ = val def __repr__(self): return self.__value__ NotSet = _StaticIdentity('NotSet') SkipValue = _StaticIdentity('SkipValue') Nil = _StaticIdentity('Nil') AUTO_IMPORT_NAMESPACES = [ 'http://schemas.xmlsoap.org/soap/encoding/' ] zeep-2.5.0/src/zeep/xsd/context.py0000644000076500000240000000020613224161177021037 0ustar mvantellingenstaff00000000000000class XmlParserContext(object): """Parser context when parsing XML elements""" def __init__(self): self.schemas = [] zeep-2.5.0/src/zeep/xsd/elements/0000755000076500000240000000000013224162010020602 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/xsd/elements/__init__.py0000644000076500000240000000023713224161177022732 0ustar mvantellingenstaff00000000000000from .any import * # noqa from .attribute import * # noqa from .element import * # noqa from .indicators import * # noqa from .references import * # noqa zeep-2.5.0/src/zeep/xsd/elements/any.py0000644000076500000240000002021713224161177021762 0ustar mvantellingenstaff00000000000000import logging from lxml import etree from zeep import exceptions, ns from zeep.utils import qname_attr from zeep.xsd.const import NotSet, xsi_ns from zeep.xsd.elements.base import Base from zeep.xsd.utils import max_occurs_iter from zeep.xsd.valueobjects import AnyObject logger = logging.getLogger(__name__) __all__ = ['Any', 'AnyAttribute'] class Any(Base): name = None def __init__(self, max_occurs=1, min_occurs=1, process_contents='strict', restrict=None): """ :param process_contents: Specifies how the XML processor should handle validation against the elements specified by this any element :type process_contents: str (strict, lax, skip) """ super(Any, self).__init__() self.max_occurs = max_occurs self.min_occurs = min_occurs self.restrict = restrict self.process_contents = process_contents # cyclic import from zeep.xsd import AnyType self.type = AnyType() def __call__(self, any_object): return any_object def __repr__(self): return '<%s(name=%r)>' % (self.__class__.__name__, self.name) def accept(self, value): return True def parse(self, xmlelement, schema, context=None): if self.process_contents == 'skip': return xmlelement # If a schema was passed inline then check for a matching one qname = etree.QName(xmlelement.tag) if context and context.schemas: for context_schema in context.schemas: if context_schema.documents.has_schema_document_for_ns(qname.namespace): schema = context_schema break # Lookup type via xsi:type attribute xsd_type = qname_attr(xmlelement, xsi_ns('type')) if xsd_type is not None: xsd_type = schema.get_type(xsd_type) return xsd_type.parse_xmlelement(xmlelement, schema, context=context) # Check if a restrict is used if self.restrict: return self.restrict.parse_xmlelement( xmlelement, schema, context=context) try: element = schema.get_element(xmlelement.tag) return element.parse(xmlelement, schema, context=context) except (exceptions.NamespaceError, exceptions.LookupError): return xmlelement def parse_kwargs(self, kwargs, name, available_kwargs): if name in available_kwargs: available_kwargs.remove(name) value = kwargs[name] return {name: value} return {} def parse_xmlelements(self, xmlelements, schema, name=None, context=None): """Consume matching xmlelements and call parse() on each of them :param xmlelements: Dequeue of XML element objects :type xmlelements: collections.deque of lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param name: The name of the parent element :type name: str :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :return: dict or None """ result = [] for _unused in max_occurs_iter(self.max_occurs): if xmlelements: xmlelement = xmlelements.popleft() item = self.parse(xmlelement, schema, context=context) if item is not None: result.append(item) else: break if not self.accepts_multiple: result = result[0] if result else None return result def render(self, parent, value, render_path=None): assert parent is not None self.validate(value, render_path) if self.accepts_multiple and isinstance(value, list): from zeep.xsd import AnySimpleType if isinstance(self.restrict, AnySimpleType): for val in value: node = etree.SubElement(parent, 'item') node.set(xsi_ns('type'), self.restrict.qname) self._render_value_item(node, val, render_path) elif self.restrict: for val in value: node = etree.SubElement(parent, self.restrict.name) # node.set(xsi_ns('type'), self.restrict.qname) self._render_value_item(node, val, render_path) else: for val in value: self._render_value_item(parent, val, render_path) else: self._render_value_item(parent, value, render_path) def _render_value_item(self, parent, value, render_path): if value is None: # can be an lxml element return elif isinstance(value, etree._Element): parent.append(value) elif self.restrict: if isinstance(value, list): for val in value: self.restrict.render(parent, val, None, render_path) else: self.restrict.render(parent, value, None, render_path) else: if isinstance(value.value, list): for val in value.value: value.xsd_elm.render(parent, val, render_path) else: value.xsd_elm.render(parent, value.value, render_path) def validate(self, value, render_path): if self.accepts_multiple and isinstance(value, list): # Validate bounds if len(value) < self.min_occurs: raise exceptions.ValidationError( "Expected at least %d items (minOccurs check)" % self.min_occurs) if self.max_occurs != 'unbounded' and len(value) > self.max_occurs: raise exceptions.ValidationError( "Expected at most %d items (maxOccurs check)" % self.min_occurs) for val in value: self._validate_item(val, render_path) else: if not self.is_optional and value in (None, NotSet): raise exceptions.ValidationError("Missing element for Any") self._validate_item(value, render_path) def _validate_item(self, value, render_path): if value is None: # can be an lxml element return # Check if we received a proper value object. If we receive the wrong # type then return a nice error message if self.restrict: expected_types = (etree._Element, dict,) + self.restrict.accepted_types else: expected_types = (etree._Element, dict, AnyObject) if not isinstance(value, expected_types): type_names = [ '%s.%s' % (t.__module__, t.__name__) for t in expected_types ] err_message = "Any element received object of type %r, expected %s" % ( type(value).__name__, ' or '.join(type_names)) raise TypeError('\n'.join(( err_message, "See http://docs.python-zeep.org/en/master/datastructures.html" "#any-objects for more information" ))) def resolve(self): return self def signature(self, schema=None, standalone=True): if self.restrict: base = self.restrict.name else: base = 'ANY' if self.accepts_multiple: return '%s[]' % base return base class AnyAttribute(Base): name = None _ignore_attributes = [ etree.QName(ns.XSI, 'type') ] def __init__(self, process_contents='strict'): self.qname = None self.process_contents = process_contents def parse(self, attributes, context=None): result = {} for key, value in attributes.items(): if key not in self._ignore_attributes: result[key] = value return result def resolve(self): return self def render(self, parent, value, render_path=None): if value in (None, NotSet): return for name, val in value.items(): parent.set(name, val) def signature(self, schema=None, standalone=True): return '{}' zeep-2.5.0/src/zeep/xsd/elements/attribute.py0000644000076500000240000000534613224161177023204 0ustar mvantellingenstaff00000000000000import logging from lxml import etree from zeep import exceptions from zeep.xsd.const import NotSet from zeep.xsd.elements.element import Element logger = logging.getLogger(__name__) __all__ = ['Attribute', 'AttributeGroup'] class Attribute(Element): def __init__(self, name, type_=None, required=False, default=None): super(Attribute, self).__init__(name=name, type_=type_, default=default) self.required = required self.array_type = None def parse(self, value): try: return self.type.pythonvalue(value) except (TypeError, ValueError): logger.exception("Error during xml -> python translation") return None def render(self, parent, value, render_path=None): if value in (None, NotSet) and not self.required: return self.validate(value, render_path) value = self.type.xmlvalue(value) parent.set(self.qname, value) def validate(self, value, render_path): try: self.type.validate(value, required=self.required) except exceptions.ValidationError as exc: raise exceptions.ValidationError( "The attribute %s is not valid: %s" % (self.qname, exc.message), path=render_path) def clone(self, *args, **kwargs): array_type = kwargs.pop('array_type', None) new = super(Attribute, self).clone(*args, **kwargs) new.array_type = array_type return new def resolve(self): retval = super(Attribute, self).resolve() self.type = self.type.resolve() if self.array_type: retval.array_type = self.array_type.resolve() return retval class AttributeGroup(object): def __init__(self, name, attributes): if not isinstance(name, etree.QName): name = etree.QName(name) self.name = name.localname self.qname = name self.type = None self._attributes = attributes self.is_global = True @property def attributes(self): result = [] for attr in self._attributes: if isinstance(attr, AttributeGroup): result.extend(attr.attributes) else: result.append(attr) return result def resolve(self): resolved = [] for attribute in self._attributes: value = attribute.resolve() assert value is not None if isinstance(value, list): resolved.extend(value) else: resolved.append(value) self._attributes = resolved return self def signature(self, schema=None, standalone=True): return ', '.join(attr.signature(schema) for attr in self._attributes) zeep-2.5.0/src/zeep/xsd/elements/base.py0000644000076500000240000000240513224161177022104 0ustar mvantellingenstaff00000000000000class Base(object): @property def accepts_multiple(self): return self.max_occurs != 1 @property def default_value(self): return None @property def is_optional(self): return self.min_occurs == 0 def parse_args(self, args, index=0): result = {} if not args: return result, args, index value = args[index] index += 1 return {self.attr_name: value}, args, index def parse_kwargs(self, kwargs, name, available_kwargs): raise NotImplementedError() def parse_xmlelements(self, xmlelements, schema, name=None, context=None): """Consume matching xmlelements and call parse() on each of them :param xmlelements: Dequeue of XML element objects :type xmlelements: collections.deque of lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param name: The name of the parent element :type name: str :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :return: dict or None """ raise NotImplementedError() def signature(self, schema=None, standalone=False): return '' zeep-2.5.0/src/zeep/xsd/elements/builtins.py0000644000076500000240000000205713224161177023026 0ustar mvantellingenstaff00000000000000from __future__ import division from zeep.xsd.const import xsd_ns from zeep.xsd.elements.base import Base class Schema(Base): name = 'schema' attr_name = 'schema' qname = xsd_ns('schema') def clone(self, qname, min_occurs=1, max_occurs=1): return self.__class__() def parse_kwargs(self, kwargs, name, available_kwargs): if name in available_kwargs: value = kwargs[name] available_kwargs.remove(name) return {name: value} return {} def parse(self, xmlelement, schema, context=None): from zeep.xsd.schema import Schema schema = Schema(xmlelement, schema._transport) context.schemas.append(schema) return schema def parse_xmlelements(self, xmlelements, schema, name=None, context=None): if xmlelements[0].tag == self.qname: xmlelement = xmlelements.popleft() result = self.parse(xmlelement, schema, context=context) return result def resolve(self): return self _elements = [ Schema ] zeep-2.5.0/src/zeep/xsd/elements/element.py0000644000076500000240000002310313224161177022621 0ustar mvantellingenstaff00000000000000import copy import logging from lxml import etree from zeep import exceptions from zeep.exceptions import UnexpectedElementError from zeep.utils import qname_attr from zeep.xsd.const import Nil, NotSet, xsi_ns from zeep.xsd.context import XmlParserContext from zeep.xsd.elements.base import Base from zeep.xsd.utils import create_prefixed_name, max_occurs_iter logger = logging.getLogger(__name__) __all__ = ['Element'] class Element(Base): def __init__(self, name, type_=None, min_occurs=1, max_occurs=1, nillable=False, default=None, is_global=False, attr_name=None): if name is None: raise ValueError("name cannot be None", self.__class__) if not isinstance(name, etree.QName): name = etree.QName(name) self.name = name.localname if name else None self.qname = name self.type = type_ self.min_occurs = min_occurs self.max_occurs = max_occurs self.nillable = nillable self.is_global = is_global self.default = default self.attr_name = attr_name or self.name # assert type_ def __str__(self): if self.type: if self.type.is_global: return '%s(%s)' % (self.name, self.type.qname) else: return '%s(%s)' % (self.name, self.type.signature()) return '%s()' % self.name def __call__(self, *args, **kwargs): instance = self.type(*args, **kwargs) if hasattr(instance, '_xsd_type'): instance._xsd_elm = self return instance def __repr__(self): return '<%s(name=%r, type=%r)>' % ( self.__class__.__name__, self.name, self.type) def __eq__(self, other): return ( other is not None and self.__class__ == other.__class__ and self.__dict__ == other.__dict__) def get_prefixed_name(self, schema): return create_prefixed_name(self.qname, schema) @property def default_value(self): if self.accepts_multiple: return [] if self.is_optional: return None return self.default def clone(self, name=None, min_occurs=1, max_occurs=1): new = copy.copy(self) if name: if not isinstance(name, etree.QName): name = etree.QName(name) new.name = name.localname new.qname = name new.attr_name = new.name new.min_occurs = min_occurs new.max_occurs = max_occurs return new def parse(self, xmlelement, schema, allow_none=False, context=None): """Process the given xmlelement. If it has an xsi:type attribute then use that for further processing. This should only be done for subtypes of the defined type but for now we just accept everything. This is the entrypoint for parsing an xml document. :param xmlelement: The XML element to parse :type xmlelements: lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param allow_none: Allow none :type allow_none: bool :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :return: dict or None """ context = context or XmlParserContext() instance_type = qname_attr(xmlelement, xsi_ns('type')) xsd_type = None if instance_type: xsd_type = schema.get_type(instance_type, fail_silently=True) xsd_type = xsd_type or self.type return xsd_type.parse_xmlelement( xmlelement, schema, allow_none=allow_none, context=context, schema_type=self.type) def parse_kwargs(self, kwargs, name, available_kwargs): return self.type.parse_kwargs( kwargs, name or self.attr_name, available_kwargs) def parse_xmlelements(self, xmlelements, schema, name=None, context=None): """Consume matching xmlelements and call parse() on each of them :param xmlelements: Dequeue of XML element objects :type xmlelements: collections.deque of lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param name: The name of the parent element :type name: str :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :return: dict or None """ result = [] num_matches = 0 for _unused in max_occurs_iter(self.max_occurs): if not xmlelements: break # Workaround for SOAP servers which incorrectly use unqualified # or qualified elements in the responses (#170, #176). To make the # best of it we compare the full uri's if both elements have a # namespace. If only one has a namespace then only compare the # localname. # If both elements have a namespace and they don't match then skip element_tag = etree.QName(xmlelements[0].tag) if ( element_tag.namespace and self.qname.namespace and element_tag.namespace != self.qname.namespace and schema.strict ): break # Only compare the localname if element_tag.localname == self.qname.localname: xmlelement = xmlelements.popleft() num_matches += 1 item = self.parse( xmlelement, schema, allow_none=True, context=context) result.append(item) else: # If the element passed doesn't match and the current one is # not optional then throw an error if num_matches == 0 and not self.is_optional: raise UnexpectedElementError( "Unexpected element %r, expected %r" % ( element_tag.text, self.qname.text)) break if not self.accepts_multiple: result = result[0] if result else None return result def render(self, parent, value, render_path=None): """Render the value(s) on the parent lxml.Element. This actually just calls _render_value_item for each value. """ if not render_path: render_path = [self.qname.localname] assert parent is not None self.validate(value, render_path) if self.accepts_multiple and isinstance(value, list): for val in value: self._render_value_item(parent, val, render_path) else: self._render_value_item(parent, value, render_path) def _render_value_item(self, parent, value, render_path): """Render the value on the parent lxml.Element""" if value is Nil: elm = etree.SubElement(parent, self.qname) elm.set(xsi_ns('nil'), 'true') return if value is None or value is NotSet: if self.is_optional: return elm = etree.SubElement(parent, self.qname) if self.nillable: elm.set(xsi_ns('nil'), 'true') return node = etree.SubElement(parent, self.qname) xsd_type = getattr(value, '_xsd_type', self.type) if xsd_type != self.type: return value._xsd_type.render(node, value, xsd_type, render_path) return self.type.render(node, value, None, render_path) def validate(self, value, render_path=None): """Validate that the value is valid""" if self.accepts_multiple and isinstance(value, list): # Validate bounds if len(value) < self.min_occurs: raise exceptions.ValidationError( "Expected at least %d items (minOccurs check)" % self.min_occurs, path=render_path) elif self.max_occurs != 'unbounded' and len(value) > self.max_occurs: raise exceptions.ValidationError( "Expected at most %d items (maxOccurs check)" % self.max_occurs, path=render_path) for val in value: self._validate_item(val, render_path) else: if not self.is_optional and not self.nillable and value in (None, NotSet): raise exceptions.ValidationError( "Missing element %s" % (self.name), path=render_path) self._validate_item(value, render_path) def _validate_item(self, value, render_path): if self.nillable and value in (None, NotSet): return try: self.type.validate(value, required=True) except exceptions.ValidationError as exc: raise exceptions.ValidationError( "The element %s is not valid: %s" % (self.qname, exc.message), path=render_path) def resolve_type(self): self.type = self.type.resolve() def resolve(self): self.resolve_type() return self def signature(self, schema=None, standalone=True): from zeep.xsd import ComplexType if self.type.is_global or (not standalone and self.is_global): value = self.type.get_prefixed_name(schema) else: value = self.type.signature(schema, standalone=False) if not standalone and isinstance(self.type, ComplexType): value = '{%s}' % value if standalone: value = '%s(%s)' % (self.get_prefixed_name(schema), value) if self.accepts_multiple: return '%s[]' % value return value zeep-2.5.0/src/zeep/xsd/elements/indicators.py0000644000076500000240000006110213224161177023330 0ustar mvantellingenstaff00000000000000""" zeep.xsd.elements.indicators ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Indicators are a collection of elements. There are four available, these are All, Choice, Group and Sequence. Indicator -> OrderIndicator -> All -> Choice -> Sequence -> Group """ import copy import operator from collections import OrderedDict, defaultdict, deque from cached_property import threaded_cached_property from zeep.exceptions import UnexpectedElementError, ValidationError from zeep.xsd.const import NotSet, SkipValue from zeep.xsd.elements import Any, Element from zeep.xsd.elements.base import Base from zeep.xsd.utils import ( NamePrefixGenerator, UniqueNameGenerator, create_prefixed_name, max_occurs_iter) __all__ = ['All', 'Choice', 'Group', 'Sequence'] class Indicator(Base): """Base class for the other indicators""" def __repr__(self): return '<%s(%s)>' % ( self.__class__.__name__, super(Indicator, self).__repr__()) @property def default_value(self): values = OrderedDict([ (name, element.default_value) for name, element in self.elements ]) if self.accepts_multiple: return {'_value_1': values} return values def clone(self, name, min_occurs=1, max_occurs=1): raise NotImplementedError() class OrderIndicator(Indicator, list): """Base class for All, Choice and Sequence classes.""" name = None def __init__(self, elements=None, min_occurs=1, max_occurs=1): self.min_occurs = min_occurs self.max_occurs = max_occurs super(OrderIndicator, self).__init__() if elements is not None: self.extend(elements) def clone(self, name, min_occurs=1, max_occurs=1): return self.__class__( elements=list(self), min_occurs=min_occurs, max_occurs=max_occurs) @threaded_cached_property def elements(self): """List of tuples containing the element name and the element""" result = [] for name, elm in self.elements_nested: if name is None: result.extend(elm.elements) else: result.append((name, elm)) return result @threaded_cached_property def elements_nested(self): """List of tuples containing the element name and the element""" result = [] generator = NamePrefixGenerator() generator_2 = UniqueNameGenerator() for elm in self: if isinstance(elm, (All, Choice, Group, Sequence)): if elm.accepts_multiple: result.append((generator.get_name(), elm)) else: for sub_name, sub_elm in elm.elements: sub_name = generator_2.create_name(sub_name) result.append((None, elm)) elif isinstance(elm, (Any, Choice)): result.append((generator.get_name(), elm)) else: name = generator_2.create_name(elm.attr_name) result.append((name, elm)) return result def accept(self, values): """Return the number of values which are accepted by this choice. If not all required elements are available then 0 is returned. """ if not self.accepts_multiple: values = [values] results = set() for value in values: num = 0 for name, element in self.elements_nested: if isinstance(element, Element): if element.name in value and value[element.name] is not None: num += 1 else: num += element.accept(value) results.add(num) return max(results) def parse_args(self, args, index=0): # If the sequence contains an choice element then we can't convert # the args to kwargs since Choice elements don't work with position # arguments for name, elm in self.elements_nested: if isinstance(elm, Choice): raise TypeError("Choice elements only work with keyword arguments") result = {} for name, element in self.elements: if index >= len(args): break result[name] = args[index] index += 1 return result, args, index def parse_kwargs(self, kwargs, name, available_kwargs): """Apply the given kwarg to the element. The available_kwargs is modified in-place. Returns a dict with the result. :param kwargs: The kwargs :type kwargs: dict :param name: The name as which this type is registered in the parent :type name: str :param available_kwargs: The kwargs keys which are still available, modified in place :type available_kwargs: set :rtype: dict """ if self.accepts_multiple: assert name if name: if name not in available_kwargs: return {} assert self.accepts_multiple # Make sure we have a list, lame lame item_kwargs = kwargs.get(name) if not isinstance(item_kwargs, list): item_kwargs = [item_kwargs] result = [] for item_value in max_occurs_iter(self.max_occurs, item_kwargs): try: item_kwargs = set(item_value.keys()) except AttributeError: raise TypeError( "A list of dicts is expected for unbounded Sequences") subresult = OrderedDict() for item_name, element in self.elements: value = element.parse_kwargs(item_value, item_name, item_kwargs) if value is not None: subresult.update(value) if item_kwargs: raise TypeError(( "%s() got an unexpected keyword argument %r." ) % (self, list(item_kwargs)[0])) result.append(subresult) result = {name: result} # All items consumed if not any(filter(None, item_kwargs)): available_kwargs.remove(name) return result else: assert not self.accepts_multiple result = OrderedDict() for elm_name, element in self.elements_nested: sub_result = element.parse_kwargs(kwargs, elm_name, available_kwargs) if sub_result: result.update(sub_result) return result def resolve(self): for i, elm in enumerate(self): self[i] = elm.resolve() return self def render(self, parent, value, render_path): """Create subelements in the given parent object.""" if not isinstance(value, list): values = [value] else: values = value self.validate(values, render_path) for value in max_occurs_iter(self.max_occurs, values): for name, element in self.elements_nested: if name: if name in value: element_value = value[name] child_path = render_path + [name] else: element_value = NotSet child_path = render_path else: element_value = value child_path = render_path if element_value is SkipValue: continue if element_value is not None or not element.is_optional: element.render(parent, element_value, child_path) def validate(self, value, render_path): for item in value: if item is NotSet: raise ValidationError("No value set", path=render_path) def signature(self, schema=None, standalone=True): parts = [] for name, element in self.elements_nested: if isinstance(element, Indicator): parts.append(element.signature(schema, standalone=False)) else: value = element.signature(schema, standalone=False) parts.append('%s: %s' % (name, value)) part = ', '.join(parts) if self.accepts_multiple: return '[%s]' % (part,) return part class All(OrderIndicator): """Allows the elements in the group to appear (or not appear) in any order in the containing element. """ def __init__(self, elements=None, min_occurs=1, max_occurs=1, consume_other=False): super(All, self).__init__(elements, min_occurs, max_occurs) self._consume_other = consume_other def parse_xmlelements(self, xmlelements, schema, name=None, context=None): """Consume matching xmlelements :param xmlelements: Dequeue of XML element objects :type xmlelements: collections.deque of lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param name: The name of the parent element :type name: str :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :rtype: dict or None """ result = OrderedDict() expected_tags = {element.qname for __, element in self.elements} consumed_tags = set() values = defaultdict(deque) for i, elm in enumerate(xmlelements): if elm.tag in expected_tags: consumed_tags.add(i) values[elm.tag].append(elm) # Remove the consumed tags from the xmlelements for i in sorted(consumed_tags, reverse=True): del xmlelements[i] for name, element in self.elements: sub_elements = values.get(element.qname) if sub_elements: result[name] = element.parse_xmlelements( sub_elements, schema, context=context) if self._consume_other and xmlelements: result['_raw_elements'] = list(xmlelements) xmlelements.clear() return result class Choice(OrderIndicator): """Permits one and only one of the elements contained in the group.""" def parse_args(self, args, index=0): if args: raise TypeError("Choice elements only work with keyword arguments") @property def is_optional(self): return True @property def default_value(self): return OrderedDict() def parse_xmlelements(self, xmlelements, schema, name=None, context=None): """Consume matching xmlelements :param xmlelements: Dequeue of XML element objects :type xmlelements: collections.deque of lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param name: The name of the parent element :type name: str :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :rtype: dict or None """ result = [] for _unused in max_occurs_iter(self.max_occurs): if not xmlelements: break # Choose out of multiple options = [] for element_name, element in self.elements_nested: local_xmlelements = copy.copy(xmlelements) try: sub_result = element.parse_xmlelements( xmlelements=local_xmlelements, schema=schema, name=element_name, context=context) except UnexpectedElementError: continue if isinstance(element, Element): sub_result = {element_name: sub_result} num_consumed = len(xmlelements) - len(local_xmlelements) if num_consumed: options.append((num_consumed, sub_result)) if not options: xmlelements = [] break # Sort on least left options = sorted(options, key=operator.itemgetter(0), reverse=True) if options: result.append(options[0][1]) for i in range(options[0][0]): xmlelements.popleft() else: break if self.accepts_multiple: result = {name: result} else: result = result[0] if result else {} return result def parse_kwargs(self, kwargs, name, available_kwargs): """Processes the kwargs for this choice element. Returns a dict containing the values found. This handles two distinct initialization methods: 1. Passing the choice elements directly to the kwargs (unnested) 2. Passing the choice elements into the `name` kwarg (_value_1) (nested). This case is required when multiple choice elements are given. :param name: Name of the choice element (_value_1) :type name: str :param element: Choice element object :type element: zeep.xsd.Choice :param kwargs: dict (or list of dicts) of kwargs for initialization :type kwargs: list / dict """ if name and name in available_kwargs: assert self.accepts_multiple values = kwargs[name] or [] available_kwargs.remove(name) result = [] if isinstance(values, dict): values = [values] # TODO: Use most greedy choice instead of first matching for value in values: for element in self: if isinstance(element, OrderIndicator): choice_value = value[name] if name in value else value if element.accept(choice_value): result.append(choice_value) break else: if element.name in value: choice_value = value.get(element.name) result.append({element.name: choice_value}) break else: raise TypeError( "No complete xsd:Sequence found for the xsd:Choice %r.\n" "The signature is: %s" % (name, self.signature())) if not self.accepts_multiple: result = result[0] if result else None else: # Direct use-case isn't supported when maxOccurs > 1 if self.accepts_multiple: return {} result = {} # When choice elements are specified directly in the kwargs found = False for name, choice in self.elements_nested: temp_kwargs = copy.copy(available_kwargs) subresult = choice.parse_kwargs(kwargs, name, temp_kwargs) if subresult: if not any(subresult.values()): available_kwargs.intersection_update(temp_kwargs) result.update(subresult) elif not found: available_kwargs.intersection_update(temp_kwargs) result.update(subresult) found = True if found: for choice_name, choice in self.elements: result.setdefault(choice_name, None) else: result = {} if name and self.accepts_multiple: result = {name: result} return result def render(self, parent, value, render_path): """Render the value to the parent element tree node. This is a bit more complex then the order render methods since we need to search for the best matching choice element. """ if not self.accepts_multiple: value = [value] self.validate(value, render_path) for item in value: result = self._find_element_to_render(item) if result: element, choice_value = result element.render(parent, choice_value, render_path) def validate(self, value, render_path): found = 0 for item in value: result = self._find_element_to_render(item) if result: found += 1 if not found and not self.is_optional: raise ValidationError("Missing choice values", path=render_path) def accept(self, values): """Return the number of values which are accepted by this choice. If not all required elements are available then 0 is returned. """ nums = set() for name, element in self.elements_nested: if isinstance(element, Element): if self.accepts_multiple: if all(name in item and item[name] for item in values): nums.add(1) else: if name in values and values[name]: nums.add(1) else: num = element.accept(values) nums.add(num) return max(nums) if nums else 0 def _find_element_to_render(self, value): """Return a tuple (element, value) for the best matching choice. This is used to decide which choice child is best suitable for rendering the available data. """ matches = [] for name, element in self.elements_nested: if isinstance(element, Element): if element.name in value: try: choice_value = value[element.name] except KeyError: choice_value = value if choice_value is not None: matches.append((1, element, choice_value)) else: if name is not None: try: choice_value = value[name] except KeyError: choice_value = value else: choice_value = value score = element.accept(choice_value) if score: matches.append((score, element, choice_value)) if matches: matches = sorted(matches, key=operator.itemgetter(0), reverse=True) return matches[0][1:] def signature(self, schema=None, standalone=True): parts = [] for name, element in self.elements_nested: if isinstance(element, OrderIndicator): parts.append('{%s}' % (element.signature(schema, standalone=False))) else: parts.append('{%s: %s}' % (name, element.signature(schema, standalone=False))) part = '(%s)' % ' | '.join(parts) if self.accepts_multiple: return '%s[]' % (part,) return part class Sequence(OrderIndicator): """Requires the elements in the group to appear in the specified sequence within the containing element. """ def parse_xmlelements(self, xmlelements, schema, name=None, context=None): """Consume matching xmlelements :param xmlelements: Dequeue of XML element objects :type xmlelements: collections.deque of lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param name: The name of the parent element :type name: str :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :rtype: dict or None """ result = [] if self.accepts_multiple: assert name for _unused in max_occurs_iter(self.max_occurs): if not xmlelements: break item_result = OrderedDict() for elm_name, element in self.elements: try: item_subresult = element.parse_xmlelements( xmlelements, schema, name, context=context) except UnexpectedElementError: if schema.strict: raise item_subresult = None # Unwrap if allowed if isinstance(element, OrderIndicator): item_result.update(item_subresult) else: item_result[elm_name] = item_subresult if not xmlelements: break if item_result: result.append(item_result) if not self.accepts_multiple: return result[0] if result else None return {name: result} class Group(Indicator): """Groups a set of element declarations so that they can be incorporated as a group into complex type definitions. """ def __init__(self, name, child, max_occurs=1, min_occurs=1): super(Group, self).__init__() self.child = child self.qname = name self.name = name.localname if name else None self.max_occurs = max_occurs self.min_occurs = min_occurs def __str__(self): return self.signature() def __iter__(self, *args, **kwargs): for item in self.child: yield item @threaded_cached_property def elements(self): if self.accepts_multiple: return [('_value_1', self.child)] return self.child.elements def clone(self, name, min_occurs=1, max_occurs=1): return self.__class__( name=None, child=self.child, min_occurs=min_occurs, max_occurs=max_occurs) def accept(self, values): """Return the number of values which are accepted by this choice. If not all required elements are available then 0 is returned. """ return self.child.accept(values) def parse_args(self, args, index=0): return self.child.parse_args(args, index) def parse_kwargs(self, kwargs, name, available_kwargs): if self.accepts_multiple: if name not in kwargs: return {} available_kwargs.remove(name) item_kwargs = kwargs[name] result = [] sub_name = '_value_1' if self.child.accepts_multiple else None for sub_kwargs in max_occurs_iter(self.max_occurs, item_kwargs): available_sub_kwargs = set(sub_kwargs.keys()) subresult = self.child.parse_kwargs( sub_kwargs, sub_name, available_sub_kwargs) if available_sub_kwargs: raise TypeError(( "%s() got an unexpected keyword argument %r." ) % (self, list(available_sub_kwargs)[0])) if subresult: result.append(subresult) if result: result = {name: result} else: result = self.child.parse_kwargs(kwargs, name, available_kwargs) return result def parse_xmlelements(self, xmlelements, schema, name=None, context=None): """Consume matching xmlelements :param xmlelements: Dequeue of XML element objects :type xmlelements: collections.deque of lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param name: The name of the parent element :type name: str :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :rtype: dict or None """ result = [] for _unused in max_occurs_iter(self.max_occurs): result.append( self.child.parse_xmlelements( xmlelements, schema, name, context=context) ) if not xmlelements: break if not self.accepts_multiple and result: return result[0] return {name: result} def render(self, parent, value, render_path): if not isinstance(value, list): values = [value] else: values = value for value in values: self.child.render(parent, value, render_path) def resolve(self): self.child = self.child.resolve() return self def signature(self, schema=None, standalone=True): name = create_prefixed_name(self.qname, schema) if standalone: return '%s(%s)' % ( name, self.child.signature(schema, standalone=False)) else: return self.child.signature(schema, standalone=False) zeep-2.5.0/src/zeep/xsd/elements/references.py0000644000076500000240000000274113224161177023316 0ustar mvantellingenstaff00000000000000""" zeep.xsd.elements.references ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ref* objecs are only used temporarily between parsing the schema and resolving all the elements. """ __all__ = ['RefElement', 'RefAttribute', 'RefAttributeGroup', 'RefGroup'] class RefElement(object): def __init__(self, tag, ref, schema, is_qualified=False, min_occurs=1, max_occurs=1): self._ref = ref self._is_qualified = is_qualified self._schema = schema self.min_occurs = min_occurs self.max_occurs = max_occurs def resolve(self): elm = self._schema.get_element(self._ref) elm = elm.clone( elm.qname, min_occurs=self.min_occurs, max_occurs=self.max_occurs) return elm.resolve() class RefAttribute(RefElement): def __init__(self, *args, **kwargs): self._array_type = kwargs.pop('array_type', None) super(RefAttribute, self).__init__(*args, **kwargs) def resolve(self): attrib = self._schema.get_attribute(self._ref) attrib = attrib.clone(attrib.qname, array_type=self._array_type) return attrib.resolve() class RefAttributeGroup(RefElement): def resolve(self): value = self._schema.get_attribute_group(self._ref) return value.resolve() class RefGroup(RefElement): def resolve(self): elm = self._schema.get_group(self._ref) elm = elm.clone( elm.qname, min_occurs=self.min_occurs, max_occurs=self.max_occurs) return elm zeep-2.5.0/src/zeep/xsd/printer.py0000644000076500000240000000405113224161177021040 0ustar mvantellingenstaff00000000000000from collections import OrderedDict from six import StringIO class PrettyPrinter(object): """Cleaner pprint output. Heavily inspired by the Python pprint module, but more basic for now. """ def pformat(self, obj): stream = StringIO() self._format(obj, stream) return stream.getvalue() def _format(self, obj, stream, indent=4, level=1): _repr = getattr(type(obj), '__repr__', None) write = stream.write if ( (isinstance(obj, dict) and _repr is dict.__repr__) or (isinstance(obj, OrderedDict) and _repr == OrderedDict.__repr__) ): write('{\n') num = len(obj) if num > 0: for i, (key, value) in enumerate(obj.items()): write(' ' * (indent * level)) write("'%s'" % key) write(': ') self._format(value, stream, level=level + 1) if i < num - 1: write(',') write('\n') write(' ' * (indent * (level - 1))) write('}') elif isinstance(obj, list) and _repr is list.__repr__: write('[') num = len(obj) if num > 0: write('\n') for i, value in enumerate(obj): write(' ' * (indent * level)) self._format(value, stream, level=level + 1) if i < num - 1: write(',') write('\n') write(' ' * (indent * (level - 1))) write(']') else: value = repr(obj) if '\n' in value: lines = value.split('\n') num = len(lines) for i, line in enumerate(lines): if i > 0: write(' ' * (indent * (level - 1))) write(line) if i < num - 1: write('\n') else: write(value) zeep-2.5.0/src/zeep/xsd/schema.py0000644000076500000240000004303113224161177020616 0ustar mvantellingenstaff00000000000000import logging from collections import OrderedDict from lxml import etree from zeep import exceptions, ns from zeep.loader import load_external from zeep.xsd import const from zeep.xsd.elements import builtins as xsd_builtins_elements from zeep.xsd.types import builtins as xsd_builtins_types from zeep.xsd.visitor import SchemaVisitor logger = logging.getLogger(__name__) class Schema(object): """A schema is a collection of schema documents.""" def __init__(self, node=None, transport=None, location=None, strict=True): """ :param node: :param transport: :param location: :param strict: Boolean to indicate if the parsing is strict (default) """ self.strict = strict self._transport = transport self.documents = _SchemaContainer() self._prefix_map_auto = {} self._prefix_map_custom = {} self._load_default_documents() if not isinstance(node, list): nodes = [node] if node is not None else [] else: nodes = node self.add_documents(nodes, location) def __repr__(self): main_doc = self.root_document if main_doc: return '' % ( main_doc._location, main_doc._target_namespace) return '' @property def prefix_map(self): retval = {} retval.update(self._prefix_map_custom) retval.update({ k: v for k, v in self._prefix_map_auto.items() if v not in retval.values() }) return retval @property def root_document(self): return next( (doc for doc in self.documents if not doc._is_internal), None) @property def is_empty(self): """Boolean to indicate if this schema contains any types or elements""" return all(document.is_empty for document in self.documents) @property def namespaces(self): return self.documents.get_all_namespaces() @property def elements(self): """Yield all globla xsd.Type objects :rtype: Iterable of zeep.xsd.Element """ seen = set() for document in self.documents: for element in document._elements.values(): if element.qname not in seen: yield element seen.add(element.qname) @property def types(self): """Yield all global xsd.Type objects :rtype: Iterable of zeep.xsd.ComplexType """ seen = set() for document in self.documents: for type_ in document._types.values(): if type_.qname not in seen: yield type_ seen.add(type_.qname) def add_documents(self, schema_nodes, location): resolve_queue = [] for node in schema_nodes: document = self.create_new_document(node, location) resolve_queue.append(document) for document in resolve_queue: document.resolve() self._prefix_map_auto = self._create_prefix_map() def add_document_by_url(self, url): schema_node = load_external( url, self._transport, strict=self.strict) document = self.create_new_document(schema_node, url=url) document.resolve() def get_element(self, qname): """Return a global xsd.Element object with the given qname :rtype: zeep.xsd.Group """ qname = self._create_qname(qname) return self._get_instance(qname, 'get_element', 'element') def get_type(self, qname, fail_silently=False): """Return a global xsd.Type object with the given qname :rtype: zeep.xsd.ComplexType or zeep.xsd.AnySimpleType """ qname = self._create_qname(qname) try: return self._get_instance(qname, 'get_type', 'type') except exceptions.NamespaceError as exc: if fail_silently: logger.debug(str(exc)) else: raise def get_group(self, qname): """Return a global xsd.Group object with the given qname. :rtype: zeep.xsd.Group """ return self._get_instance(qname, 'get_group', 'group') def get_attribute(self, qname): """Return a global xsd.attributeGroup object with the given qname :rtype: zeep.xsd.Attribute """ return self._get_instance(qname, 'get_attribute', 'attribute') def get_attribute_group(self, qname): """Return a global xsd.attributeGroup object with the given qname :rtype: zeep.xsd.AttributeGroup """ return self._get_instance(qname, 'get_attribute_group', 'attributeGroup') def set_ns_prefix(self, prefix, namespace): self._prefix_map_custom[prefix] = namespace def get_ns_prefix(self, prefix): try: try: return self._prefix_map_custom[prefix] except KeyError: return self._prefix_map_auto[prefix] except KeyError: raise ValueError("No such prefix %r" % prefix) def get_shorthand_for_ns(self, namespace): for prefix, other_namespace in self._prefix_map_auto.items(): if namespace == other_namespace: return prefix for prefix, other_namespace in self._prefix_map_custom.items(): if namespace == other_namespace: return prefix if namespace == 'http://schemas.xmlsoap.org/soap/envelope/': return 'soap-env' return namespace def create_new_document(self, node, url, base_url=None): """ :rtype: zeep.xsd.schema.SchemaDocument """ namespace = node.get('targetNamespace') if node is not None else None if base_url is None: base_url = url schema = SchemaDocument(namespace, url, base_url) self.documents.add(schema) schema.load(self, node) return schema def merge(self, schema): """Merge an other XSD schema in this one""" for document in schema.documents: self.documents.add(document) self._prefix_map_auto = self._create_prefix_map() def _load_default_documents(self): schema = SchemaDocument(ns.XSD, None, None) for cls in xsd_builtins_types._types: instance = cls(is_global=True) schema.register_type(cls._default_qname, instance) for cls in xsd_builtins_elements._elements: instance = cls() schema.register_element(cls.qname, instance) schema._is_internal = True self.documents.add(schema) return schema def _get_instance(self, qname, method_name, name): """Return an object from one of the SchemaDocument's""" qname = self._create_qname(qname) try: last_exception = None for schema in self._get_schema_documents(qname.namespace): method = getattr(schema, method_name) try: return method(qname) except exceptions.LookupError as exc: last_exception = exc continue raise last_exception except exceptions.NamespaceError: raise exceptions.NamespaceError(( "Unable to resolve %s %s. " + "No schema available for the namespace %r." ) % (name, qname.text, qname.namespace)) def _create_qname(self, name): """Create an `lxml.etree.QName()` object for the given qname string. This also expands the shorthand notation. :rtype: lxml.etree.QNaame """ if isinstance(name, etree.QName): return name if not name.startswith('{') and ':' in name and self._prefix_map_auto: prefix, localname = name.split(':', 1) if prefix in self._prefix_map_custom: return etree.QName(self._prefix_map_custom[prefix], localname) elif prefix in self._prefix_map_auto: return etree.QName(self._prefix_map_auto[prefix], localname) else: raise ValueError( "No namespace defined for the prefix %r" % prefix) else: return etree.QName(name) def _create_prefix_map(self): prefix_map = { 'xsd': 'http://www.w3.org/2001/XMLSchema', } i = 0 for namespace in self.documents.get_all_namespaces(): if namespace is None or namespace in prefix_map.values(): continue prefix_map['ns%d' % i] = namespace i += 1 return prefix_map def _get_schema_documents(self, namespace, fail_silently=False): """Return a list of SchemaDocument's for the given namespace. :rtype: list of SchemaDocument """ if ( not self.documents.has_schema_document_for_ns(namespace) and namespace in const.AUTO_IMPORT_NAMESPACES ): logger.debug("Auto importing missing known schema: %s", namespace) self.add_document_by_url(namespace) return self.documents.get_by_namespace(namespace, fail_silently) class _SchemaContainer(object): """Container instances to store multiple SchemaDocument objects per namespace. """ def __init__(self): self._instances = OrderedDict() def __iter__(self): for document in self.values(): yield document def add(self, document): """Append a schema document :param document: zeep.xsd.schema.SchemaDocument """ logger.debug("Add document with tns %s to schema %s", document.namespace, id(self)) documents = self._instances.setdefault(document.namespace, []) documents.append(document) def get_all_namespaces(self): return self._instances.keys() def get_by_namespace(self, namespace, fail_silently): if namespace not in self._instances: if fail_silently: return [] raise exceptions.NamespaceError( "No schema available for the namespace %r" % namespace) return self._instances[namespace] def get_by_namespace_and_location(self, namespace, location): """Return list of SchemaDocument's for the given namespace AND location. :rtype: zeep.xsd.schema.SchemaDocument """ documents = self.get_by_namespace(namespace, fail_silently=True) for document in documents: if document._location == location: return document def has_schema_document_for_ns(self, namespace): """Return a boolean if there is a SchemaDocument for the namespace. :rtype: boolean """ return namespace in self._instances def values(self): for documents in self._instances.values(): for document in documents: yield document class SchemaDocument(object): """A Schema Document consists of a set of schema components for a specific target namespace. """ def __init__(self, namespace, location, base_url): logger.debug("Init schema document for %r", location) # Internal self._base_url = base_url or location self._location = location self._target_namespace = namespace self._is_internal = False # Containers for specific types self._attribute_groups = {} self._attributes = {} self._elements = {} self._groups = {} self._types = {} self._imports = OrderedDict() self._element_form = 'unqualified' self._attribute_form = 'unqualified' self._resolved = False # self._xml_schema = None def __repr__(self): return '' % ( self._location, self._target_namespace, self.is_empty) @property def namespace(self): return self._target_namespace @property def is_empty(self): return not bool(self._imports or self._types or self._elements) def load(self, schema, node): """Load the XML Schema passed in via the node attribute. :type schema: zeep.xsd.schema.Schema :type node: etree._Element """ if node is None: return if not schema.documents.has_schema_document_for_ns(self._target_namespace): raise RuntimeError( "The document needs to be registered in the schema before " + "it can be loaded") # Disable XML schema validation for now # if len(node) > 0: # self.xml_schema = etree.XMLSchema(node) visitor = SchemaVisitor(schema, self) visitor.visit_schema(node) def resolve(self): logger.debug("Resolving in schema %s", self) if self._resolved: return self._resolved = True for schemas in self._imports.values(): for schema in schemas: schema.resolve() def _resolve_dict(val): try: for key, obj in val.items(): new = obj.resolve() assert new is not None, "resolve() should return an object" val[key] = new except exceptions.LookupError as exc: raise exceptions.LookupError( ( "Unable to resolve %(item_name)s %(qname)s in " "%(file)s. (via %(parent)s)" ) % { 'item_name': exc.item_name, 'qname': exc.qname, 'file': exc.location, 'parent': obj.qname, }) _resolve_dict(self._attribute_groups) _resolve_dict(self._attributes) _resolve_dict(self._elements) _resolve_dict(self._groups) _resolve_dict(self._types) def register_import(self, namespace, schema): """Register an import for an other schema document. :type namespace: str :type schema: zeep.xsd.schema.SchemaDocument """ schemas = self._imports.setdefault(namespace, []) schemas.append(schema) def is_imported(self, namespace): return namespace in self._imports def register_type(self, qname, value): """Register a xsd.Type in this schema :type qname: str or etree.QName :type value: zeep.xsd.Type """ self._add_component(qname, value, self._types, 'type') def register_element(self, qname, value): """Register a xsd.Element in this schema :type qname: str or etree.QName :type value: zeep.xsd.Element """ self._add_component(qname, value, self._elements, 'element') def register_group(self, qname, value): """Register a xsd.Element in this schema :type qname: str or etree.QName :type value: zeep.xsd.Element """ self._add_component(qname, value, self._groups, 'group') def register_attribute(self, qname, value): """Register a xsd.Element in this schema :type qname: str or etree.QName :type value: zeep.xsd.Attribute """ self._add_component(qname, value, self._attributes, 'attribute') def register_attribute_group(self, qname, value): """Register a xsd.Element in this schema :type qname: str or etree.QName :type value: zeep.xsd.Group """ self._add_component(qname, value, self._attribute_groups, 'attribute_group') def get_type(self, qname): """Return a xsd.Type object from this schema :rtype: zeep.xsd.ComplexType or zeep.xsd.AnySimpleType """ return self._get_component(qname, self._types, 'type') def get_element(self, qname): """Return a xsd.Element object from this schema :rtype: zeep.xsd.Element """ return self._get_component(qname, self._elements, 'element') def get_group(self, qname): """Return a xsd.Group object from this schema. :rtype: zeep.xsd.Group """ return self._get_component(qname, self._groups, 'group') def get_attribute(self, qname): """Return a xsd.Attribute object from this schema :rtype: zeep.xsd.Attribute """ return self._get_component(qname, self._attributes, 'attribute') def get_attribute_group(self, qname): """Return a xsd.AttributeGroup object from this schema :rtype: zeep.xsd.AttributeGroup """ return self._get_component(qname, self._attribute_groups, 'attributeGroup') def _add_component(self, name, value, items, item_name): if isinstance(name, etree.QName): name = name.text logger.debug("register_%s(%r, %r)", item_name, name, value) items[name] = value def _get_component(self, qname, items, item_name): try: return items[qname] except KeyError: known_items = ', '.join(items.keys()) raise exceptions.LookupError(( "No %(item_name)s '%(localname)s' in namespace %(namespace)s. " + "Available %(item_name_plural)s are: %(known_items)s" ) % { 'item_name': item_name, 'item_name_plural': item_name + 's', 'localname': qname.localname, 'namespace': qname.namespace, 'known_items': known_items or ' - ' }, qname=qname, item_name=item_name, location=self._location) zeep-2.5.0/src/zeep/xsd/types/0000755000076500000240000000000013224162010020132 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep/xsd/types/__init__.py0000644000076500000240000000022613224161177022260 0ustar mvantellingenstaff00000000000000from .any import * # noqa from .base import * # noqa from .collection import * # noqa from .complex import * # noqa from .simple import * # noqa zeep-2.5.0/src/zeep/xsd/types/any.py0000644000076500000240000000762213224161177021317 0ustar mvantellingenstaff00000000000000import logging from zeep.utils import qname_attr from zeep.xsd.const import xsd_ns, xsi_ns from zeep.xsd.types.base import Type from zeep.xsd.valueobjects import AnyObject logger = logging.getLogger(__name__) __all__ = ['AnyType'] class AnyType(Type): _default_qname = xsd_ns('anyType') _attributes_unwrapped = [] _element = None def __call__(self, value=None): return value or '' def render(self, parent, value, xsd_type=None, render_path=None): if isinstance(value, AnyObject): if value.xsd_type is None: parent.set(xsi_ns('nil'), 'true') else: value.xsd_type.render(parent, value.value, None, render_path) parent.set(xsi_ns('type'), value.xsd_type.qname) elif hasattr(value, '_xsd_elm'): value._xsd_elm.render(parent, value, render_path) parent.set(xsi_ns('type'), value._xsd_elm.qname) else: parent.text = self.xmlvalue(value) def parse_xmlelement(self, xmlelement, schema=None, allow_none=True, context=None, schema_type=None): """Consume matching xmlelements and call parse() on each :param xmlelement: XML element objects :type xmlelement: lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param allow_none: Allow none :type allow_none: bool :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :param schema_type: The original type (not overriden via xsi:type) :type schema_type: zeep.xsd.types.base.Type :rtype: dict or None """ xsi_type = qname_attr(xmlelement, xsi_ns('type')) xsi_nil = xmlelement.get(xsi_ns('nil')) children = list(xmlelement.getchildren()) # Handle xsi:nil attribute if xsi_nil == 'true': return None # Check if a xsi:type is defined and try to parse the xml according # to that type. if xsi_type and schema: xsd_type = schema.get_type(xsi_type, fail_silently=True) # If we were unable to resolve a type for the xsi:type (due to # buggy soap servers) then we just return the text or lxml element. if not xsd_type: logger.debug( "Unable to resolve type for %r, returning raw data", xsi_type.text) if xmlelement.text: return xmlelement.text return children # If the xsd_type is xsd:anyType then we will recurs so ignore # that. if isinstance(xsd_type, self.__class__): return xmlelement.text or None return xsd_type.parse_xmlelement( xmlelement, schema, context=context) # If no xsi:type is set and the element has children then there is # not much we can do. Just return the children elif children: return children elif xmlelement.text is not None: return self.pythonvalue(xmlelement.text) return None def resolve(self): return self def xmlvalue(self, value): """Guess the xsd:type for the value and use corresponding serializer""" from zeep.xsd.types import builtins available_types = [ builtins.String, builtins.Boolean, builtins.Decimal, builtins.Float, builtins.DateTime, builtins.Date, builtins.Time, ] for xsd_type in available_types: if isinstance(value, xsd_type.accepted_types): return xsd_type().xmlvalue(value) return str(value) def pythonvalue(self, value, schema=None): return value def signature(self, schema=None, standalone=True): return 'xsd:anyType' zeep-2.5.0/src/zeep/xsd/types/base.py0000644000076500000240000000360713224161177021441 0ustar mvantellingenstaff00000000000000from zeep.xsd.utils import create_prefixed_name __all__ = ['Type'] class Type(object): def __init__(self, qname=None, is_global=False): self.qname = qname self.name = qname.localname if qname else None self._resolved = False self.is_global = is_global def get_prefixed_name(self, schema): return create_prefixed_name(self.qname, schema) def accept(self, value): raise NotImplementedError @property def accepted_types(self): return tuple() def validate(self, value, required=False): return def parse_kwargs(self, kwargs, name, available_kwargs): value = None name = name or self.name if name in available_kwargs: value = kwargs[name] available_kwargs.remove(name) return {name: value} return {} def parse_xmlelement(self, xmlelement, schema=None, allow_none=True, context=None, schema_type=None): raise NotImplementedError( '%s.parse_xmlelement() is not implemented' % self.__class__.__name__) def parsexml(self, xml, schema=None): raise NotImplementedError def render(self, parent, value, xsd_type=None, render_path=None): raise NotImplementedError( '%s.render() is not implemented' % self.__class__.__name__) def resolve(self): raise NotImplementedError( '%s.resolve() is not implemented' % self.__class__.__name__) def extend(self, child): raise NotImplementedError( '%s.extend() is not implemented' % self.__class__.__name__) def restrict(self, child): raise NotImplementedError( '%s.restrict() is not implemented' % self.__class__.__name__) @property def attributes(self): return [] @classmethod def signature(cls, schema=None, standalone=True): return '' zeep-2.5.0/src/zeep/xsd/types/builtins.py0000644000076500000240000003224613224161177022361 0ustar mvantellingenstaff00000000000000import base64 import datetime import math import re from decimal import Decimal as _Decimal import isodate import pytz import six from zeep.xsd.const import xsd_ns from zeep.xsd.types.any import AnyType from zeep.xsd.types.simple import AnySimpleType class ParseError(ValueError): pass class BuiltinType(object): def __init__(self, qname=None, is_global=False): super(BuiltinType, self).__init__(qname, is_global=True) def check_no_collection(func): def _wrapper(self, value): if isinstance(value, (list, dict, set)): raise ValueError( "The %s type doesn't accept collections as value" % ( self.__class__.__name__)) return func(self, value) return _wrapper ## # Primitive types class String(BuiltinType, AnySimpleType): _default_qname = xsd_ns('string') accepted_types = six.string_types @check_no_collection def xmlvalue(self, value): if isinstance(value, bytes): return value.decode('utf-8') return six.text_type(value if value is not None else '') def pythonvalue(self, value): return value class Boolean(BuiltinType, AnySimpleType): _default_qname = xsd_ns('boolean') accepted_types = (bool,) @check_no_collection def xmlvalue(self, value): return 'true' if value and value not in ('false', '0') else 'false' def pythonvalue(self, value): """Return True if the 'true' or '1'. 'false' and '0' are legal false values, but we consider everything not true as false. """ return value in ('true', '1') class Decimal(BuiltinType, AnySimpleType): _default_qname = xsd_ns('decimal') accepted_types = (_Decimal, float) + six.string_types @check_no_collection def xmlvalue(self, value): return str(value) def pythonvalue(self, value): return _Decimal(value) class Float(BuiltinType, AnySimpleType): _default_qname = xsd_ns('float') accepted_types = (float, _Decimal) + six.string_types def xmlvalue(self, value): return str(value).upper() def pythonvalue(self, value): return float(value) class Double(BuiltinType, AnySimpleType): _default_qname = xsd_ns('double') accepted_types = (_Decimal, float) + six.string_types @check_no_collection def xmlvalue(self, value): return str(value) def pythonvalue(self, value): return float(value) class Duration(BuiltinType, AnySimpleType): _default_qname = xsd_ns('duration') accepted_types = (isodate.duration.Duration,) + six.string_types @check_no_collection def xmlvalue(self, value): return isodate.duration_isoformat(value) def pythonvalue(self, value): if value.startswith('PT-'): value = value.replace('PT-', 'PT') result = isodate.parse_duration(value) return datetime.timedelta(0 - result.total_seconds()) else: return isodate.parse_duration(value) class DateTime(BuiltinType, AnySimpleType): _default_qname = xsd_ns('dateTime') accepted_types = (datetime.datetime,) + six.string_types @check_no_collection def xmlvalue(self, value): # Bit of a hack, since datetime is a subclass of date we can't just # test it with an isinstance(). And actually, we should not really # care about the type, as long as it has the required attributes if not all(hasattr(value, attr) for attr in ('hour', 'minute', 'second')): value = datetime.datetime.combine(value, datetime.time( getattr(value, 'hour', 0), getattr(value, 'minute', 0), getattr(value, 'second', 0))) if getattr(value, 'microsecond', 0): return isodate.isostrf.strftime(value, '%Y-%m-%dT%H:%M:%S.%f%Z') return isodate.isostrf.strftime(value, '%Y-%m-%dT%H:%M:%S%Z') def pythonvalue(self, value): return isodate.parse_datetime(value) class Time(BuiltinType, AnySimpleType): _default_qname = xsd_ns('time') accepted_types = (datetime.time,) + six.string_types @check_no_collection def xmlvalue(self, value): if isinstance(value, six.string_types): return value if value.microsecond: return isodate.isostrf.strftime(value, '%H:%M:%S.%f%Z') return isodate.isostrf.strftime(value, '%H:%M:%S%Z') def pythonvalue(self, value): return isodate.parse_time(value) class Date(BuiltinType, AnySimpleType): _default_qname = xsd_ns('date') accepted_types = (datetime.date,) + six.string_types @check_no_collection def xmlvalue(self, value): if isinstance(value, six.string_types): return value return isodate.isostrf.strftime(value, '%Y-%m-%d') def pythonvalue(self, value): return isodate.parse_date(value) class gYearMonth(BuiltinType, AnySimpleType): """gYearMonth represents a specific gregorian month in a specific gregorian year. Lexical representation: CCYY-MM """ accepted_types = (datetime.date,) + six.string_types _default_qname = xsd_ns('gYearMonth') _pattern = re.compile( r'^(?P-?\d{4,})-(?P\d\d)(?PZ|[-+]\d\d:?\d\d)?$') @check_no_collection def xmlvalue(self, value): year, month, tzinfo = value return '%04d-%02d%s' % (year, month, _unparse_timezone(tzinfo)) def pythonvalue(self, value): match = self._pattern.match(value) if not match: raise ParseError() group = match.groupdict() return ( int(group['year']), int(group['month']), _parse_timezone(group['timezone'])) class gYear(BuiltinType, AnySimpleType): """gYear represents a gregorian calendar year. Lexical representation: CCYY """ accepted_types = (datetime.date,) + six.string_types _default_qname = xsd_ns('gYear') _pattern = re.compile(r'^(?P-?\d{4,})(?PZ|[-+]\d\d:?\d\d)?$') @check_no_collection def xmlvalue(self, value): year, tzinfo = value return '%04d%s' % (year, _unparse_timezone(tzinfo)) def pythonvalue(self, value): match = self._pattern.match(value) if not match: raise ParseError() group = match.groupdict() return (int(group['year']), _parse_timezone(group['timezone'])) class gMonthDay(BuiltinType, AnySimpleType): """gMonthDay is a gregorian date that recurs, specifically a day of the year such as the third of May. Lexical representation: --MM-DD """ accepted_types = (datetime.date, ) + six.string_types _default_qname = xsd_ns('gMonthDay') _pattern = re.compile( r'^--(?P\d\d)-(?P\d\d)(?PZ|[-+]\d\d:?\d\d)?$') @check_no_collection def xmlvalue(self, value): month, day, tzinfo = value return '--%02d-%02d%s' % (month, day, _unparse_timezone(tzinfo)) def pythonvalue(self, value): match = self._pattern.match(value) if not match: raise ParseError() group = match.groupdict() return ( int(group['month']), int(group['day']), _parse_timezone(group['timezone'])) class gDay(BuiltinType, AnySimpleType): """gDay is a gregorian day that recurs, specifically a day of the month such as the 5th of the month Lexical representation: ---DD """ accepted_types = (datetime.date,) + six.string_types _default_qname = xsd_ns('gDay') _pattern = re.compile(r'^---(?P\d\d)(?PZ|[-+]\d\d:?\d\d)?$') @check_no_collection def xmlvalue(self, value): day, tzinfo = value return '---%02d%s' % (day, _unparse_timezone(tzinfo)) def pythonvalue(self, value): match = self._pattern.match(value) if not match: raise ParseError() group = match.groupdict() return (int(group['day']), _parse_timezone(group['timezone'])) class gMonth(BuiltinType, AnySimpleType): """gMonth is a gregorian month that recurs every year. Lexical representation: --MM """ accepted_types = (datetime.date,) + six.string_types _default_qname = xsd_ns('gMonth') _pattern = re.compile(r'^--(?P\d\d)(?PZ|[-+]\d\d:?\d\d)?$') @check_no_collection def xmlvalue(self, value): month, tzinfo = value return '--%d%s' % (month, _unparse_timezone(tzinfo)) def pythonvalue(self, value): match = self._pattern.match(value) if not match: raise ParseError() group = match.groupdict() return (int(group['month']), _parse_timezone(group['timezone'])) class HexBinary(BuiltinType, AnySimpleType): accepted_types = six.string_types _default_qname = xsd_ns('hexBinary') @check_no_collection def xmlvalue(self, value): return value def pythonvalue(self, value): return value class Base64Binary(BuiltinType, AnySimpleType): accepted_types = six.string_types _default_qname = xsd_ns('base64Binary') @check_no_collection def xmlvalue(self, value): return base64.b64encode(value) def pythonvalue(self, value): return base64.b64decode(value) class AnyURI(BuiltinType, AnySimpleType): accepted_types = six.string_types _default_qname = xsd_ns('anyURI') @check_no_collection def xmlvalue(self, value): return value def pythonvalue(self, value): return value class QName(BuiltinType, AnySimpleType): accepted_types = six.string_types _default_qname = xsd_ns('QName') @check_no_collection def xmlvalue(self, value): return value def pythonvalue(self, value): return value class Notation(BuiltinType, AnySimpleType): accepted_types = six.string_types _default_qname = xsd_ns('NOTATION') ## # Derived datatypes class NormalizedString(String): _default_qname = xsd_ns('normalizedString') class Token(NormalizedString): _default_qname = xsd_ns('token') class Language(Token): _default_qname = xsd_ns('language') class NmToken(Token): _default_qname = xsd_ns('NMTOKEN') class NmTokens(NmToken): _default_qname = xsd_ns('NMTOKENS') class Name(Token): _default_qname = xsd_ns('Name') class NCName(Name): _default_qname = xsd_ns('NCName') class ID(NCName): _default_qname = xsd_ns('ID') class IDREF(NCName): _default_qname = xsd_ns('IDREF') class IDREFS(IDREF): _default_qname = xsd_ns('IDREFS') class Entity(NCName): _default_qname = xsd_ns('ENTITY') class Entities(Entity): _default_qname = xsd_ns('ENTITIES') class Integer(Decimal): _default_qname = xsd_ns('integer') accepted_types = (int, float) + six.string_types def xmlvalue(self, value): return str(value) def pythonvalue(self, value): return int(value) class NonPositiveInteger(Integer): _default_qname = xsd_ns('nonPositiveInteger') class NegativeInteger(Integer): _default_qname = xsd_ns('negativeInteger') class Long(Integer): _default_qname = xsd_ns('long') def pythonvalue(self, value): return long(value) if six.PY2 else int(value) # noqa class Int(Long): _default_qname = xsd_ns('int') class Short(Int): _default_qname = xsd_ns('short') class Byte(Short): """A signed 8-bit integer""" _default_qname = xsd_ns('byte') class NonNegativeInteger(Integer): _default_qname = xsd_ns('nonNegativeInteger') class UnsignedLong(NonNegativeInteger): _default_qname = xsd_ns('unsignedLong') class UnsignedInt(UnsignedLong): _default_qname = xsd_ns('unsignedInt') class UnsignedShort(UnsignedInt): _default_qname = xsd_ns('unsignedShort') class UnsignedByte(UnsignedShort): _default_qname = xsd_ns('unsignedByte') class PositiveInteger(NonNegativeInteger): _default_qname = xsd_ns('positiveInteger') ## # Other def _parse_timezone(val): """Return a pytz.tzinfo object""" if not val: return if val == 'Z' or val == '+00:00': return pytz.utc negative = val.startswith('-') minutes = int(val[-2:]) minutes += int(val[1:3]) * 60 if negative: minutes = 0 - minutes return pytz.FixedOffset(minutes) def _unparse_timezone(tzinfo): if not tzinfo: return '' if tzinfo == pytz.utc: return 'Z' hours = math.floor(tzinfo._minutes / 60) minutes = tzinfo._minutes % 60 if hours > 0: return '+%02d:%02d' % (hours, minutes) return '-%02d:%02d' % (abs(hours), minutes) _types = [ # Primitive String, Boolean, Decimal, Float, Double, Duration, DateTime, Time, Date, gYearMonth, gYear, gMonthDay, gDay, gMonth, HexBinary, Base64Binary, AnyURI, QName, Notation, # Derived NormalizedString, Token, Language, NmToken, NmTokens, Name, NCName, ID, IDREF, IDREFS, Entity, Entities, Integer, NonPositiveInteger, # noqa NegativeInteger, Long, Int, Short, Byte, NonNegativeInteger, # noqa UnsignedByte, UnsignedInt, UnsignedLong, UnsignedShort, PositiveInteger, # Other AnyType, AnySimpleType, ] default_types = { cls._default_qname: cls(is_global=True) for cls in _types } zeep-2.5.0/src/zeep/xsd/types/collection.py0000644000076500000240000000432713224161177022662 0ustar mvantellingenstaff00000000000000from zeep.utils import get_base_class from zeep.xsd.types.simple import AnySimpleType __all__ = ['ListType', 'UnionType'] class ListType(AnySimpleType): """Space separated list of simpleType values""" def __init__(self, item_type): self.item_type = item_type super(ListType, self).__init__() def __call__(self, value): return value def render(self, parent, value, xsd_type=None, render_path=None): parent.text = self.xmlvalue(value) def resolve(self): self.item_type = self.item_type.resolve() self.base_class = self.item_type.__class__ return self def xmlvalue(self, value): item_type = self.item_type return ' '.join(item_type.xmlvalue(v) for v in value) def pythonvalue(self, value): if not value: return [] item_type = self.item_type return [item_type.pythonvalue(v) for v in value.split()] def signature(self, schema=None, standalone=True): return self.item_type.signature(schema) + '[]' class UnionType(AnySimpleType): """Simple type existing out of multiple other types""" def __init__(self, item_types): self.item_types = item_types self.item_class = None assert item_types super(UnionType, self).__init__(None) def resolve(self): self.item_types = [item.resolve() for item in self.item_types] base_class = get_base_class(self.item_types) if issubclass(base_class, AnySimpleType) and base_class != AnySimpleType: self.item_class = base_class return self def signature(self, schema=None, standalone=True): return '' def parse_xmlelement(self, xmlelement, schema=None, allow_none=True, context=None, schema_type=None): if self.item_class: return self.item_class().parse_xmlelement( xmlelement, schema, allow_none, context) return xmlelement.text def pythonvalue(self, value): if self.item_class: return self.item_class().pythonvalue(value) return value def xmlvalue(self, value): if self.item_class: return self.item_class().xmlvalue(value) return value zeep-2.5.0/src/zeep/xsd/types/complex.py0000644000076500000240000003772513224161177022206 0ustar mvantellingenstaff00000000000000import copy import logging from collections import OrderedDict, deque from itertools import chain from cached_property import threaded_cached_property from zeep.exceptions import UnexpectedElementError, XMLParseError from zeep.xsd.const import NotSet, SkipValue, xsi_ns from zeep.xsd.elements import ( Any, AnyAttribute, AttributeGroup, Choice, Element, Group, Sequence) from zeep.xsd.elements.indicators import OrderIndicator from zeep.xsd.types.any import AnyType from zeep.xsd.types.simple import AnySimpleType from zeep.xsd.utils import NamePrefixGenerator from zeep.xsd.valueobjects import ArrayValue, CompoundValue logger = logging.getLogger(__name__) __all__ = ['ComplexType'] class ComplexType(AnyType): _xsd_name = None def __init__(self, element=None, attributes=None, restriction=None, extension=None, qname=None, is_global=False): if element and type(element) == list: element = Sequence(element) self.name = self.__class__.__name__ if qname else None self._element = element self._attributes = attributes or [] self._restriction = restriction self._extension = extension self._extension_types = tuple() super(ComplexType, self).__init__(qname=qname, is_global=is_global) def __call__(self, *args, **kwargs): if self._array_type: return self._array_class(*args, **kwargs) return self._value_class(*args, **kwargs) @property def accepted_types(self): return (self._value_class,) + self._extension_types @threaded_cached_property def _array_class(self): assert self._array_type return type( self.__class__.__name__, (ArrayValue,), {'_xsd_type': self, '__module__': 'zeep.objects'}) @threaded_cached_property def _value_class(self): return type( self.__class__.__name__, (CompoundValue,), {'_xsd_type': self, '__module__': 'zeep.objects'}) def __str__(self): return '%s(%s)' % (self.__class__.__name__, self.signature()) @threaded_cached_property def attributes(self): generator = NamePrefixGenerator(prefix='_attr_') result = [] elm_names = {name for name, elm in self.elements if name is not None} for attr in self._attributes_unwrapped: if attr.name is None: name = generator.get_name() elif attr.name in elm_names: name = 'attr__%s' % attr.name else: name = attr.name result.append((name, attr)) return result @threaded_cached_property def _attributes_unwrapped(self): attributes = [] for attr in self._attributes: if isinstance(attr, AttributeGroup): attributes.extend(attr.attributes) else: attributes.append(attr) return attributes @threaded_cached_property def elements(self): """List of tuples containing the element name and the element""" result = [] for name, element in self.elements_nested: if isinstance(element, Element): result.append((element.attr_name, element)) else: result.extend(element.elements) return result @threaded_cached_property def elements_nested(self): """List of tuples containing the element name and the element""" result = [] generator = NamePrefixGenerator() # Handle wsdl:arrayType objects if self._array_type: name = generator.get_name() if isinstance(self._element, Group): result = [(name, Sequence([ Any(max_occurs='unbounded', restrict=self._array_type.array_type) ]))] else: result = [(name, self._element)] else: # _element is one of All, Choice, Group, Sequence if self._element: result.append((generator.get_name(), self._element)) return result @property def _array_type(self): attrs = {attr.qname.text: attr for attr in self._attributes if attr.qname} array_type = attrs.get('{http://schemas.xmlsoap.org/soap/encoding/}arrayType') return array_type def parse_xmlelement(self, xmlelement, schema=None, allow_none=True, context=None, schema_type=None): """Consume matching xmlelements and call parse() on each :param xmlelement: XML element objects :type xmlelement: lxml.etree._Element :param schema: The parent XML schema :type schema: zeep.xsd.Schema :param allow_none: Allow none :type allow_none: bool :param context: Optional parsing context (for inline schemas) :type context: zeep.xsd.context.XmlParserContext :param schema_type: The original type (not overriden via xsi:type) :type schema_type: zeep.xsd.types.base.Type :rtype: dict or None """ # If this is an empty complexType () if not self.attributes and not self.elements: return None attributes = xmlelement.attrib init_kwargs = OrderedDict() # If this complexType extends a simpleType then we have no nested # elements. Parse it directly via the type object. This is the case # for xsd:simpleContent if isinstance(self._element, Element) and isinstance(self._element.type, AnySimpleType): name, element = self.elements_nested[0] init_kwargs[name] = element.type.parse_xmlelement( xmlelement, schema, name, context=context) else: elements = deque(xmlelement.iterchildren()) if allow_none and len(elements) == 0 and len(attributes) == 0: return # Parse elements. These are always indicator elements (all, choice, # group, sequence) assert len(self.elements_nested) < 2 for name, element in self.elements_nested: try: result = element.parse_xmlelements( elements, schema, name, context=context) if result: init_kwargs.update(result) except UnexpectedElementError as exc: raise XMLParseError(exc.message) # Check if all children are consumed (parsed) if elements: if schema.strict: raise XMLParseError("Unexpected element %r" % elements[0].tag) else: init_kwargs['_raw_elements'] = elements # Parse attributes if attributes: attributes = copy.copy(attributes) for name, attribute in self.attributes: if attribute.name: if attribute.qname.text in attributes: value = attributes.pop(attribute.qname.text) init_kwargs[name] = attribute.parse(value) else: init_kwargs[name] = attribute.parse(attributes) value = self._value_class(**init_kwargs) schema_type = schema_type or self if schema_type and getattr(schema_type, '_array_type', None): value = schema_type._array_class.from_value_object(value) return value def render(self, parent, value, xsd_type=None, render_path=None): """Serialize the given value lxml.Element subelements on the parent element. :type parent: lxml.etree._Element :type value: Union[list, dict, zeep.xsd.valueobjects.CompoundValue] :type xsd_type: zeep.xsd.types.base.Type :param render_path: list """ if not render_path: render_path = [self.name] if not self.elements_nested and not self.attributes: return # TODO: Implement test case for this if value is None: value = {} if isinstance(value, ArrayValue): value = value.as_value_object() # Render attributes for name, attribute in self.attributes: attr_value = value[name] if name in value else NotSet child_path = render_path + [name] attribute.render(parent, attr_value, child_path) if ( len(self.elements_nested) == 1 and isinstance(value, self.accepted_types) and not isinstance(value, (list, dict, CompoundValue)) ): element = self.elements_nested[0][1] element.type.render(parent, value, None, child_path) return # Render sub elements for name, element in self.elements_nested: if isinstance(element, Element) or element.accepts_multiple: element_value = value[name] if name in value else NotSet child_path = render_path + [name] else: element_value = value child_path = list(render_path) # We want to explicitly skip this sub-element if element_value is SkipValue: continue if isinstance(element, Element): element.type.render(parent, element_value, None, child_path) else: element.render(parent, element_value, child_path) if xsd_type: if xsd_type._xsd_name: parent.set(xsi_ns('type'), xsd_type._xsd_name) if xsd_type.qname: parent.set(xsi_ns('type'), xsd_type.qname) def parse_kwargs(self, kwargs, name, available_kwargs): """Parse the kwargs for this type and return the accepted data as a dict. :param kwargs: The kwargs :type kwargs: dict :param name: The name as which this type is registered in the parent :type name: str :param available_kwargs: The kwargs keys which are still available, modified in place :type available_kwargs: set :rtype: dict """ value = None name = name or self.name if name in available_kwargs: value = kwargs[name] available_kwargs.remove(name) value = self._create_object(value, name) return {name: value} return {} def _create_object(self, value, name): """Return the value as a CompoundValue object :type value: str :type value: list, dict, CompoundValue """ if value is None: return None if isinstance(value, list) and not self._array_type: return [self._create_object(val, name) for val in value] if isinstance(value, CompoundValue) or value is SkipValue: return value if isinstance(value, dict): return self(**value) # Try to automatically create an object. This might fail if there # are multiple required arguments. return self(value) def resolve(self): """Resolve all sub elements and types""" if self._resolved: return self._resolved self._resolved = self resolved = [] for attribute in self._attributes: value = attribute.resolve() assert value is not None if isinstance(value, list): resolved.extend(value) else: resolved.append(value) self._attributes = resolved if self._extension: self._extension = self._extension.resolve() self._resolved = self.extend(self._extension) elif self._restriction: self._restriction = self._restriction.resolve() self._resolved = self.restrict(self._restriction) if self._element: self._element = self._element.resolve() return self._resolved def extend(self, base): """Create a new ComplexType instance which is the current type extending the given base type. Used for handling xsd:extension tags TODO: Needs a rewrite where the child containers are responsible for the extend functionality. :type base: zeep.xsd.types.base.Type :rtype base: zeep.xsd.types.base.Type """ if isinstance(base, ComplexType): base_attributes = base._attributes_unwrapped base_element = base._element else: base_attributes = [] base_element = None attributes = base_attributes + self._attributes_unwrapped # Make sure we don't have duplicate (child is leading) if base_attributes and self._attributes_unwrapped: new_attributes = OrderedDict() for attr in attributes: if isinstance(attr, AnyAttribute): new_attributes['##any'] = attr else: new_attributes[attr.qname.text] = attr attributes = new_attributes.values() # If the base and the current type both have an element defined then # these need to be merged. The base_element might be empty (or just # container a placeholder element). element = [] if self._element and base_element: self._element = self._element.resolve() base_element = base_element.resolve() element = self._element.clone(self._element.name) if isinstance(base_element, OrderIndicator): if isinstance(self._element, Choice): element = base_element.clone(self._element.name) element.append(self._element) elif isinstance(element, OrderIndicator): for item in reversed(base_element): element.insert(0, item) elif isinstance(element, Group): for item in reversed(base_element): element.child.insert(0, item) elif isinstance(self._element, Group): raise NotImplementedError('TODO') else: pass # Element (ignore for now) elif self._element or base_element: element = self._element or base_element else: element = Element('_value_1', base) new = self.__class__( element=element, attributes=attributes, qname=self.qname, is_global=self.is_global) new._extension_types = base.accepted_types return new def restrict(self, base): """Create a new complextype instance which is the current type restricted by the base type. Used for handling xsd:restriction :type base: zeep.xsd.types.base.Type :rtype base: zeep.xsd.types.base.Type """ attributes = list( chain(base._attributes_unwrapped, self._attributes_unwrapped)) # Make sure we don't have duplicate (self is leading) if base._attributes_unwrapped and self._attributes_unwrapped: new_attributes = OrderedDict() for attr in attributes: if isinstance(attr, AnyAttribute): new_attributes['##any'] = attr else: new_attributes[attr.qname.text] = attr attributes = list(new_attributes.values()) if base._element: base._element.resolve() new = self.__class__( element=self._element or base._element, attributes=attributes, qname=self.qname, is_global=self.is_global) return new.resolve() def signature(self, schema=None, standalone=True): parts = [] for name, element in self.elements_nested: part = element.signature(schema, standalone=False) parts.append(part) for name, attribute in self.attributes: part = '%s: %s' % (name, attribute.signature(schema, standalone=False)) parts.append(part) value = ', '.join(parts) if standalone: return '%s(%s)' % (self.get_prefixed_name(schema), value) else: return value zeep-2.5.0/src/zeep/xsd/types/simple.py0000644000076500000240000000523613224161177022020 0ustar mvantellingenstaff00000000000000import logging import six from lxml import etree from zeep.exceptions import ValidationError from zeep.xsd.const import Nil, xsd_ns, xsi_ns from zeep.xsd.types.any import AnyType logger = logging.getLogger(__name__) __all__ = ['AnySimpleType'] @six.python_2_unicode_compatible class AnySimpleType(AnyType): _default_qname = xsd_ns('anySimpleType') def __init__(self, qname=None, is_global=False): super(AnySimpleType, self).__init__( qname or etree.QName(self._default_qname), is_global) def __call__(self, *args, **kwargs): """Return the xmlvalue for the given value. Expects only one argument 'value'. The args, kwargs handling is done here manually so that we can return readable error messages instead of only '__call__ takes x arguments' """ num_args = len(args) + len(kwargs) if num_args != 1: raise TypeError(( '%s() takes exactly 1 argument (%d given). ' + 'Simple types expect only a single value argument' ) % (self.__class__.__name__, num_args)) if kwargs and 'value' not in kwargs: raise TypeError(( '%s() got an unexpected keyword argument %r. ' + 'Simple types expect only a single value argument' ) % (self.__class__.__name__, next(six.iterkeys(kwargs)))) value = args[0] if args else kwargs['value'] return self.xmlvalue(value) def __eq__(self, other): return ( other is not None and self.__class__ == other.__class__ and self.__dict__ == other.__dict__) def __str__(self): return '%s(value)' % (self.__class__.__name__) def parse_xmlelement(self, xmlelement, schema=None, allow_none=True, context=None, schema_type=None): if xmlelement.text is None: return try: return self.pythonvalue(xmlelement.text) except (TypeError, ValueError): logger.exception("Error during xml -> python translation") return None def pythonvalue(self, xmlvalue): raise NotImplementedError( '%s.pytonvalue() not implemented' % self.__class__.__name__) def render(self, parent, value, xsd_type=None, render_path=None): if value is Nil: parent.set(xsi_ns('nil'), 'true') return parent.text = self.xmlvalue(value) def signature(self, schema=None, standalone=True): return self.get_prefixed_name(schema) def validate(self, value, required=False): if required and value is None: raise ValidationError("Value is required") zeep-2.5.0/src/zeep/xsd/types/unresolved.py0000644000076500000240000000335613224161177022716 0ustar mvantellingenstaff00000000000000from zeep.xsd.types.base import Type from zeep.xsd.types.collection import UnionType # FIXME from zeep.xsd.types.simple import AnySimpleType # FIXME class UnresolvedType(Type): def __init__(self, qname, schema): self.qname = qname assert self.qname.text != 'None' self.schema = schema def __repr__(self): return '<%s(qname=%r)>' % (self.__class__.__name__, self.qname.text) def render(self, parent, value, xsd_type=None, render_path=None): raise RuntimeError( "Unable to render unresolved type %s. This is probably a bug." % ( self.qname)) def resolve(self): retval = self.schema.get_type(self.qname) return retval.resolve() class UnresolvedCustomType(Type): def __init__(self, qname, base_type, schema): assert qname is not None self.qname = qname self.name = str(qname.localname) self.schema = schema self.base_type = base_type def __repr__(self): return '<%s(qname=%r, base_type=%r)>' % ( self.__class__.__name__, self.qname.text, self.base_type) def resolve(self): base = self.base_type base = base.resolve() cls_attributes = { '__module__': 'zeep.xsd.dynamic_types', } if issubclass(base.__class__, UnionType): xsd_type = type(self.name, (base.__class__,), cls_attributes) return xsd_type(base.item_types) elif issubclass(base.__class__, AnySimpleType): xsd_type = type(self.name, (base.__class__,), cls_attributes) return xsd_type(self.qname) else: xsd_type = type(self.name, (base.base_class,), cls_attributes) return xsd_type(self.qname) zeep-2.5.0/src/zeep/xsd/utils.py0000644000076500000240000000312213224161177020513 0ustar mvantellingenstaff00000000000000from six.moves import range from zeep import ns class NamePrefixGenerator(object): def __init__(self, prefix='_value_'): self._num = 1 self._prefix = prefix def get_name(self): retval = '%s%d' % (self._prefix, self._num) self._num += 1 return retval class UniqueNameGenerator(object): def __init__(self): self._unique_count = {} def create_name(self, name): if name in self._unique_count: self._unique_count[name] += 1 return '%s__%d' % (name, self._unique_count[name]) else: self._unique_count[name] = 0 return name def max_occurs_iter(max_occurs, items=None): assert max_occurs is not None generator = range(0, max_occurs if max_occurs != 'unbounded' else 2**31-1) if items is not None: for i, sub_kwargs in zip(generator, items): yield sub_kwargs else: for i in generator: yield i def create_prefixed_name(qname, schema): """Convert a QName to a xsd:name ('ns1:myType'). :type qname: lxml.etree.QName :type schema: zeep.xsd.schema.Schema :rtype: str """ if not qname: return if schema and qname.namespace: prefix = schema.get_shorthand_for_ns(qname.namespace) if prefix: return '%s:%s' % (prefix, qname.localname) elif qname.namespace in ns.NAMESPACE_TO_PREFIX: prefix = ns.NAMESPACE_TO_PREFIX[qname.namespace] return '%s:%s' % (prefix, qname.localname) if qname.namespace: return qname.text return qname.localname zeep-2.5.0/src/zeep/xsd/valueobjects.py0000644000076500000240000001557013224161177022053 0ustar mvantellingenstaff00000000000000import copy from collections import OrderedDict from zeep.xsd.printer import PrettyPrinter __all__ = ['AnyObject', 'CompoundValue'] class AnyObject(object): """Create an any object :param xsd_object: the xsd type :param value: The value """ def __init__(self, xsd_object, value): self.xsd_obj = xsd_object self.value = value def __repr__(self): return '<%s(type=%r, value=%r)>' % ( self.__class__.__name__, self.xsd_elm, self.value) def __deepcopy__(self, memo): return type(self)(self.xsd_elm, copy.deepcopy(self.value)) @property def xsd_type(self): return self.xsd_obj @property def xsd_elm(self): return self.xsd_obj def _unpickle_compound_value(name, values): """Helper function to recreate pickled CompoundValue. See CompoundValue.__reduce__ """ cls = type(name, (CompoundValue,), { '_xsd_type': None, '__module__': 'zeep.objects' }) obj = cls() obj.__values__ = values return obj class ArrayValue(list): def __init__(self, items): super(ArrayValue, self).__init__(items) def as_value_object(self): anon_type = type( self.__class__.__name__, (CompoundValue,), {'_xsd_type': self._xsd_type, '__module__': 'zeep.objects'}) return anon_type(list(self)) @classmethod def from_value_object(cls, obj): items = next(iter(obj.__values__.values())) return cls(items or []) class CompoundValue(object): """Represents a data object for a specific xsd:complexType.""" def __init__(self, *args, **kwargs): values = OrderedDict() # Can be done after unpickle if self._xsd_type is None: return # Set default values for container_name, container in self._xsd_type.elements_nested: elm_values = container.default_value if isinstance(elm_values, dict): values.update(elm_values) else: values[container_name] = elm_values # Set attributes for attribute_name, attribute in self._xsd_type.attributes: values[attribute_name] = attribute.default_value # Set elements items = _process_signature(self._xsd_type, args, kwargs) for key, value in items.items(): values[key] = value self.__values__ = values def __reduce__(self): return (_unpickle_compound_value, (self.__class__.__name__, self.__values__,)) def __contains__(self, key): return self.__values__.__contains__(key) def __eq__(self, other): if self.__class__ != other.__class__: return False other_values = {key: other[key] for key in other} return other_values == self.__values__ def __len__(self): return self.__values__.__len__() def __iter__(self): return self.__values__.__iter__() def __dir__(self): return list(self.__values__.keys()) def __repr__(self): return PrettyPrinter().pformat(self.__values__) def __delitem__(self, key): return self.__values__.__delitem__(key) def __getitem__(self, key): return self.__values__[key] def __setitem__(self, key, value): self.__values__[key] = value def __setattr__(self, key, value): if key.startswith('__') or key in ('_xsd_type', '_xsd_elm'): return super(CompoundValue, self).__setattr__(key, value) self.__values__[key] = value def __getattribute__(self, key): if key.startswith('__') or key in ('_xsd_type', '_xsd_elm'): return super(CompoundValue, self).__getattribute__(key) try: return self.__values__[key] except KeyError: raise AttributeError( "%s instance has no attribute '%s'" % ( self.__class__.__name__, key)) def __deepcopy__(self, memo): new = type(self)() new.__values__ = copy.deepcopy(self.__values__) for attr, value in self.__dict__.items(): if attr != '__values__': setattr(new, attr, value) return new def __json__(self): return self.__values__ def _process_signature(xsd_type, args, kwargs): """Return a dict with the args/kwargs mapped to the field name. Special handling is done for Choice elements since we need to record which element the user intends to use. :param fields: List of tuples (name, element) :type fields: list :param args: arg tuples :type args: tuple :param kwargs: kwargs :type kwargs: dict """ result = OrderedDict() # Process the positional arguments. args is currently still modified # in-place here if args: args = list(args) num_args = len(args) index = 0 for element_name, element in xsd_type.elements_nested: values, args, index = element.parse_args(args, index) if not values: break result.update(values) for attribute_name, attribute in xsd_type.attributes: if num_args <= index: break result[attribute_name] = args[index] index += 1 if num_args > index: raise TypeError( "__init__() takes at most %s positional arguments (%s given)" % ( len(result), num_args)) # Process the named arguments (sequence/group/all/choice). The # available_kwargs set is modified in-place. available_kwargs = set(kwargs.keys()) for element_name, element in xsd_type.elements_nested: if element.accepts_multiple: values = element.parse_kwargs( kwargs, element_name, available_kwargs) else: values = element.parse_kwargs(kwargs, None, available_kwargs) if values is not None: for key, value in values.items(): if key not in result: result[key] = value # Process the named arguments for attributes if available_kwargs: for attribute_name, attribute in xsd_type.attributes: if attribute_name in available_kwargs: available_kwargs.remove(attribute_name) result[attribute_name] = kwargs[attribute_name] # _raw_elements is a special kwarg used for unexpected unparseable xml # elements (e.g. for soap:header or when strict is disabled) if '_raw_elements' in available_kwargs and kwargs['_raw_elements']: result['_raw_elements'] = kwargs['_raw_elements'] available_kwargs.remove('_raw_elements') if available_kwargs: raise TypeError(( "%s() got an unexpected keyword argument %r. " + "Signature: `%s`" ) % ( xsd_type.qname or 'ComplexType', next(iter(available_kwargs)), xsd_type.signature(standalone=False))) return result zeep-2.5.0/src/zeep/xsd/visitor.py0000644000076500000240000011775413224161177021073 0ustar mvantellingenstaff00000000000000import keyword import logging import re from lxml import etree from zeep.exceptions import XMLParseError from zeep.loader import absolute_location, load_external from zeep.utils import as_qname, qname_attr from zeep.xsd import elements as xsd_elements from zeep.xsd import types as xsd_types from zeep.xsd.const import AUTO_IMPORT_NAMESPACES, xsd_ns from zeep.xsd.types.unresolved import UnresolvedCustomType, UnresolvedType logger = logging.getLogger(__name__) class tags(object): pass for name in [ 'schema', 'import', 'include', 'annotation', 'element', 'simpleType', 'complexType', 'simpleContent', 'complexContent', 'sequence', 'group', 'choice', 'all', 'list', 'union', 'attribute', 'any', 'anyAttribute', 'attributeGroup', 'restriction', 'extension', 'notation', ]: attr = name if name not in keyword.kwlist else name + '_' setattr(tags, attr, xsd_ns(name)) class SchemaVisitor(object): """Visitor which processes XSD files and registers global elements and types in the given schema. :param schema: :type schema: zeep.xsd.schema.Schema :param document: :type document: zeep.xsd.schema.SchemaDocument """ def __init__(self, schema, document): self.document = document self.schema = schema self._includes = set() def register_element(self, qname, instance): self.document.register_element(qname, instance) def register_attribute(self, name, instance): self.document.register_attribute(name, instance) def register_type(self, qname, instance): self.document.register_type(qname, instance) def register_group(self, qname, instance): self.document.register_group(qname, instance) def register_attribute_group(self, qname, instance): self.document.register_attribute_group(qname, instance) def register_import(self, namespace, document): self.document.register_import(namespace, document) def process(self, node, parent): visit_func = self.visitors.get(node.tag) if not visit_func: raise ValueError("No visitor defined for %r" % node.tag) result = visit_func(self, node, parent) return result def process_ref_attribute(self, node, array_type=None): ref = qname_attr(node, 'ref') if ref: ref = self._create_qname(ref) # Some wsdl's reference to xs:schema, we ignore that for now. It # might be better in the future to process the actual schema file # so that it is handled correctly if ref.namespace == 'http://www.w3.org/2001/XMLSchema': return return xsd_elements.RefAttribute( node.tag, ref, self.schema, array_type=array_type) def process_reference(self, node, **kwargs): ref = qname_attr(node, 'ref') if not ref: return if node.tag == tags.element: cls = xsd_elements.RefElement elif node.tag == tags.attribute: cls = xsd_elements.RefAttribute elif node.tag == tags.group: cls = xsd_elements.RefGroup elif node.tag == tags.attributeGroup: cls = xsd_elements.RefAttributeGroup return cls(node.tag, ref, self.schema, **kwargs) def visit_schema(self, node): """Visit the xsd:schema element and process all the child elements Definition:: Content: ( (include | import | redefine | annotation)*, (((simpleType | complexType | group | attributeGroup) | element | attribute | notation), annotation*)*) :param node: The XML node :type node: lxml.etree._Element """ assert node is not None self.document._target_namespace = node.get('targetNamespace') self.document._element_form = node.get('elementFormDefault', 'unqualified') self.document._attribute_form = node.get('attributeFormDefault', 'unqualified') for child in node: self.process(child, parent=node) def visit_import(self, node, parent): """ Definition:: Content: (annotation?) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ schema_node = None namespace = node.get('namespace') location = node.get('schemaLocation') if location: location = absolute_location(location, self.document._base_url) if not namespace and not self.document._target_namespace: raise XMLParseError( "The attribute 'namespace' must be existent if the " "importing schema has no target namespace.", filename=self._document.location, sourceline=node.sourceline) # Check if the schema is already imported before based on the # namespace. Schema's without namespace are registered as 'None' document = self.schema.documents.get_by_namespace_and_location(namespace, location) if document: logger.debug("Returning existing schema: %r", location) self.register_import(namespace, document) return document # Hardcode the mapping between the xml namespace and the xsd for now. # This seems to fix issues with exchange wsdl's, see #220 if not location and namespace == 'http://www.w3.org/XML/1998/namespace': location = 'https://www.w3.org/2001/xml.xsd' # Silently ignore import statements which we can't resolve via the # namespace and doesn't have a schemaLocation attribute. if not location: logger.debug( "Ignoring import statement for namespace %r " + "(missing schemaLocation)", namespace) return # Load the XML schema_node = load_external( location, self.schema._transport, strict=self.schema.strict) # Check if the xsd:import namespace matches the targetNamespace. If # the xsd:import statement didn't specify a namespace then make sure # that the targetNamespace wasn't declared by another schema yet. schema_tns = schema_node.get('targetNamespace') if namespace and schema_tns and namespace != schema_tns: raise XMLParseError(( "The namespace defined on the xsd:import doesn't match the " "imported targetNamespace located at %r " ) % (location), filename=self.document._location, sourceline=node.sourceline) schema = self.schema.create_new_document(schema_node, location) self.register_import(namespace, schema) return schema def visit_include(self, node, parent): """ Definition:: Content: (annotation?) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ if not node.get('schemaLocation'): raise NotImplementedError("schemaLocation is required") location = node.get('schemaLocation') if location in self._includes: return schema_node = load_external( location, self.schema._transport, base_url=self.document._base_url, strict=self.schema.strict) self._includes.add(location) # When the included document has no default namespace defined but the # parent document does have this then we should (atleast for #360) # transfer the default namespace to the included schema. We can't # update the nsmap of elements in lxml so we create a new schema with # the correct nsmap and move all the content there. if not schema_node.nsmap.get(None) and node.nsmap.get(None): nsmap = {None: node.nsmap[None]} nsmap.update(schema_node.nsmap) new = etree.Element(schema_node.tag, nsmap=nsmap) for child in schema_node: new.append(child) for key, value in schema_node.attrib.items(): new.set(key, value) schema_node = new # Use the element/attribute form defaults from the schema while # processing the nodes. element_form_default = self.document._element_form attribute_form_default = self.document._attribute_form base_url = self.document._base_url self.document._element_form = schema_node.get('elementFormDefault', 'unqualified') self.document._attribute_form = schema_node.get('attributeFormDefault', 'unqualified') self.document._base_url = absolute_location(location, self.document._base_url) # Iterate directly over the children. for child in schema_node: self.process(child, parent=schema_node) self.document._element_form = element_form_default self.document._attribute_form = attribute_form_default self.document._base_url = base_url def visit_element(self, node, parent): """ Definition:: Content: (annotation?, ( (simpleType | complexType)?, (unique | key | keyref)*)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ is_global = parent.tag == tags.schema # minOccurs / maxOccurs are not allowed on global elements if not is_global: min_occurs, max_occurs = _process_occurs_attrs(node) else: max_occurs = 1 min_occurs = 1 # If the element has a ref attribute then all other attributes cannot # be present. Short circuit that here. # Ref is prohibited on global elements (parent = schema) if not is_global: result = self.process_reference( node, min_occurs=min_occurs, max_occurs=max_occurs) if result: return result element_form = node.get('form', self.document._element_form) if element_form == 'qualified' or is_global: qname = qname_attr(node, 'name', self.document._target_namespace) else: qname = etree.QName(node.get('name')) children = node.getchildren() xsd_type = None if children: value = None for child in children: if child.tag == tags.annotation: continue elif child.tag in (tags.simpleType, tags.complexType): assert not value xsd_type = self.process(child, node) if not xsd_type: node_type = qname_attr(node, 'type') if node_type: xsd_type = self._get_type(node_type.text) else: xsd_type = xsd_types.AnyType() # Naive workaround to mark fields which are part of a choice element # as optional if parent.tag == tags.choice: min_occurs = 0 nillable = node.get('nillable') == 'true' default = node.get('default') element = xsd_elements.Element( name=qname, type_=xsd_type, min_occurs=min_occurs, max_occurs=max_occurs, nillable=nillable, default=default, is_global=is_global) # Only register global elements if is_global: self.register_element(qname, element) return element def visit_attribute(self, node, parent): """Declares an attribute. Definition:: Content: (annotation?, (simpleType?)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ is_global = parent.tag == tags.schema # Check of wsdl:arayType array_type = node.get('{http://schemas.xmlsoap.org/wsdl/}arrayType') if array_type: match = re.match('([^\[]+)', array_type) if match: array_type = match.groups()[0] qname = as_qname(array_type, node.nsmap) array_type = UnresolvedType(qname, self.schema) # If the elment has a ref attribute then all other attributes cannot # be present. Short circuit that here. # Ref is prohibited on global elements (parent = schema) if not is_global: result = self.process_ref_attribute(node, array_type=array_type) if result: return result attribute_form = node.get('form', self.document._attribute_form) if attribute_form == 'qualified' or is_global: name = qname_attr(node, 'name', self.document._target_namespace) else: name = etree.QName(node.get('name')) annotation, items = self._pop_annotation(node.getchildren()) if items: xsd_type = self.visit_simple_type(items[0], node) else: node_type = qname_attr(node, 'type') if node_type: xsd_type = self._get_type(node_type) else: xsd_type = xsd_types.AnyType() # TODO: We ignore 'prohobited' for now required = node.get('use') == 'required' default = node.get('default') attr = xsd_elements.Attribute( name, type_=xsd_type, default=default, required=required) # Only register global elements if is_global: self.register_attribute(name, attr) return attr def visit_simple_type(self, node, parent): """ Definition:: Content: (annotation?, (restriction | list | union)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ if parent.tag == tags.schema: name = node.get('name') is_global = True else: name = parent.get('name', 'Anonymous') is_global = False base_type = '{http://www.w3.org/2001/XMLSchema}string' qname = as_qname(name, node.nsmap, self.document._target_namespace) annotation, items = self._pop_annotation(node.getchildren()) child = items[0] if child.tag == tags.restriction: base_type = self.visit_restriction_simple_type(child, node) xsd_type = UnresolvedCustomType(qname, base_type, self.schema) elif child.tag == tags.list: xsd_type = self.visit_list(child, node) elif child.tag == tags.union: xsd_type = self.visit_union(child, node) else: raise AssertionError("Unexpected child: %r" % child.tag) assert xsd_type is not None if is_global: self.register_type(qname, xsd_type) return xsd_type def visit_complex_type(self, node, parent): """ Definition:: Content: (annotation?, (simpleContent | complexContent | ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)))) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ children = [] base_type = '{http://www.w3.org/2001/XMLSchema}anyType' # If the complexType's parent is an element then this type is # anonymous and should have no name defined. Otherwise it's global if parent.tag == tags.schema: name = node.get('name') is_global = True else: name = parent.get('name') is_global = False qname = as_qname(name, node.nsmap, self.document._target_namespace) cls_attributes = { '__module__': 'zeep.xsd.dynamic_types', '_xsd_name': qname, } xsd_cls = type(name, (xsd_types.ComplexType,), cls_attributes) xsd_type = None # Process content annotation, children = self._pop_annotation(node.getchildren()) first_tag = children[0].tag if children else None if first_tag == tags.simpleContent: base_type, attributes = self.visit_simple_content(children[0], node) xsd_type = xsd_cls( attributes=attributes, extension=base_type, qname=qname, is_global=is_global) elif first_tag == tags.complexContent: kwargs = self.visit_complex_content(children[0], node) xsd_type = xsd_cls(qname=qname, is_global=is_global, **kwargs) elif first_tag: element = None if first_tag in (tags.group, tags.all, tags.choice, tags.sequence): child = children.pop(0) element = self.process(child, node) attributes = self._process_attributes(node, children) xsd_type = xsd_cls( element=element, attributes=attributes, qname=qname, is_global=is_global) else: xsd_type = xsd_cls(qname=qname, is_global=is_global) if is_global: self.register_type(qname, xsd_type) return xsd_type def visit_complex_content(self, node, parent): """The complexContent element defines extensions or restrictions on a complex type that contains mixed content or elements only. Definition:: Content: (annotation?, (restriction | extension)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ child = node.getchildren()[-1] if child.tag == tags.restriction: base, element, attributes = self.visit_restriction_complex_content( child, node) return { 'attributes': attributes, 'element': element, 'restriction': base, } elif child.tag == tags.extension: base, element, attributes = self.visit_extension_complex_content( child, node) return { 'attributes': attributes, 'element': element, 'extension': base, } def visit_simple_content(self, node, parent): """Contains extensions or restrictions on a complexType element with character data or a simpleType element as content and contains no elements. Definition:: Content: (annotation?, (restriction | extension)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ child = node.getchildren()[-1] if child.tag == tags.restriction: return self.visit_restriction_simple_content(child, node) elif child.tag == tags.extension: return self.visit_extension_simple_content(child, node) raise AssertionError("Expected restriction or extension") def visit_restriction_simple_type(self, node, parent): """ Definition:: Content: (annotation?, (simpleType?, ( minExclusive | minInclusive | maxExclusive | maxInclusive | totalDigits |fractionDigits | length | minLength | maxLength | enumeration | whiteSpace | pattern)*)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ base_name = qname_attr(node, 'base') if base_name: return self._get_type(base_name) annotation, children = self._pop_annotation(node.getchildren()) if children[0].tag == tags.simpleType: return self.visit_simple_type(children[0], node) def visit_restriction_simple_content(self, node, parent): """ Definition:: Content: (annotation?, (simpleType?, ( minExclusive | minInclusive | maxExclusive | maxInclusive | totalDigits |fractionDigits | length | minLength | maxLength | enumeration | whiteSpace | pattern)* )?, ((attribute | attributeGroup)*, anyAttribute?)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ base_name = qname_attr(node, 'base') base_type = self._get_type(base_name) return base_type, [] def visit_restriction_complex_content(self, node, parent): """ Definition:: Content: (annotation?, (group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ base_name = qname_attr(node, 'base') base_type = self._get_type(base_name) annotation, children = self._pop_annotation(node.getchildren()) element = None attributes = [] if children: child = children[0] if child.tag in (tags.group, tags.all, tags.choice, tags.sequence): children.pop(0) element = self.process(child, node) attributes = self._process_attributes(node, children) return base_type, element, attributes def visit_extension_complex_content(self, node, parent): """ Definition:: Content: (annotation?, ( (group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ base_name = qname_attr(node, 'base') base_type = self._get_type(base_name) annotation, children = self._pop_annotation(node.getchildren()) element = None attributes = [] if children: child = children[0] if child.tag in (tags.group, tags.all, tags.choice, tags.sequence): children.pop(0) element = self.process(child, node) attributes = self._process_attributes(node, children) return base_type, element, attributes def visit_extension_simple_content(self, node, parent): """ Definition:: Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?)) """ base_name = qname_attr(node, 'base') base_type = self._get_type(base_name) annotation, children = self._pop_annotation(node.getchildren()) attributes = self._process_attributes(node, children) return base_type, attributes def visit_annotation(self, node, parent): """Defines an annotation. Definition:: Content: (appinfo | documentation)* :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ return def visit_any(self, node, parent): """ Definition:: Content: (annotation?, (element | group | choice | sequence | any)*) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ sub_types = [ tags.annotation, tags.any, tags.choice, tags.element, tags.group, tags.sequence ] min_occurs, max_occurs = _process_occurs_attrs(node) result = xsd_elements.Sequence( min_occurs=min_occurs, max_occurs=max_occurs) annotation, items = self._pop_annotation(node.getchildren()) for child in items: assert child.tag in sub_types, child item = self.process(child, node) assert item is not None result.append(item) assert None not in result return result def visit_all(self, node, parent): """Allows the elements in the group to appear (or not appear) in any order in the containing element. Definition:: Content: (annotation?, element*) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ sub_types = [ tags.annotation, tags.element ] result = xsd_elements.All() for child in node.iterchildren(): assert child.tag in sub_types, child item = self.process(child, node) result.append(item) assert None not in result return result def visit_group(self, node, parent): """Groups a set of element declarations so that they can be incorporated as a group into complex type definitions. Definition:: Content: (annotation?, (all | choice | sequence)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ min_occurs, max_occurs = _process_occurs_attrs(node) result = self.process_reference( node, min_occurs=min_occurs, max_occurs=max_occurs) if result: return result qname = qname_attr(node, 'name', self.document._target_namespace) # There should be only max nodes, first node (annotation) is irrelevant annotation, children = self._pop_annotation(node.getchildren()) child = children[0] item = self.process(child, parent) elm = xsd_elements.Group(name=qname, child=item) if parent.tag == tags.schema: self.register_group(qname, elm) return elm def visit_list(self, node, parent): """ Definition:: Content: (annotation?, (simpleType?)) The use of the simpleType element child and the itemType attribute is mutually exclusive. :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ item_type = qname_attr(node, 'itemType') if item_type: sub_type = self._get_type(item_type.text) else: subnodes = node.getchildren() child = subnodes[-1] # skip annotation sub_type = self.visit_simple_type(child, node) return xsd_types.ListType(sub_type) def visit_choice(self, node, parent): """ Definition:: Content: (annotation?, (element | group | choice | sequence | any)*) """ min_occurs, max_occurs = _process_occurs_attrs(node) children = node.getchildren() annotation, children = self._pop_annotation(children) choices = [] for child in children: elm = self.process(child, node) choices.append(elm) return xsd_elements.Choice( choices, min_occurs=min_occurs, max_occurs=max_occurs) def visit_union(self, node, parent): """Defines a collection of multiple simpleType definitions. Definition:: Content: (annotation?, (simpleType*)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ # TODO members = node.get('memberTypes') types = [] if members: for member in members.split(): qname = as_qname(member, node.nsmap) xsd_type = self._get_type(qname) types.append(xsd_type) else: annotation, types = self._pop_annotation(node.getchildren()) types = [self.visit_simple_type(t, node) for t in types] return xsd_types.UnionType(types) def visit_unique(self, node, parent): """Specifies that an attribute or element value (or a combination of attribute or element values) must be unique within the specified scope. The value must be unique or nil. Definition:: Content: (annotation?, (selector, field+)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ # TODO pass def visit_attribute_group(self, node, parent): """ Definition:: Content: (annotation?), ((attribute | attributeGroup)*, anyAttribute?)) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ ref = self.process_reference(node) if ref: return ref qname = qname_attr(node, 'name', self.document._target_namespace) annotation, children = self._pop_annotation(node.getchildren()) attributes = self._process_attributes(node, children) attribute_group = xsd_elements.AttributeGroup(qname, attributes) self.register_attribute_group(qname, attribute_group) def visit_any_attribute(self, node, parent): """ Definition:: Content: (annotation?) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ process_contents = node.get('processContents', 'strict') return xsd_elements.AnyAttribute(process_contents=process_contents) def visit_notation(self, node, parent): """Contains the definition of a notation to describe the format of non-XML data within an XML document. An XML Schema notation declaration is a reconstruction of XML 1.0 NOTATION declarations. Definition:: Content: (annotation?) :param node: The XML node :type node: lxml.etree._Element :param parent: The parent XML node :type parent: lxml.etree._Element """ pass def _get_type(self, name): assert name is not None name = self._create_qname(name) return UnresolvedType(name, self.schema) def _create_qname(self, name): if not isinstance(name, etree.QName): name = etree.QName(name) # Handle reserved namespace if name.namespace == 'xml': name = etree.QName( 'http://www.w3.org/XML/1998/namespace', name.localname) # Various xsd builders assume that some schema's are available by # default (actually this is mostly just the soap-enc ns). So live with # that fact and handle it by auto-importing the schema if it is # referenced. if ( name.namespace in AUTO_IMPORT_NAMESPACES and not self.document.is_imported(name.namespace) ): logger.debug( "Auto importing missing known schema: %s", name.namespace) import_node = etree.Element( tags.import_, namespace=name.namespace, schemaLocation=name.namespace) self.visit_import(import_node, None) return name def _pop_annotation(self, items): if not len(items): return None, [] if items[0].tag == tags.annotation: annotation = self.visit_annotation(items[0], None) return annotation, items[1:] return None, items def _process_attributes(self, node, items): attributes = [] for child in items: if child.tag in (tags.attribute, tags.attributeGroup, tags.anyAttribute): attribute = self.process(child, node) attributes.append(attribute) else: raise XMLParseError( "Unexpected tag `%s`" % (child.tag), filename=self.document._location, sourceline=node.sourceline) return attributes visitors = { tags.any: visit_any, tags.element: visit_element, tags.choice: visit_choice, tags.simpleType: visit_simple_type, tags.anyAttribute: visit_any_attribute, tags.complexType: visit_complex_type, tags.simpleContent: None, tags.complexContent: None, tags.sequence: visit_sequence, tags.all: visit_all, tags.group: visit_group, tags.attribute: visit_attribute, tags.import_: visit_import, tags.include: visit_include, tags.annotation: visit_annotation, tags.attributeGroup: visit_attribute_group, tags.notation: visit_notation, } def _process_occurs_attrs(node): """Process the min/max occurrence indicators""" max_occurs = node.get('maxOccurs', '1') min_occurs = int(node.get('minOccurs', '1')) if max_occurs == 'unbounded': max_occurs = 'unbounded' else: max_occurs = int(max_occurs) return min_occurs, max_occurs zeep-2.5.0/src/zeep.egg-info/0000755000076500000240000000000013224162010017662 5ustar mvantellingenstaff00000000000000zeep-2.5.0/src/zeep.egg-info/dependency_links.txt0000644000076500000240000000000113224162010023730 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/src/zeep.egg-info/not-zip-safe0000644000076500000240000000000113224162010022110 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/src/zeep.egg-info/PKG-INFO0000644000076500000240000000527413224162010020767 0ustar mvantellingenstaff00000000000000Metadata-Version: 1.1 Name: zeep Version: 2.5.0 Summary: A modern/fast Python SOAP client based on lxml / requests Home-page: http://docs.python-zeep.org Author: Michael van Tellingen Author-email: michaelvantellingen@gmail.com License: MIT Description: ======================== Zeep: Python SOAP client ======================== A fast and modern Python SOAP client Highlights: * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6 and PyPy * Build on top of lxml and requests * Support for Soap 1.1, Soap 1.2 and HTTP bindings * Support for WS-Addressing headers * Support for WSSE (UserNameToken / x.509 signing) * Support for tornado async transport via gen.coroutine (Python 2.7+) * Support for asyncio via aiohttp (Python 3.5+) * Experimental support for XOP messages Please see for more information the documentation at http://docs.python-zeep.org/ Installation ------------ .. code-block:: bash pip install zeep Usage ----- .. code-block:: python from zeep import Client client = Client('tests/wsdl_files/example.rst') client.service.ping() To quickly inspect a WSDL file use:: python -m zeep Please see the documentation at http://docs.python-zeep.org for more information. Support ======= If you want to report a bug then please first read http://docs.python-zeep.org/en/master/reporting_bugs.html I'm also able to offer commercial support. As in contracting work. Please contact me at info@mvantellingen.nl for more information. Note that asking questions or reporting bugs via this e-mail address will be ignored. Pleae use the appropriate channels for that (e.g. stackoverflow) Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy zeep-2.5.0/src/zeep.egg-info/requires.txt0000644000076500000240000000074113224162010022264 0ustar mvantellingenstaff00000000000000appdirs>=1.4.0 cached-property>=1.3.0 defusedxml>=0.4.1 isodate>=0.5.4 lxml>=3.0.0 requests>=2.7.0 requests-toolbelt>=0.7.1 six>=1.9.0 pytz [async] aiohttp>=1.0 [docs] sphinx>=1.4.0 [test] freezegun==0.3.8 mock==2.0.0 pretend==1.0.8 pytest-cov==2.5.1 pytest==3.1.3 requests_mock>=0.7.0 pytest-tornado==0.4.5 isort==4.2.15 flake8==3.3.0 flake8-blind-except==0.1.1 flake8-debugger==1.4.0 flake8-imports==0.1.1 aioresponses>=0.1.3 [tornado] tornado>=4.0.2 [xmlsec] xmlsec>=0.6.1 zeep-2.5.0/src/zeep.egg-info/SOURCES.txt0000644000076500000240000001011113224162010021540 0ustar mvantellingenstaff00000000000000CHANGES CONTRIBUTORS.rst LICENSE MANIFEST.in README.rst setup.cfg setup.py examples/async_client.py examples/code39.py examples/echo_services.py examples/eu_vat_service.py examples/http_basic_auth.py examples/km_to_miles.py examples/soap_server.py src/zeep/__init__.py src/zeep/__main__.py src/zeep/cache.py src/zeep/client.py src/zeep/exceptions.py src/zeep/helpers.py src/zeep/loader.py src/zeep/ns.py src/zeep/plugins.py src/zeep/transports.py src/zeep/utils.py src/zeep/wsa.py src/zeep.egg-info/PKG-INFO src/zeep.egg-info/SOURCES.txt src/zeep.egg-info/dependency_links.txt src/zeep.egg-info/not-zip-safe src/zeep.egg-info/requires.txt src/zeep.egg-info/top_level.txt src/zeep/asyncio/__init__.py src/zeep/asyncio/bindings.py src/zeep/asyncio/transport.py src/zeep/tornado/__init__.py src/zeep/tornado/bindings.py src/zeep/tornado/transport.py src/zeep/wsdl/__init__.py src/zeep/wsdl/attachments.py src/zeep/wsdl/definitions.py src/zeep/wsdl/parse.py src/zeep/wsdl/utils.py src/zeep/wsdl/wsdl.py src/zeep/wsdl/bindings/__init__.py src/zeep/wsdl/bindings/http.py src/zeep/wsdl/bindings/soap.py src/zeep/wsdl/messages/__init__.py src/zeep/wsdl/messages/base.py src/zeep/wsdl/messages/http.py src/zeep/wsdl/messages/mime.py src/zeep/wsdl/messages/multiref.py src/zeep/wsdl/messages/soap.py src/zeep/wsdl/messages/xop.py src/zeep/wsse/__init__.py src/zeep/wsse/compose.py src/zeep/wsse/signature.py src/zeep/wsse/username.py src/zeep/wsse/utils.py src/zeep/xsd/__init__.py src/zeep/xsd/const.py src/zeep/xsd/context.py src/zeep/xsd/printer.py src/zeep/xsd/schema.py src/zeep/xsd/utils.py src/zeep/xsd/valueobjects.py src/zeep/xsd/visitor.py src/zeep/xsd/elements/__init__.py src/zeep/xsd/elements/any.py src/zeep/xsd/elements/attribute.py src/zeep/xsd/elements/base.py src/zeep/xsd/elements/builtins.py src/zeep/xsd/elements/element.py src/zeep/xsd/elements/indicators.py src/zeep/xsd/elements/references.py src/zeep/xsd/types/__init__.py src/zeep/xsd/types/any.py src/zeep/xsd/types/base.py src/zeep/xsd/types/builtins.py src/zeep/xsd/types/collection.py src/zeep/xsd/types/complex.py src/zeep/xsd/types/simple.py src/zeep/xsd/types/unresolved.py tests/__init__.py tests/cert_valid.pem tests/cert_valid_pw.pem tests/conftest.py tests/test_asyncio_transport.py tests/test_cache.py tests/test_client.py tests/test_client_factory.py tests/test_helpers.py tests/test_loader.py tests/test_main.py tests/test_pprint.py tests/test_response.py tests/test_soap_multiref.py tests/test_soap_xop.py tests/test_tornado_transport.py tests/test_transports.py tests/test_wsa.py tests/test_wsdl.py tests/test_wsdl_arrays.py tests/test_wsdl_messages_document.py tests/test_wsdl_messages_http.py tests/test_wsdl_messages_rpc.py tests/test_wsdl_soap.py tests/test_wsse_signature.py tests/test_wsse_username.py tests/test_wsse_utils.py tests/test_xsd.py tests/test_xsd_any.py tests/test_xsd_attributes.py tests/test_xsd_builtins.py tests/test_xsd_complex_types.py tests/test_xsd_extension.py tests/test_xsd_indicators_all.py tests/test_xsd_indicators_choice.py tests/test_xsd_indicators_group.py tests/test_xsd_indicators_sequence.py tests/test_xsd_integration.py tests/test_xsd_parse.py tests/test_xsd_schemas.py tests/test_xsd_signatures.py tests/test_xsd_simple_types.py tests/test_xsd_types.py tests/test_xsd_union.py tests/test_xsd_validation.py tests/test_xsd_valueobjects.py tests/test_xsd_visitor.py tests/utils.py tests/integration/hello_world_recursive.wsdl tests/integration/hello_world_recursive_import.wsdl tests/integration/recursive_schema_a.xsd tests/integration/recursive_schema_b.xsd tests/integration/recursive_schema_c.xsd tests/integration/recursive_schema_main.wsdl tests/integration/test_hello_world_recursive.py tests/integration/test_http_post.py tests/integration/test_http_post.wsdl tests/integration/test_recursive_schema.py tests/wsdl_files/claim.wsdl tests/wsdl_files/http.wsdl tests/wsdl_files/soap-enc.xsd tests/wsdl_files/soap.wsdl tests/wsdl_files/soap_header.wsdl tests/wsdl_files/soap_import_2.wsdl tests/wsdl_files/soap_import_main.wsdl tests/wsdl_files/soap_transport_err.wsdl tests/wsdl_files/test_import_2.xsd tests/wsdl_files/xmldsig-core-schema.xsdzeep-2.5.0/src/zeep.egg-info/top_level.txt0000644000076500000240000000000513224162010022407 0ustar mvantellingenstaff00000000000000zeep zeep-2.5.0/tests/0000755000076500000240000000000013224162010015600 5ustar mvantellingenstaff00000000000000zeep-2.5.0/tests/__init__.py0000644000076500000240000000012113224161177017720 0ustar mvantellingenstaff00000000000000from zeep.client import Client # noqa from zeep.exceptions import Fault # noqa zeep-2.5.0/tests/cert_valid.pem0000644000076500000240000000643613224161177020445 0ustar mvantellingenstaff00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAyrWxlRtnAT7q2IgWEaQUCwG+0gNuqTNPUi3fLZ/7XI7lOBem TT3YeCZc7fbTLrABH8l4NEdXG58CFa/Mkdsm05Xt4G3avrYT+CNZA3tOf42TsGIB 5/Vvqd9LS4uhBUo2qqcTUz1VA8jP7CKY25dq8RLEd1gw3lLjPGqYa69sUkEYzABC uJfP9xUEySLeclxWx+ky2Pp8AAXPpDtlGdb9rszxYhSJtce/KEsbZqaCQ9a2ZAJe FauJiUvODPNigs8vUQLGNYHcmEm4SvVkSSgvpKekAk1gC51OqLYypp8c916n1DG2 PC5b9kTj6mUXkBoa9jjWJQYkE8zG5wbaM7xAGwIDAQABAoIBAQCPlGqYRrSK+Vm2 zY10KVfZA/95Gd1EE4fXmY4+7tZIFR7ewEGW7HtrfyDLnMywgRIKxdVSkkVs1w/O 9JpdpXC25bd8A9OwyZ8TX1YpVSmgx1MY2BKpjfrtw6+9bsU6zfoynezeRM72w0Ii 686Bm5qv7q8iKWFT2DoEDSyw+awsBZQokVTCwHFWdbXZ50mAXoXxovn19DTRNqzD yqO8dae9gjk16vap7gRpB60Y/YZ4Rf46X47SlRqTcqgEB/C/1jyGtl3jQlaLq4KL POe1jFZYGUZTctmRvsol4VdSzfITqr/kd3DhJw0LxvXnT6c02wxzKLCSo2HnN6HT A7l6eEWhAoGBAPZ46R8qPln9uGhbdYjtEjR+vxDhQcuCNkjn40psEOyXu62cFhFO FSj3lVCyRCHIhrUWUzJIQTIPDnczH7iwrWZlqUujjYKs3DJcpu7J5B4ODatklXO+ 2NZa45XEto6ygOPUp7OYZhLlGpjWnC2yp0XLqAEC0URkc1zOTTfJ0VFNAoGBANKL tXPJLOZ2F1e3IPkX6y1hfbfbRlyuA2vai/2cAhbld4oZIpm7Yy6Jw4BFuDaUs02P nDGBBh6EVgbZNZphZEUhgvglSdJaa2/3cS+1pGcnjmYMj4xywHpOxiomgZ8Xa1LW ZuJdD2SajS0yPYcrEDg+xBQBvDpE0NEIka6Zu6MHAoGBAMVbKegPjl/GvuupGGMs 2Z/5QYsFpAaN3GPicmh8Qc0A7oHkcvMmX+Eu5nv4Un/urpbAKpwfqTypO78My8C6 kA5nJvlvG/ff7G3TLMQWGzhJrn5oCxfkYIK7wnKBUmDO5FAKTsKLLGjC1No/Nk2N OU209nDgzaqC+LD+bGxYiOgdAoGAWFtXD7s6Q5EFZMMubDqUcFv8dV7pHVXNi8KQ gyKoYdF0pBi+Q4O3ML2RtNANaaJnyMHey4uY9M+WhpM7AomimbxhiR+k5kkZ00gl UN9Kmhuoj7zvtQInMmzCjsfQF+KtIHtne9GP9ylA29m8pm/1A5WblcXQpydf9olB EePkMZsCgYABr07cGT31CXxrbQwDTgiQJm2JHq/wIR+q0eys1aiMvKRN+0arfqvz j8zPK6B9SRcCXY4XAda3rilsF/7eHf2zkg/0kHV6NqaSWFEA8yqAoIqpc03cE/ef lUgGakZ6Wb0sucIRB40loAZIu0lN0kF45K1P8JDHg74jk6uM2xnZvg== -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- MIIEqjCCA5KgAwIBAgIJALOCBen0S+W5MA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD VQQGEwJOTDEQMA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHVXRyZWNodDEeMBwG A1UEChMVTWljaGFlbCB2YW4gVGVsbGluZ2VuMRwwGgYDVQQDExN3d3cucHl0aG9u LXplZXAub3JnMSMwIQYJKoZIhvcNAQkBFhRpbmZvQHB5dGhvbi16ZWVwLm9yZzAe Fw0xNzAxMjUxOTI3NTJaFw0yNzAxMjMxOTI3NTJaMIGUMQswCQYDVQQGEwJOTDEQ MA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHVXRyZWNodDEeMBwGA1UEChMVTWlj aGFlbCB2YW4gVGVsbGluZ2VuMRwwGgYDVQQDExN3d3cucHl0aG9uLXplZXAub3Jn MSMwIQYJKoZIhvcNAQkBFhRpbmZvQHB5dGhvbi16ZWVwLm9yZzCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMq1sZUbZwE+6tiIFhGkFAsBvtIDbqkzT1It 3y2f+1yO5TgXpk092HgmXO320y6wAR/JeDRHVxufAhWvzJHbJtOV7eBt2r62E/gj WQN7Tn+Nk7BiAef1b6nfS0uLoQVKNqqnE1M9VQPIz+wimNuXavESxHdYMN5S4zxq mGuvbFJBGMwAQriXz/cVBMki3nJcVsfpMtj6fAAFz6Q7ZRnW/a7M8WIUibXHvyhL G2amgkPWtmQCXhWriYlLzgzzYoLPL1ECxjWB3JhJuEr1ZEkoL6SnpAJNYAudTqi2 MqafHPdep9QxtjwuW/ZE4+plF5AaGvY41iUGJBPMxucG2jO8QBsCAwEAAaOB/DCB +TAdBgNVHQ4EFgQUxd12m9nIS0QO4uIPRy7oerPyVygwgckGA1UdIwSBwTCBvoAU xd12m9nIS0QO4uIPRy7oerPyVyihgZqkgZcwgZQxCzAJBgNVBAYTAk5MMRAwDgYD VQQIEwdVdHJlY2h0MRAwDgYDVQQHEwdVdHJlY2h0MR4wHAYDVQQKExVNaWNoYWVs IHZhbiBUZWxsaW5nZW4xHDAaBgNVBAMTE3d3dy5weXRob24temVlcC5vcmcxIzAh BgkqhkiG9w0BCQEWFGluZm9AcHl0aG9uLXplZXAub3JnggkAs4IF6fRL5bkwDAYD VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAHTUp/i9FYbvl86By7EvMlZeK v6I38IYcrIGzDdbrk8KkilYv7p2Ll8gUJYRFj96iX6Uvn0ACTutFJW9xE2ytBMOu UurTBpcpk8k368gfO/fGVi6HzjyFqTnhLkmd3CADIzPN/yg5j2q+mgA3ys6wISBR aDJR2jGt9sTAkAwkVJdDCFkCwyRfB28mBRnI5SLeR5vQyLT97THPma39xR3FaqYv h2q3coXBnaOOcuigiKyIynhJtXH42XlN3TM23b9NK2Oep2e51pxst3uohlDGmB/W uzx/hG+kNxy9D+Ms7qNL9+i4nHFOoR034RB/NGTChzTxq2JcXIKPWIo2tslNsg== -----END CERTIFICATE----- zeep-2.5.0/tests/cert_valid_pw.pem0000644000076500000240000000654713224161177021156 0ustar mvantellingenstaff00000000000000-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,415D98F4452C4F85 5QDgVsTX78dXETR9RfVoAPXA3M0WKB6YibDZCavgMXBorWxOAaQz+chSehaJHHsi ZYWgxSUYsiZk5NVZLi3Pidg3zx8G1HWThxRmWakiEqseVTX9B/H2h1AEwd7AQ+5K kbLXbZ/CGTSkrVA+IjEWRtSjigaNfrDiyNO6YfLWjZrYk8mC2EVRnMD7LGDPL4E0 rD9sFT4zAovZEe+WBGCWjzRiBMgIvY+4frsX3u9MkTYFxpNMkdDozUJu5sxr0asB 68fSfLo2lDYuH9azWQe6aeMLkW/1QalRsYwGurZ/PW6DEnfkhN0ikdz0vpGARohL if+7wsSxKU6MRd5GntMqQ3uE+C5GxoiJ0T51+b/MDpo7hwhnhAqLvjPZNAcOtOip krLI3BPiyraBkY+km3EheDxX3XjGIl381577WO4HxKUtcT1KOYBLgN1VVdgrrlt3 mLO9kexl6h57bi3OZxxanuX76zE7gvwVLgagMY/8Ek2TJJXhSne5YIrC5WPT1bQv nFenU8VzcF0xf02vTCos9Os6obeSJr3Lzz6StIj+WVytMD/QmfvG/0TCtlmq+av4 VR7OWgnNcXWXPsoy6+8JwZLZGz3+Q2tav2JjGOCHneTIlgRyBKQX7sT6M8DGcLYf mZgysrA048Hh3bcPmk4W7p4/dHZBU1HR6KT34wKjMuLIEXuMVITfBK+Gv6Is9Q3h sVWJ2ChGcTkAIyqIaRglU0QBKUgOs4q2HsAxrefOXStaKlmawDjttFHhPveyKXCr NSqNMwK16g+9S1LcxCNoX8y1qTDObhGO/A3uTm81RcDN4gzQiZNd6ZM5ynuPyAAn 8vACDiAXaf1SnAJjBuuqKm3dTXQgu+Ise7KmQXoherzHoXUZa0T49hCowb+WOJsB ljmEoehxVAPrgs8GmzvpSiJfCw2NgowaG75wMt8zTKAGgIht+R5xRNqp9g6Qgyn8 0Z4E6JTZ8JRN9FtutUc0NKLuwiai2F4qwUzmOTPLiFTT6sAQrDX+JSmezPAVbtw0 rchz/p1jCS+V0k78D/UwV/mGkXeqPUn370MVK0boPfi1ZrjASi6/ztaB7nza7QKP LCoZC7P6cbWqJG3g4MUxR/zDIdLF6XGBuSp2i6GmHlIi464AIv+O0Oz1ftIC/Xt2 HZQoL7KW9NeU2WdIkczxQnPc/DucK/dkyKzzxLDr7ztU9E4maYTNd4Ml6B2azysY gyvAKq9ui8zYAZt096mACUSLj1/yNZKwlGUuyeq3XLaeqsHAGecEJsg3smnJ39Rd B4RSx3BDEQ4GiS1zNUaxs5KCCqtV4iylYWdUu6Y9H/koLuj04/c9qOjYCzIDDgTS M8asTkZIjXeknBb+RSTYj48Q1Vom2zjFhunPKvPmSgfdDGzX24ipagwZvmzLxlg1 6edI75EBMMetVVwMWWe3ZU/AlERe/QVNBZUO2TwiVM2VvIVkaaa1pFYDv/eWeGLD HdJ1Hoz7O/gX6eKVY6OJu3nIwAXDTutxYC1VpZ6a48OrOWHuz352wkmw7aGcn0GS ZYjbizCZckvuDKk8PFpGbF7gPmaxtVKx/hijoqRJlRIJq9pSXTxbq7HEXPKq9dtI -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- MIIEqjCCA5KgAwIBAgIJALOCBen0S+W5MA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD VQQGEwJOTDEQMA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHVXRyZWNodDEeMBwG A1UEChMVTWljaGFlbCB2YW4gVGVsbGluZ2VuMRwwGgYDVQQDExN3d3cucHl0aG9u LXplZXAub3JnMSMwIQYJKoZIhvcNAQkBFhRpbmZvQHB5dGhvbi16ZWVwLm9yZzAe Fw0xNzAxMjUxOTI3NTJaFw0yNzAxMjMxOTI3NTJaMIGUMQswCQYDVQQGEwJOTDEQ MA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHVXRyZWNodDEeMBwGA1UEChMVTWlj aGFlbCB2YW4gVGVsbGluZ2VuMRwwGgYDVQQDExN3d3cucHl0aG9uLXplZXAub3Jn MSMwIQYJKoZIhvcNAQkBFhRpbmZvQHB5dGhvbi16ZWVwLm9yZzCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMq1sZUbZwE+6tiIFhGkFAsBvtIDbqkzT1It 3y2f+1yO5TgXpk092HgmXO320y6wAR/JeDRHVxufAhWvzJHbJtOV7eBt2r62E/gj WQN7Tn+Nk7BiAef1b6nfS0uLoQVKNqqnE1M9VQPIz+wimNuXavESxHdYMN5S4zxq mGuvbFJBGMwAQriXz/cVBMki3nJcVsfpMtj6fAAFz6Q7ZRnW/a7M8WIUibXHvyhL G2amgkPWtmQCXhWriYlLzgzzYoLPL1ECxjWB3JhJuEr1ZEkoL6SnpAJNYAudTqi2 MqafHPdep9QxtjwuW/ZE4+plF5AaGvY41iUGJBPMxucG2jO8QBsCAwEAAaOB/DCB +TAdBgNVHQ4EFgQUxd12m9nIS0QO4uIPRy7oerPyVygwgckGA1UdIwSBwTCBvoAU xd12m9nIS0QO4uIPRy7oerPyVyihgZqkgZcwgZQxCzAJBgNVBAYTAk5MMRAwDgYD VQQIEwdVdHJlY2h0MRAwDgYDVQQHEwdVdHJlY2h0MR4wHAYDVQQKExVNaWNoYWVs IHZhbiBUZWxsaW5nZW4xHDAaBgNVBAMTE3d3dy5weXRob24temVlcC5vcmcxIzAh BgkqhkiG9w0BCQEWFGluZm9AcHl0aG9uLXplZXAub3JnggkAs4IF6fRL5bkwDAYD VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAHTUp/i9FYbvl86By7EvMlZeK v6I38IYcrIGzDdbrk8KkilYv7p2Ll8gUJYRFj96iX6Uvn0ACTutFJW9xE2ytBMOu UurTBpcpk8k368gfO/fGVi6HzjyFqTnhLkmd3CADIzPN/yg5j2q+mgA3ys6wISBR aDJR2jGt9sTAkAwkVJdDCFkCwyRfB28mBRnI5SLeR5vQyLT97THPma39xR3FaqYv h2q3coXBnaOOcuigiKyIynhJtXH42XlN3TM23b9NK2Oep2e51pxst3uohlDGmB/W uzx/hG+kNxy9D+Ms7qNL9+i4nHFOoR034RB/NGTChzTxq2JcXIKPWIo2tslNsg== -----END CERTIFICATE----- zeep-2.5.0/tests/conftest.py0000644000076500000240000000123713224161177020017 0ustar mvantellingenstaff00000000000000import sys import pytest # Don't try to test asyncio since it is py3 only syntax if sys.version_info < (3, 5): collect_ignore = [ 'test_asyncio_transport.py' ] pytest.register_assert_rewrite('tests.utils') @pytest.fixture(autouse=True) def no_requests(request, monkeypatch): if request.node.get_marker('requests'): return def func(*args, **kwargs): pytest.fail("External connections not allowed during tests.") monkeypatch.setattr("socket.socket", func) @pytest.yield_fixture() def event_loop(): import asyncio loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) yield loop loop.close() zeep-2.5.0/tests/integration/0000755000076500000240000000000013224162010020123 5ustar mvantellingenstaff00000000000000zeep-2.5.0/tests/integration/hello_world_recursive.wsdl0000644000076500000240000002201313224161177025432 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/tests/integration/hello_world_recursive_import.wsdl0000644000076500000240000000730513224161177027033 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/tests/integration/recursive_schema_a.xsd0000644000076500000240000000040013224161177024501 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/tests/integration/recursive_schema_b.xsd0000644000076500000240000000040013224161177024502 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/tests/integration/recursive_schema_c.xsd0000644000076500000240000000040013224161177024503 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/tests/integration/recursive_schema_main.wsdl0000644000076500000240000000222013224161177025362 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/tests/integration/test_hello_world_recursive.py0000644000076500000240000000033313224161177026151 0ustar mvantellingenstaff00000000000000import os import zeep def test_hello_world(): path = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'hello_world_recursive.wsdl') client = zeep.Client(path) client.wsdl.dump() zeep-2.5.0/tests/integration/test_http_post.py0000644000076500000240000000165613224161177023605 0ustar mvantellingenstaff00000000000000import os import pytest import requests_mock import zeep WSDL = os.path.join(os.path.dirname(__file__), 'test_http_post.wsdl') @pytest.mark.requests def test_get_urlreplacement(): client = zeep.Client(WSDL) with requests_mock.mock() as m: m.get('http://example.com/companyinfo/o1/EUR/', text='Hoi') result = client.service.o1('EUR') assert result == 'Hoi' history = m.request_history[0] assert history._request.path_url == '/companyinfo/o1/EUR/' @pytest.mark.requests def test_post_mime_content(): client = zeep.Client(WSDL, service_name='CompanyInfoService', port_name='Port3') with requests_mock.mock() as m: m.post('http://example.com/companyinfo/o1', text='Hoi') result = client.service.o1('EUR') assert result == 'Hoi' history = m.request_history[0] assert history._request.path_url == '/companyinfo/o1' zeep-2.5.0/tests/integration/test_http_post.wsdl0000644000076500000240000000416113224161177024120 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/tests/integration/test_recursive_schema.py0000644000076500000240000000033313224161177025077 0ustar mvantellingenstaff00000000000000import os import zeep def test_hello_world(): path = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'recursive_schema_main.wsdl') client = zeep.Client(path) client.wsdl.dump() zeep-2.5.0/tests/test_asyncio_transport.py0000644000076500000240000000430613224161177023012 0ustar mvantellingenstaff00000000000000import aiohttp import pytest from aioresponses import aioresponses from lxml import etree from pretend import stub from zeep import asyncio, exceptions @pytest.mark.requests def test_no_cache(event_loop): transport = asyncio.AsyncTransport(loop=event_loop) assert transport.cache is None @pytest.mark.requests def test_load(event_loop): cache = stub(get=lambda url: None, add=lambda url, content: None) transport = asyncio.AsyncTransport(loop=event_loop, cache=cache) with aioresponses() as m: m.get('http://tests.python-zeep.org/test.xml', body='x') result = transport.load('http://tests.python-zeep.org/test.xml') assert result == b'x' @pytest.mark.requests @pytest.mark.asyncio async def test_post(event_loop): cache = stub(get=lambda url: None, add=lambda url, content: None) transport = asyncio.AsyncTransport(loop=event_loop, cache=cache) envelope = etree.Element('Envelope') with aioresponses() as m: m.post('http://tests.python-zeep.org/test.xml', body='x') result = await transport.post_xml( 'http://tests.python-zeep.org/test.xml', envelope=envelope, headers={}) assert result.content == b'x' @pytest.mark.requests @pytest.mark.asyncio async def test_session_close(event_loop): transport = asyncio.AsyncTransport(loop=event_loop) session = transport.session # copy session object from transport del transport assert session.closed @pytest.mark.requests @pytest.mark.asyncio async def test_session_no_close(event_loop): session = aiohttp.ClientSession(loop=event_loop) transport = asyncio.AsyncTransport(loop=event_loop, session=session) del transport assert not session.closed @pytest.mark.requests def test_http_error(event_loop): transport = asyncio.AsyncTransport(loop=event_loop) with aioresponses() as m: m.get( 'http://tests.python-zeep.org/test.xml', body='x', status=500, ) with pytest.raises(exceptions.TransportError) as exc: transport.load('http://tests.python-zeep.org/test.xml') assert exc.value.status_code == 500 assert exc.value.message is None zeep-2.5.0/tests/test_cache.py0000644000076500000240000000300713224161177020271 0ustar mvantellingenstaff00000000000000import datetime import freezegun from zeep import cache def test_sqlite_cache(tmpdir): c = cache.SqliteCache(path=tmpdir.join('sqlite.cache.db').strpath) c.add('http://tests.python-zeep.org/example.wsdl', b'content') result = c.get('http://tests.python-zeep.org/example.wsdl') assert result == b'content' def test_sqlite_cache_timeout(tmpdir): c = cache.SqliteCache(path=tmpdir.join('sqlite.cache.db').strpath) c.add('http://tests.python-zeep.org/example.wsdl', b'content') result = c.get('http://tests.python-zeep.org/example.wsdl') assert result == b'content' freeze_dt = datetime.datetime.utcnow() + datetime.timedelta(seconds=7200) with freezegun.freeze_time(freeze_dt): result = c.get('http://tests.python-zeep.org/example.wsdl') assert result is None def test_memory_cache_timeout(tmpdir): c = cache.InMemoryCache() c.add('http://tests.python-zeep.org/example.wsdl', b'content') result = c.get('http://tests.python-zeep.org/example.wsdl') assert result == b'content' freeze_dt = datetime.datetime.utcnow() + datetime.timedelta(seconds=7200) with freezegun.freeze_time(freeze_dt): result = c.get('http://tests.python-zeep.org/example.wsdl') assert result is None def test_memory_cache_share_data(tmpdir): a = cache.InMemoryCache() b = cache.InMemoryCache() a.add('http://tests.python-zeep.org/example.wsdl', b'content') result = b.get('http://tests.python-zeep.org/example.wsdl') assert result == b'content' zeep-2.5.0/tests/test_client.py0000644000076500000240000002241013224161177020503 0ustar mvantellingenstaff00000000000000import os import pytest import requests_mock from tests.utils import load_xml from zeep import client, xsd from zeep.exceptions import Error def test_bind(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') service = client_obj.bind() assert service def test_unknown_transport(): client_obj = client.Client('tests/wsdl_files/soap_transport_err.wsdl') service = client_obj.bind() assert service def test_bind_service(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') service = client_obj.bind('StockQuoteService') assert service def test_bind_service_port(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') service = client_obj.bind('StockQuoteService', 'StockQuotePort') assert service def test_service_proxy_ok(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') assert client_obj.service.GetLastTradePrice def test_service_proxy_non_existing(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') with pytest.raises(AttributeError): assert client_obj.service.NonExisting def test_service_proxy_dir_operations(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') operations = [op for op in dir(client_obj.service) if not op.startswith('_')] assert set(operations) == set(['GetLastTradePrice', 'GetLastTradePriceNoOutput']) def test_operation_proxy_doc(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') assert (client_obj.service.GetLastTradePrice.__doc__ == 'GetLastTradePrice(tickerSymbol: xsd:string, ' 'account: ns0:account, ' 'country: ns0:country) -> price: xsd:float') def test_open_from_file_object(): with open('tests/wsdl_files/soap_transport_err.wsdl', 'rb') as fh: client_obj = client.Client(fh) service = client_obj.bind() assert service def test_client_no_wsdl(): with pytest.raises(ValueError): client.Client(None) def test_client_cache_service(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') assert client_obj.service.GetLastTradePrice assert client_obj.service.GetLastTradePrice def test_force_https(): with open('tests/wsdl_files/soap.wsdl') as fh: response = fh.read() with requests_mock.mock() as m: url = 'https://tests.python-zeep.org/wsdl' m.get(url, text=response, status_code=200) client_obj = client.Client(url) binding_options = client_obj.service._binding_options assert binding_options['address'].startswith('https') expected_url = 'https://example.com/stockquote' assert binding_options['address'] == expected_url @pytest.mark.requests def test_create_service(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') service = client_obj.create_service( '{http://example.com/stockquote.wsdl}StockQuoteBinding', 'http://test.python-zeep.org/x') response = """ 120.123 """.strip() with requests_mock.mock() as m: m.post('http://test.python-zeep.org/x', text=response) result = service.GetLastTradePrice('foobar') assert result == 120.123 assert m.request_history[0].headers['User-Agent'].startswith('Zeep/') assert m.request_history[0].body.startswith( b"") def test_load_wsdl_with_file_prefix(): cwd = os.path.dirname(__file__) client.Client( 'file://' + os.path.join(cwd, 'wsdl_files/soap.wsdl')) @pytest.mark.requests def test_service_proxy(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') response = """ 120.123 """.strip() with requests_mock.mock() as m: m.post('http://example.com/stockquote', text=response) result = client_obj.service.GetLastTradePrice('foobar') assert result == 120.123 @pytest.mark.requests def test_call_method_fault(): obj = client.Client('tests/wsdl_files/soap.wsdl') response = """ soap:Server Big fatal error!! StockListByDate wrong security code StockListByDate """.strip() with requests_mock.mock() as m: m.post('http://example.com/stockquote', text=response, status_code=500) with pytest.raises(Error): obj.service.GetLastTradePrice(tickerSymbol='foobar') def test_set_context_options_timeout(): obj = client.Client('tests/wsdl_files/soap.wsdl') assert obj.transport.operation_timeout is None with obj.options(timeout=120): assert obj.transport.operation_timeout == 120 with obj.options(timeout=90): assert obj.transport.operation_timeout == 90 assert obj.transport.operation_timeout == 120 assert obj.transport.operation_timeout is None def test_set_context_options_raw_response(): obj = client.Client('tests/wsdl_files/soap.wsdl') assert obj.raw_response is False with obj.options(raw_response=True): assert obj.raw_response is True with obj.options(): # Check that raw_response is not changed by default value assert obj.raw_response is True # Check that the original value returned assert obj.raw_response is False @pytest.mark.requests def test_default_soap_headers(): header = xsd.ComplexType( xsd.Sequence([ xsd.Element('{http://tests.python-zeep.org}name', xsd.String()), xsd.Element('{http://tests.python-zeep.org}password', xsd.String()), ]) ) header_value = header(name='ik', password='foo') client_obj = client.Client('tests/wsdl_files/soap.wsdl') client_obj.set_default_soapheaders([header_value]) response = """ 120.123 """.strip() with requests_mock.mock() as m: m.post('http://example.com/stockquote', text=response) client_obj.service.GetLastTradePrice('foobar') doc = load_xml(m.request_history[0].body) header = doc.find('{http://schemas.xmlsoap.org/soap/envelope/}Header') assert header is not None assert len(header.getchildren()) == 2 @pytest.mark.requests def test_default_soap_headers_extra(): header = xsd.ComplexType( xsd.Sequence([ xsd.Element('{http://tests.python-zeep.org}name', xsd.String()), xsd.Element('{http://tests.python-zeep.org}password', xsd.String()), ]) ) header_value = header(name='ik', password='geheim') extra_header = xsd.ComplexType( xsd.Sequence([ xsd.Element('{http://tests.python-zeep.org}name', xsd.String()), xsd.Element('{http://tests.python-zeep.org}password', xsd.String()), ]) ) extra_header_value = extra_header(name='ik', password='geheim') client_obj = client.Client('tests/wsdl_files/soap.wsdl') client_obj.set_default_soapheaders([header_value]) response = """ 120.123 """.strip() with requests_mock.mock() as m: m.post('http://example.com/stockquote', text=response) client_obj.service.GetLastTradePrice('foobar', _soapheaders=[extra_header_value]) doc = load_xml(m.request_history[0].body) header = doc.find('{http://schemas.xmlsoap.org/soap/envelope/}Header') assert header is not None assert len(header.getchildren()) == 4 zeep-2.5.0/tests/test_client_factory.py0000644000076500000240000000265513224161177022243 0ustar mvantellingenstaff00000000000000import pytest from zeep import Client def test_factory_namespace(): client = Client('tests/wsdl_files/soap.wsdl') factory = client.type_factory('http://example.com/stockquote.xsd') obj = factory.Address(NameFirst='Michael', NameLast='van Tellingen') assert obj.NameFirst == 'Michael' assert obj.NameLast == 'van Tellingen' def test_factory_no_reference(): client = Client('tests/wsdl_files/soap.wsdl') obj_1 = client.get_type('ns0:ArrayOfAddress')() obj_1.Address.append({ 'NameFirst': 'J', 'NameLast': 'Doe', }) obj_2 = client.get_type('ns0:ArrayOfAddress')() assert len(obj_2.Address) == 0 def test_factory_ns_auto_prefix(): client = Client('tests/wsdl_files/soap.wsdl') factory = client.type_factory('ns0') obj = factory.Address(NameFirst='Michael', NameLast='van Tellingen') assert obj.NameFirst == 'Michael' assert obj.NameLast == 'van Tellingen' def test_factory_ns_custom_prefix(): client = Client('tests/wsdl_files/soap.wsdl') client.set_ns_prefix('sq', 'http://example.com/stockquote.xsd') factory = client.type_factory('sq') obj = factory.Address(NameFirst='Michael', NameLast='van Tellingen') assert obj.NameFirst == 'Michael' assert obj.NameLast == 'van Tellingen' def test_factory_ns_unknown_prefix(): client = Client('tests/wsdl_files/soap.wsdl') with pytest.raises(ValueError): client.type_factory('bla') zeep-2.5.0/tests/test_helpers.py0000644000076500000240000001671313224161177020700 0ustar mvantellingenstaff00000000000000import datetime from collections import OrderedDict from lxml import etree from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import helpers, xsd from zeep.helpers import serialize_object def test_serialize_simple(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'name'), xsd.String()), xsd.Attribute( etree.QName('http://tests.python-zeep.org/', 'attr'), xsd.String()), ]) )) obj = custom_type(name='foo', attr='x') assert obj.name == 'foo' assert obj.attr == 'x' result = serialize_object(obj) assert result == { 'name': 'foo', 'attr': 'x', } def test_serialize_nested_complex_type(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'items'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'x'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'y'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'x'), xsd.String()), ]) ) ) ]) ), max_occurs=2 ) ]) )) obj = custom_type( items=[ {'x': 'bla', 'y': {'x': 'deep'}}, {'x': 'foo', 'y': {'x': 'deeper'}}, ]) assert len(obj.items) == 2 obj.items[0].x == 'bla' obj.items[0].y.x == 'deep' obj.items[1].x == 'foo' obj.items[1].y.x == 'deeper' result = serialize_object(obj) assert result == { 'items': [ {'x': 'bla', 'y': {'x': 'deep'}}, {'x': 'foo', 'y': {'x': 'deeper'}}, ] } def test_nested_complex_types(): schema = xsd.Schema(load_xml(""" """)) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') item_type = schema.get_type('{http://tests.python-zeep.org/}item') instance = container_elm(item=item_type(item_1='foo')) result = serialize_object(instance) assert isinstance(result, dict), type(result) assert isinstance(result['item'], dict), type(result['item']) assert result['item']['item_1'] == 'foo' def test_serialize_any_array(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Any(max_occurs=2), ]) )) any_obj = etree.Element('{http://tests.python-zeep.org}lxml') etree.SubElement(any_obj, 'node').text = 'foo' obj = custom_type(any_obj) expected = """ foo """ node = etree.Element('document') custom_type.render(node, obj) assert_nodes_equal(expected, node) schema = xsd.Schema() obj = custom_type.parse(node.getchildren()[0], schema=schema) result = serialize_object(obj) assert result == { '_value_1': [any_obj], } def test_create_xml_soap_map(): data = OrderedDict([ ('text', u'String'), ('bytes', b'Bytes'), ('boolean', True), ('integer', 100), ('float', 100.1234), ('datetime', datetime.datetime(2017, 10, 28, 12, 30, 10)), ('date', datetime.date(2016, 1, 14)), ]) value = helpers.create_xml_soap_map(data) expected = """ text String bytes Bytes boolean true integer 100 float 100.1234 datetime 2017-10-28T12:30:10 date 2016-01-14 """ # noqa node = render_node(value._xsd_type, value) assert_nodes_equal(expected, node) zeep-2.5.0/tests/test_loader.py0000644000076500000240000000100213224161177020465 0ustar mvantellingenstaff00000000000000from tests.utils import DummyTransport from zeep.loader import parse_xml def test_huge_text(): # libxml2>=2.7.3 has XML_MAX_TEXT_LENGTH 10000000 without XML_PARSE_HUGE tree = parse_xml(u""" %s """ % (u'\u00e5' * 10000001), DummyTransport(), xml_huge_tree=True) assert tree[0][0].text == u'\u00e5' * 10000001 zeep-2.5.0/tests/test_main.py0000644000076500000240000000161513224161177020155 0ustar mvantellingenstaff00000000000000from mock import patch from pretend import stub from zeep import __main__, client def test_main_no_args(monkeypatch): def mock_init(self, *args, **kwargs): self.wsdl = stub(dump=lambda: None) monkeypatch.setattr(client.Client, '__init__', mock_init) args = __main__.parse_arguments(['foo.wsdl']) __main__.main(args) def test_main_extract_auth(monkeypatch): def mock_init(self, *args, **kwargs): self.wsdl = stub(dump=lambda: None) monkeypatch.setattr(client.Client, '__init__', mock_init) with patch.object(__main__, 'Transport', autospec=True) as mock_transport: args = __main__.parse_arguments( ['http://user:secret@tests.python-zeep.org/foo.wsdl']) __main__.main(args) assert mock_transport.call_count == 1 args, kwargs = mock_transport.call_args assert kwargs['session'].auth == ('user', 'secret') zeep-2.5.0/tests/test_pprint.py0000644000076500000240000000115513224161177020544 0ustar mvantellingenstaff00000000000000from zeep.xsd import printer def test_dict(): pprint = printer.PrettyPrinter() data = { 'foo': 'bar', 'foo_2': 'bar', 'foo_3': 'bar', 'foo_4': { 'foo': '1', 'bar': { 'bala': 'qwe', }, 'x': [1, 2, 3, 4], 'y': [], } } pprint.pformat(data) def test_list(): pprint = printer.PrettyPrinter() data = [ { 'foo': 'bar', 'foo_2': 'bar', }, { 'foo': 'bar', 'foo_2': 'bar', }, ] pprint.pformat(data) zeep-2.5.0/tests/test_response.py0000644000076500000240000000700613224161177021067 0ustar mvantellingenstaff00000000000000from lxml import etree from zeep.xsd import Schema def test_parse_response(): schema_node = etree.fromstring(b""" """.strip()) # noqa response_node = etree.fromstring(b""" 45313 ABC100 10 ABC200 20 """.strip()) schema = Schema(schema_node.find('*/{http://www.w3.org/2001/XMLSchema}schema')) assert schema response_type = schema.get_element( '{http://tests.python-zeep.org/}ZeepExampleResponse') nsmap = { 'soap': 'http://schemas.xmlsoap.org/soap/envelope/', 'tns': 'http://tests.python-zeep.org/', } node = response_node.find('soap:Body/tns:ZeepExampleResponse', namespaces=nsmap) assert node is not None obj = response_type.parse(node, schema) assert obj.ZeepExampleResult.SomeValue == 45313 assert len(obj.ZeepExampleResult.Results.Item) == 2 assert obj.ZeepExampleResult.Results.Item[0].Key == 'ABC100' assert obj.ZeepExampleResult.Results.Item[0].Value == 10 assert obj.ZeepExampleResult.Results.Item[1].Key == 'ABC200' assert obj.ZeepExampleResult.Results.Item[1].Value == 20 zeep-2.5.0/tests/test_soap_multiref.py0000644000076500000240000002410113224161177022075 0ustar mvantellingenstaff00000000000000import io import pytest from pretend import stub from zeep import Client from zeep.transports import Transport @pytest.mark.requests def test_parse_multiref_soap_response(): wsdl_file = io.StringIO(u""" Test service """.strip()) # noqa content = """ foo bar bar foo bar """.strip() client = Client(wsdl_file, transport=Transport(),) response = stub( status_code=200, headers={}, content=content) operation = client.service._binding._operations['TestOperation'] result = client.service._binding.process_reply( client, operation, response) assert result.item_1.subitem_1 == 'foo' assert result.item_1.subitem_2 == 'bar' assert result.item_2.subitem_1.subitem_1 == 'foo' assert result.item_2.subitem_1.subitem_2 == 'bar' assert result.item_2.subitem_2 == 'bar' @pytest.mark.requests def test_parse_multiref_soap_response_child(): wsdl_file = io.StringIO(u""" Test service """.strip()) # noqa content = """ foo bar bar foo bar """.strip() # noqa client = Client(wsdl_file, transport=Transport(),) response = stub( status_code=200, headers={}, content=content) operation = client.service._binding._operations['TestOperation'] result = client.service._binding.process_reply( client, operation, response) assert result.item_1.subitem_1 == 'foo' assert result.item_1.subitem_2 == 'bar' assert result.item_2.subitem_1.subitem_1 == 'foo' assert result.item_2.subitem_1.subitem_2 == 'bar' assert result.item_2.subitem_2 == 'bar' zeep-2.5.0/tests/test_soap_xop.py0000644000076500000240000002173013224161177021061 0ustar mvantellingenstaff00000000000000import requests_mock from lxml import etree from pretend import stub from requests_toolbelt.multipart.decoder import MultipartDecoder from six import StringIO from tests.utils import assert_nodes_equal from zeep import Client from zeep.transports import Transport from zeep.wsdl.attachments import MessagePack from zeep.wsdl.messages import xop def test_rebuild_xml(): data = '\r\n'.join(line.strip() for line in """ --MIME_boundary Content-Type: application/soap+xml; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-ID: 5XJ45-3B2 accident --MIME_boundary Content-Type: image/jpeg Content-Transfer-Encoding: binary Content-ID: ...binary JPG image... --MIME_boundary-- """.splitlines()).encode('utf-8') response = stub( status_code=200, content=data, encoding=None, headers={ 'Content-Type': 'multipart/related; boundary=MIME_boundary; type="application/soap+xml"; start="" 1' } ) decoder = MultipartDecoder( response.content, response.headers['Content-Type'], 'utf-8') document = etree.fromstring(decoder.parts[0].content) message_pack = MessagePack(parts=decoder.parts[1:]) xop.process_xop(document, message_pack) expected = """ 5XJ45-3B2 accident Li4uYmluYXJ5IEpQRyBpbWFnZS4uLg== """ assert_nodes_equal(etree.tostring(document), expected) def test_xop(): wsdl_main = StringIO(""" Test service """.strip()) client = Client(wsdl_main, transport=Transport()) service = client.create_service( "{http://tests.python-zeep.org/xsd-main}TestBinding", "http://tests.python-zeep.org/test") content_type = 'multipart/related; boundary="boundary"; type="application/xop+xml"; start=""; start-info="application/soap+xml; charset=utf-8"' response1 = '\r\n'.join(line.strip() for line in """ Content-Type: application/xop+xml; charset=utf-8; type="application/soap+xml" Content-Transfer-Encoding: binary Content-ID: --boundary Content-Type: application/binary Content-Transfer-Encoding: binary Content-ID: BINARYDATA --boundary-- """.splitlines()) response2 = '\r\n'.join(line.strip() for line in """ Content-Type: application/xop+xml; charset=utf-8; type="application/soap+xml" Content-Transfer-Encoding: binary Content-ID: --boundary Content-Type: application/binary Content-Transfer-Encoding: binary Content-ID: BINARYDATA --boundary-- """.splitlines()) print(response1) with requests_mock.mock() as m: m.post('http://tests.python-zeep.org/test', content=response2.encode("utf-8"), headers={"Content-Type": content_type}) result = service.TestOperation2("") assert result["_value_1"] == "BINARYDATA".encode() m.post( 'http://tests.python-zeep.org/test', content=response1.encode("utf-8"), headers={"Content-Type": content_type}) result = service.TestOperation1("") assert result == "BINARYDATA".encode() zeep-2.5.0/tests/test_tornado_transport.py0000644000076500000240000000355013224161177023013 0ustar mvantellingenstaff00000000000000import pytest from lxml import etree from mock import patch from pretend import stub from tornado.concurrent import Future from tornado.httpclient import HTTPRequest, HTTPResponse from tornado.testing import AsyncTestCase, gen_test from zeep.tornado import TornadoAsyncTransport class TornadoAsyncTransportTest(AsyncTestCase): @pytest.mark.requests def test_no_cache(self): transport = TornadoAsyncTransport() assert transport.cache is None @pytest.mark.requests @patch('tornado.httpclient.HTTPClient.fetch') @gen_test def test_load(self, mock_httpclient_fetch): cache = stub(get=lambda url: None, add=lambda url, content: None) response = HTTPResponse(HTTPRequest('http://tests.python-zeep.org/test.xml'), 200) response.buffer = True response._body = 'x' mock_httpclient_fetch.return_value = response transport = TornadoAsyncTransport(cache=cache) result = transport.load('http://tests.python-zeep.org/test.xml') assert result == 'x' @pytest.mark.requests @patch('tornado.httpclient.AsyncHTTPClient.fetch') @gen_test def test_post(self, mock_httpclient_fetch): cache = stub(get=lambda url: None, add=lambda url, content: None) response = HTTPResponse(HTTPRequest('http://tests.python-zeep.org/test.xml'), 200) response.buffer = True response._body = 'x' http_fetch_future = Future() http_fetch_future.set_result(response) mock_httpclient_fetch.return_value = http_fetch_future transport = TornadoAsyncTransport(cache=cache) envelope = etree.Element('Envelope') result = yield transport.post_xml( 'http://tests.python-zeep.org/test.xml', envelope=envelope, headers={}) assert result.content == 'x' assert result.status_code == 200 zeep-2.5.0/tests/test_transports.py0000644000076500000240000000137313224161177021451 0ustar mvantellingenstaff00000000000000import pytest import requests_mock from pretend import stub from zeep import cache, transports @pytest.mark.requests def test_no_cache(): transport = transports.Transport(cache=None) assert transport.cache is None @pytest.mark.requests def test_custom_cache(): transport = transports.Transport(cache=cache.SqliteCache()) assert isinstance(transport.cache, cache.SqliteCache) @pytest.mark.requests def test_load(): cache = stub(get=lambda url: None, add=lambda url, content: None) transport = transports.Transport(cache=cache) with requests_mock.mock() as m: m.get('http://tests.python-zeep.org/test.xml', text='x') result = transport.load('http://tests.python-zeep.org/test.xml') assert result == b'x' zeep-2.5.0/tests/test_wsa.py0000644000076500000240000002522013224161177020021 0ustar mvantellingenstaff00000000000000import uuid from pretend import stub from six import StringIO from tests.utils import DummyTransport, assert_nodes_equal from zeep import Client, wsa, wsdl def test_require_wsa(recwarn, monkeypatch): monkeypatch.setattr( uuid, 'uuid4', lambda: uuid.UUID('ada686f9-5995-4088-bea4-239f694b2eaf')) wsdl_main = StringIO(""" Test service """.strip()) client = stub(plugins=[], wsse=None) transport = DummyTransport() client = Client(wsdl_main, transport=transport) binding = client.wsdl.services.get('TestService').ports.get('TestPortType').binding envelope, headers = binding._create( 'TestOperation1', args=['foo'], kwargs={}, client=client, options={'address': 'http://tests.python-zeep.org/test'}) expected = """ urn:dummyRequest urn:uuid:ada686f9-5995-4088-bea4-239f694b2eaf http://tests.python-zeep.org/test foo """ assert_nodes_equal(expected, envelope) def test_force_wsa(recwarn, monkeypatch): monkeypatch.setattr( uuid, 'uuid4', lambda: uuid.UUID('ada686f9-5995-4088-bea4-239f694b2eaf')) wsdl_main = StringIO(""" Test service """.strip()) transport = DummyTransport() client = Client(wsdl_main, transport=transport, plugins=[wsa.WsAddressingPlugin()]) binding = client.wsdl.services.get('TestService').ports.get('TestPortType').binding envelope, headers = binding._create( 'TestOperation1', args=['foo'], kwargs={}, client=client, options={'address': 'http://tests.python-zeep.org/test'}) expected = """ urn:dummyRequest urn:uuid:ada686f9-5995-4088-bea4-239f694b2eaf http://tests.python-zeep.org/test foo """ assert_nodes_equal(expected, envelope) def test_force_wsa_soap12(recwarn, monkeypatch): monkeypatch.setattr( uuid, 'uuid4', lambda: uuid.UUID('ada686f9-5995-4088-bea4-239f694b2eaf')) wsdl_main = StringIO(""" Test service """.strip()) client = stub(plugins=[wsa.WsAddressingPlugin()], wsse=None) transport = DummyTransport() doc = wsdl.Document(wsdl_main, transport) binding = doc.services.get('TestService').ports.get('TestPortType').binding envelope, headers = binding._create( 'TestOperation1', args=['foo'], kwargs={}, client=client, options={'address': 'http://tests.python-zeep.org/test'}) expected = """ urn:dummyRequest urn:uuid:ada686f9-5995-4088-bea4-239f694b2eaf http://tests.python-zeep.org/test foo """ assert_nodes_equal(expected, envelope) assert headers['Content-Type'] == ( 'application/soap+xml; charset=utf-8; action="urn:dummyRequest"') zeep-2.5.0/tests/test_wsdl.py0000644000076500000240000010443613224161177020207 0ustar mvantellingenstaff00000000000000import io import pytest import requests_mock from lxml import etree from pretend import stub from six import StringIO from tests.utils import DummyTransport, assert_nodes_equal from zeep import Client, wsdl from zeep.transports import Transport @pytest.mark.requests def test_parse_soap_wsdl(): client = Client('tests/wsdl_files/soap.wsdl', transport=Transport(),) response = """ 120.123 """.strip() client.set_ns_prefix('stoc', 'http://example.com/stockquote.xsd') with requests_mock.mock() as m: m.post('http://example.com/stockquote', text=response) account_type = client.get_type('stoc:account') account = account_type(id=100) account.user = 'mvantellingen' country = client.get_element('stoc:country').type() country.name = 'The Netherlands' country.code = 'NL' result = client.service.GetLastTradePrice( tickerSymbol='foobar', account=account, country=country) assert result == 120.123 request = m.request_history[0] # Compare request body expected = """ foobar 100 mvantellingen The Netherlands NL """ assert_nodes_equal(expected, request.body) @pytest.mark.requests def test_parse_soap_header_wsdl(): client = Client('tests/wsdl_files/soap_header.wsdl', transport=Transport(),) response = """ 120.123 """.strip() with requests_mock.mock() as m: m.post('http://example.com/stockquote', text=response) result = client.service.GetLastTradePrice( tickerSymbol='foobar', _soapheaders={ 'header': { 'username': 'ikke', 'password': 'oeh-is-geheim!', } }) assert result.body.price == 120.123 assert result.header.body is None request = m.request_history[0] # Compare request body expected = """ ikke oeh-is-geheim! foobar """ assert_nodes_equal(expected, request.body) def test_parse_types_multiple_schemas(): content = StringIO(""" """.strip()) assert wsdl.Document(content, None) def test_parse_types_nsmap_issues(): content = StringIO(""" """.strip()) assert wsdl.Document(content, None) @pytest.mark.requests def test_parse_soap_import_wsdl(): client = stub(transport=Transport(), wsse=None) content = io.open( 'tests/wsdl_files/soap-enc.xsd', 'r', encoding='utf-8').read() with requests_mock.mock() as m: m.get('http://schemas.xmlsoap.org/soap/encoding/', text=content) obj = wsdl.Document( 'tests/wsdl_files/soap_import_main.wsdl', transport=client.transport) assert len(obj.services) == 1 assert obj.types.is_empty is False obj.dump() def test_multiple_extension(): content = StringIO(""" """.strip()) document = wsdl.Document(content, None) type_a = document.types.get_element('ns0:typetje') type_a(wat='x') type_a = document.types.get_type('ns0:type_a') type_a(wat='x') def test_create_import_schema(recwarn): content = StringIO(""" """.strip()) schema_node_a = etree.fromstring(""" """.strip()) schema_node_b = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/a.xsd', schema_node_a) transport.bind('http://tests.python-zeep.org/b.xsd', schema_node_b) document = wsdl.Document( content, transport, 'http://tests.python-zeep.org/content.wsdl') assert len(recwarn) == 0 assert document.types.get_element('{http://tests.python-zeep.org/b}global') def test_wsdl_imports_xsd(recwarn): content = StringIO(""" """.strip()) schema_node_a = etree.fromstring(""" """.strip()) schema_node_b = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/a.xsd', schema_node_a) transport.bind('http://tests.python-zeep.org/b.xsd', schema_node_b) wsdl.Document( content, transport, 'http://tests.python-zeep.org/content.wsdl') def test_import_schema_without_location(recwarn): content = StringIO(""" """.strip()) schema_node_a = etree.fromstring(""" """.strip()) schema_node_b = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/a.xsd', schema_node_a) transport.bind('http://tests.python-zeep.org/b.xsd', schema_node_b) document = wsdl.Document( content, transport, 'http://tests.python-zeep.org/content.wsdl') assert len(recwarn) == 0 assert document.types.get_type('{http://tests.python-zeep.org/b}foo') def test_wsdl_import(recwarn): wsdl_main = StringIO(""" Test service """.strip()) wsdl_2 = (""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/schema-2.wsdl', wsdl_2) document = wsdl.Document(wsdl_main, transport) document.dump() def test_wsdl_import_transitive(recwarn): wsdl_main = StringIO(""" Test service """.strip()) wsdl_2 = (""" """.strip()) wsdl_3 = (""" """.strip()) wsdl_4 = (""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/schema-2.wsdl', wsdl_2) transport.bind('http://tests.python-zeep.org/schema-3.wsdl', wsdl_3) transport.bind('http://tests.python-zeep.org/schema-4.wsdl', wsdl_4) document = wsdl.Document(wsdl_main, transport) document.dump() def test_wsdl_import_xsd_references(recwarn): wsdl_main = StringIO(""" Test service """.strip()) wsdl_2 = (""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/schema-2.wsdl', wsdl_2) document = wsdl.Document(wsdl_main, transport) document.dump() def test_parse_operation_empty_nodes(): content = StringIO(""" Example documentation. """.strip()) assert wsdl.Document(content, None) def test_wsdl_duplicate_tns(recwarn): wsdl_main = StringIO(""" Test service """.strip()) wsdl_2 = (""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/schema-2.wsdl', wsdl_2) document = wsdl.Document(wsdl_main, transport) document.dump() zeep-2.5.0/tests/test_wsdl_arrays.py0000644000076500000240000005172713224161177021574 0ustar mvantellingenstaff00000000000000import io import pytest from lxml import etree from tests.utils import ( DummyTransport, assert_nodes_equal, load_xml, render_node) from zeep import xsd @pytest.fixture(scope='function') def transport(): transport = DummyTransport() transport.bind( 'http://schemas.xmlsoap.org/soap/encoding/', load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8'))) return transport def test_simple_type(transport): schema = xsd.Schema(load_xml(""" """), transport=transport) ArrayOfString = schema.get_type('ns0:ArrayOfString') print(ArrayOfString.__dict__) value = ArrayOfString(['item', 'and', 'even', 'more', 'items']) node = etree.Element('document') ArrayOfString.render(node, value) expected = """ item and even more items """ # noqa assert_nodes_equal(expected, node) data = ArrayOfString.parse_xmlelement(node, schema) assert data == ['item', 'and', 'even', 'more', 'items'] assert data.as_value_object() def test_simple_type_nested(transport): schema = xsd.Schema(load_xml(""" """), transport=transport) Container = schema.get_type('ns0:container') value = Container(strings=['item', 'and', 'even', 'more', 'items']) assert value.strings == ['item', 'and', 'even', 'more', 'items'] node = etree.Element('document') Container.render(node, value) expected = """ item and even more items """ # noqa assert_nodes_equal(expected, node) data = Container.parse_xmlelement(node, schema) assert data.strings == ['item', 'and', 'even', 'more', 'items'] def test_simple_type_nested_inline_type(transport): schema = xsd.Schema(load_xml(""" """), transport=transport) Container = schema.get_type('ns0:container') node = load_xml(""" item and even more items """) # noqa data = Container.parse_xmlelement(node, schema) assert data.strings == ['item', 'and', 'even', 'more', 'items'] def test_complex_type(transport): schema = xsd.Schema(load_xml(""" """), transport=transport) ArrayOfObject = schema.get_type('ns0:ArrayOfObject') ArrayObject = schema.get_type('ns0:ArrayObject') value = ArrayOfObject([ ArrayObject(attr_1='attr-1', attr_2='attr-2'), ArrayObject(attr_1='attr-3', attr_2='attr-4'), ArrayObject(attr_1='attr-5', attr_2='attr-6'), ]) node = etree.Element('document') ArrayOfObject.render(node, value) expected = """ attr-1 attr-2 attr-3 attr-4 attr-5 attr-6 """ assert_nodes_equal(expected, node) data = ArrayOfObject.parse_xmlelement(node, schema) assert data[0].attr_1 == 'attr-1' assert data[0].attr_2 == 'attr-2' assert data[1].attr_1 == 'attr-3' assert data[1].attr_2 == 'attr-4' assert data[2].attr_1 == 'attr-5' assert data[2].attr_2 == 'attr-6' def test_complex_type_without_name(transport): schema = xsd.Schema(load_xml(""" """), transport=transport) ArrayOfObject = schema.get_type('ns0:ArrayOfObject') ArrayObject = schema.get_type('ns0:ArrayObject') value = ArrayOfObject([ ArrayObject(attr_1='attr-1', attr_2='attr-2'), ArrayObject(attr_1='attr-3', attr_2='attr-4'), ArrayObject(attr_1='attr-5', attr_2='attr-6'), ]) node = etree.Element('document') ArrayOfObject.render(node, value) expected = """ attr-1 attr-2 attr-3 attr-4 attr-5 attr-6 """ assert_nodes_equal(expected, node) data = ArrayOfObject.parse_xmlelement(node, schema) assert len(data) == 3 assert data[0]['attr_1'] == 'attr-1' assert data[0]['attr_2'] == 'attr-2' assert data[1]['attr_1'] == 'attr-3' assert data[1]['attr_2'] == 'attr-4' assert data[2]['attr_1'] == 'attr-5' assert data[2]['attr_2'] == 'attr-6' def test_soap_array_parse_remote_ns(): transport = DummyTransport() transport.bind( 'http://schemas.xmlsoap.org/soap/encoding/', load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8'))) schema = xsd.Schema(load_xml(""" """), transport) doc = load_xml(""" NL The Netherlands """) elm = schema.get_element('ns0:countries') data = elm.parse(doc, schema) assert data[0].code == 'NL' assert data[0].name == 'The Netherlands' def test_wsdl_array_type(): transport = DummyTransport() transport.bind( 'http://schemas.xmlsoap.org/soap/encoding/', load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8'))) schema = xsd.Schema(load_xml(""" """), transport) array_elm = schema.get_element('{http://tests.python-zeep.org/}array') item_type = schema.get_type('{http://tests.python-zeep.org/}base') item_1 = item_type(item_1='foo_1', item_2='bar_1') item_2 = item_type(item_1='foo_2', item_2='bar_2') # array = array_elm([ # xsd.AnyObject(item_type, item_1), # xsd.AnyObject(item_type, item_2), # ]) array = array_elm([item_1, item_2]) node = etree.Element('document') assert array_elm.signature(schema=schema) == 'ns0:array(ns0:array)' array_type = schema.get_type('ns0:array') assert array_type.signature(schema=schema) == ( 'ns0:array(_value_1: base[], arrayType: xsd:string, ' + 'offset: ns1:arrayCoordinate, id: xsd:ID, href: xsd:anyURI, _attr_1: {})') array_elm.render(node, array) expected = """ foo_1 bar_1 foo_2 bar_2 """ assert_nodes_equal(expected, node) def test_soap_array_parse(): transport = DummyTransport() transport.bind( 'http://schemas.xmlsoap.org/soap/encoding/', load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8'))) schema = xsd.Schema(load_xml(""" """), transport) doc = load_xml(""" flag1 value1 flag2 value2 """) elm = schema.get_element('ns0:FlagDetailsList') data = elm.parse(doc, schema) assert data[0].Name == 'flag1' assert data[0].Value == 'value1' assert data[1].Name == 'flag2' assert data[1].Value == 'value2' def test_xml_soap_enc_string(transport): schema = xsd.Schema(load_xml(""" """), transport) shoe_type = schema.get_element('{http://tests.python-zeep.org/}value') obj = shoe_type(["foo"]) node = render_node(shoe_type, obj) expected = """ foo """ assert_nodes_equal(expected, node) obj = shoe_type.parse(node[0], schema) assert obj[0]['_value_1'] == "foo" # Via string-types string_type = schema.get_type('{http://schemas.xmlsoap.org/soap/encoding/}string') obj = shoe_type([string_type('foo')]) node = render_node(shoe_type, obj) expected = """ foo """ assert_nodes_equal(expected, node) obj = shoe_type.parse(node[0], schema) assert obj[0]['_value_1'] == "foo" # Via dicts string_type = schema.get_type('{http://schemas.xmlsoap.org/soap/encoding/}string') obj = shoe_type([{'_value_1': 'foo'}]) node = render_node(shoe_type, obj) expected = """ foo """ assert_nodes_equal(expected, node) obj = shoe_type.parse(node[0], schema) assert obj[0]['_value_1'] == "foo" zeep-2.5.0/tests/test_wsdl_messages_document.py0000644000076500000240000013255313224161177023775 0ustar mvantellingenstaff00000000000000from lxml import etree from six import StringIO from tests.utils import assert_nodes_equal, load_xml from zeep import xsd from zeep.wsdl import wsdl def test_parse(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.body.signature(schema=root.types) == 'ns0:Request(xsd:string)' assert operation.input.header.signature(schema=root.types) == 'soap-env:Header()' assert operation.input.envelope.signature(schema=root.types) == 'soap-env:envelope(body: xsd:string)' assert operation.input.signature(as_output=False) == 'xsd:string' assert operation.output.body.signature(schema=root.types) == 'ns0:Response(xsd:string)' assert operation.output.header.signature(schema=root.types) == 'soap-env:Header()' assert operation.output.envelope.signature(schema=root.types) == 'soap-env:envelope(body: xsd:string)' assert operation.output.signature(as_output=True) == 'xsd:string' def test_parse_with_header(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.body.signature(schema=root.types) == 'ns0:Request(xsd:string)' assert operation.input.header.signature(schema=root.types) == 'soap-env:Header(auth: xsd:string)' assert operation.input.envelope.signature(schema=root.types) == 'soap-env:envelope(header: {auth: xsd:string}, body: xsd:string)' # noqa assert operation.input.signature(as_output=False) == 'xsd:string, _soapheaders={auth: xsd:string}' # noqa assert operation.output.body.signature(schema=root.types) == 'ns0:Response(xsd:string)' assert operation.output.header.signature(schema=root.types) == 'soap-env:Header(auth: xsd:string)' assert operation.output.envelope.signature(schema=root.types) == 'soap-env:envelope(header: {auth: xsd:string}, body: xsd:string)' # noqa assert operation.output.signature(as_output=True) == 'header: {auth: xsd:string}, body: xsd:string' # noqa def test_parse_with_header_type(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.body.signature(schema=root.types) == 'ns0:Request(xsd:string)' assert operation.input.header.signature(schema=root.types) == 'soap-env:Header(auth: ns0:RequestHeaderType)' assert operation.input.envelope.signature(schema=root.types) == 'soap-env:envelope(header: {auth: ns0:RequestHeaderType}, body: xsd:string)' # noqa assert operation.input.signature(as_output=False) == 'xsd:string, _soapheaders={auth: ns0:RequestHeaderType}' # noqa assert operation.output.body.signature(schema=root.types) == 'ns0:Response(xsd:string)' assert operation.output.header.signature(schema=root.types) == 'soap-env:Header(auth: ns0:ResponseHeaderType)' assert operation.output.envelope.signature(schema=root.types) == 'soap-env:envelope(header: {auth: ns0:ResponseHeaderType}, body: xsd:string)' # noqa assert operation.output.signature(as_output=True) == 'header: {auth: ns0:ResponseHeaderType}, body: xsd:string' # noqa def test_parse_with_header_other_message(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) root.types.set_ns_prefix('soap-env', 'http://schemas.xmlsoap.org/soap/envelope/') binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.header.signature(schema=root.types) == 'soap-env:Header(header: xsd:string)' assert operation.input.body.signature(schema=root.types) == 'ns0:Request(xsd:string)' header = root.types.get_element( '{http://tests.python-zeep.org/tns}RequestHeader' )('foo') serialized = operation.input.serialize( 'ah1', _soapheaders={'header': header}) expected = """ foo ah1 """ assert_nodes_equal(expected, serialized.content) def test_serialize(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') serialized = operation.input.serialize(arg1='ah1', arg2='ah2') expected = """ ah1 ah2 """ assert_nodes_equal(expected, serialized.content) def test_serialize_with_header(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') AuthHeader = root.types.get_element('{http://tests.python-zeep.org/tns}Authentication') auth_header = AuthHeader(username='mvantellingen') serialized = operation.input.serialize( arg1='ah1', arg2='ah2', _soapheaders=[auth_header]) serialized = operation.input.serialize( arg1='ah1', arg2='ah2', _soapheaders=[auth_header]) expected = """ mvantellingen ah1 ah2 """ assert_nodes_equal(expected, serialized.content) def test_serialize_with_headers_simple(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') header = xsd.ComplexType( xsd.Sequence([ xsd.Element('{http://www.w3.org/2005/08/addressing}Action', xsd.String()), xsd.Element('{http://www.w3.org/2005/08/addressing}To', xsd.String()), ]) ) header_value = header(Action='doehet', To='server') serialized = operation.input.serialize( arg1='ah1', arg2='ah2', _soapheaders=[header_value]) expected = """ doehet server ah1 ah2 """ assert_nodes_equal(expected, serialized.content) def test_serialize_with_header_and_custom_mixed(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') header = root.types.get_element( '{http://tests.python-zeep.org/tns}Authentication' ) header_1 = header(username='mvantellingen') header = xsd.Element( '{http://test.python-zeep.org/custom}custom', xsd.ComplexType([ xsd.Element('{http://test.python-zeep.org/custom}foo', xsd.String()), ]) ) header_2 = header(foo='bar') serialized = operation.input.serialize( arg1='ah1', arg2='ah2', _soapheaders=[header_1, header_2]) expected = """ mvantellingen bar ah1 ah2 """ assert_nodes_equal(expected, serialized.content) def test_serializer_with_header_custom_elm(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') header = xsd.Element( '{http://test.python-zeep.org/custom}auth', xsd.ComplexType([ xsd.Element('{http://test.python-zeep.org/custom}username', xsd.String()), ]) ) serialized = operation.input.serialize( arg1='ah1', arg2='ah2', _soapheaders=[header(username='mvantellingen')]) expected = """ mvantellingen ah1 ah2 """ assert_nodes_equal(expected, serialized.content) def test_serializer_with_header_custom_xml(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') header_value = etree.Element('{http://test.python-zeep.org/custom}auth') etree.SubElement( header_value, '{http://test.python-zeep.org/custom}username' ).text = 'mvantellingen' serialized = operation.input.serialize( arg1='ah1', arg2='ah2', _soapheaders=[header_value]) expected = """ mvantellingen ah1 ah2 """ assert_nodes_equal(expected, serialized.content) def test_deserialize(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') response_body = load_xml(""" ah1 ah2 """) result = operation.process_reply(response_body) assert result.arg1 == 'ah1' assert result.arg2 == 'ah2' def test_deserialize_no_content(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') response_body = load_xml(""" """) result = operation.process_reply(response_body) assert result is None def test_deserialize_choice(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') response_body = load_xml(""" ah1 """) result = operation.process_reply(response_body) assert result.arg1 == 'ah1' def test_deserialize_one_part(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') response_body = load_xml(""" mvantellingen ah1 ah2 """) # noqa serialized = operation.process_reply(response_body) assert serialized.arg1 == 'ah1' assert serialized.arg2 == 'ah2' def test_deserialize_with_headers(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') response_body = load_xml(""" mvantellingen foo ah1 ah2 """) # noqa serialized = operation.process_reply(response_body) assert operation.output.signature(as_output=True) == ( 'header: {header_1: ns0:Header1, header_2: xsd:string}, body: {request_1: ns0:Request1, request_2: ns0:Request2}') assert serialized.body.request_1.arg1 == 'ah1' assert serialized.body.request_2.arg2 == 'ah2' assert serialized.header.header_1.username == 'mvantellingen' def test_serialize_any_type(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') serialized = operation.input.serialize( arg1=xsd.AnyObject(xsd.String(), 'ah1')) expected = """ ah1 """ assert_nodes_equal(expected, serialized.content) deserialized = operation.input.deserialize(serialized.content) assert deserialized == 'ah1' def test_empty_input_parse(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/}Binding'] operation = binding.get('getResult') assert operation.input.signature() == '' serialized = operation.input.serialize() expected = """ """ assert_nodes_equal(expected, serialized.content) zeep-2.5.0/tests/test_wsdl_messages_http.py0000644000076500000240000003324713224161177023136 0ustar mvantellingenstaff00000000000000from six import StringIO from tests.utils import assert_nodes_equal, load_xml from zeep.wsdl import wsdl ## # URLEncoded Message # def test_urlencoded_serialize(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.body.signature(schema=root.types) == 'TestOperation(arg1: xsd:string, arg2: xsd:string)' assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string' assert operation.output.body.signature(schema=root.types) == 'TestOperation(Body: xsd:string)' assert operation.output.signature(as_output=True) == 'xsd:string' serialized = operation.input.serialize(arg1='ah1', arg2='ah2') assert serialized.headers == {'Content-Type': 'text/xml; charset=utf-8'} assert serialized.path == 'test-operation' assert serialized.content == {'arg1': 'ah1', 'arg2': 'ah2'} ## # URLReplacement Message # def test_urlreplacement_serialize(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.body.signature(schema=root.types) == 'TestOperation(arg1: xsd:string, arg2: xsd:string)' assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string' assert operation.output.body.signature(schema=root.types) == 'TestOperation(Body: xsd:string)' assert operation.output.signature(as_output=True) == 'xsd:string' serialized = operation.input.serialize(arg1='ah1', arg2='ah2') assert serialized.headers == {'Content-Type': 'text/xml; charset=utf-8'} assert serialized.path == 'test-operation/ah1/ah2/' assert serialized.content == '' ## # MimeContent Message # def test_mime_content_serialize_form_urlencoded(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.body.signature(schema=root.types) == 'TestOperation(arg1: xsd:string, arg2: xsd:string)' assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string' assert operation.output.body.signature(schema=root.types) == 'TestOperation(Body: xsd:string)' assert operation.output.signature(as_output=True) == 'xsd:string' serialized = operation.input.serialize(arg1='ah1', arg2='ah2') assert serialized.headers == {'Content-Type': 'application/x-www-form-urlencoded'} assert serialized.path == 'test-operation' assert serialized.content == 'arg1=ah1&arg2=ah2' def test_mime_content_serialize_text_xml(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.body.signature(schema=root.types) == 'TestOperation(arg1: xsd:string, arg2: xsd:string)' assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string' assert operation.output.body.signature(schema=root.types) == 'TestOperation(Body: xsd:string)' assert operation.output.signature(as_output=True) == 'xsd:string' serialized = operation.input.serialize(arg1='ah1', arg2='ah2') assert serialized.headers == {'Content-Type': 'text/xml'} assert serialized.path == 'test-operation' assert_nodes_equal( load_xml(serialized.content), load_xml("ah1ah2")) def test_mime_content_no_parts(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.signature() == '' serialized = operation.input.serialize() assert serialized.content == '' def test_mime_xml_deserialize(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.signature() == 'arg1: xsd:string, arg2: xsd:string' assert operation.output.signature(as_output=True) == ( 'item_1: xsd:string, item_2: xsd:string') node = """ foo bar """.strip() serialized = operation.output.deserialize(node) assert serialized.item_1 == 'foo' assert serialized.item_2 == 'bar' def test_mime_multipart_parse(): load_xml(""" """) zeep-2.5.0/tests/test_wsdl_messages_rpc.py0000644000076500000240000004046613224161177022744 0ustar mvantellingenstaff00000000000000import io from six import StringIO from tests.utils import DummyTransport, assert_nodes_equal, load_xml from zeep.wsdl import wsdl def test_serialize(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') assert operation.input.signature() == 'arg1: xsd:string, arg2: xsd:string' serialized = operation.input.serialize(arg1='ah1', arg2='ah2') expected = """ ah1 ah2 """ assert_nodes_equal(expected, serialized.content) def test_serialize_empty_input(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') serialized = operation.input.serialize() expected = """ """ assert_nodes_equal(expected, serialized.content) def test_deserialize(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('TestOperation') document = load_xml(""" ah1 """) assert operation.output.signature(True) == 'result: xsd:string' result = operation.output.deserialize(document) assert result == 'ah1' def test_wsdl_array_of_simple_types(): wsdl_content = StringIO(""" """.strip()) # noqa transport = DummyTransport() transport.bind( 'http://schemas.xmlsoap.org/soap/encoding/', load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8'))) root = wsdl.Document(wsdl_content, transport) binding = root.bindings['{http://tests.python-zeep.org/tns}SimpleTypeArrayBinding'] operation = binding.get('getSimpleArray') document = load_xml(""" item and even more items """) deserialized = operation.output.deserialize(document) assert deserialized == ['item', 'and', 'even', 'more', 'items'] def test_handle_incorrectly_qualified(): # Based on #176 wsdl_content = StringIO(""" """.strip()) transport = DummyTransport() root = wsdl.Document(wsdl_content, transport) binding = root.bindings['{http://tests.python-zeep.org/tns}TestSoapBinding'] operation = binding.get('getItem') document = load_xml(""" foobar """) deserialized = operation.output.deserialize(document) assert deserialized == 'foobar' def test_deserialize_rpc_literal(): # Based on #219 wsdl_content = StringIO(""" """.strip()) transport = DummyTransport() root = wsdl.Document(wsdl_content, transport) binding = root.bindings['{http://tests.python-zeep.org/tns}TestSoapBinding'] operation = binding.get('getItem') document = load_xml(""" foobar """) deserialized = operation.output.deserialize(document) assert deserialized == 'foobar' def test_deserialize_x(): wsdl_content = StringIO(""" """.strip()) root = wsdl.Document(wsdl_content, None) binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] operation = binding.get('clearFoo') document = load_xml(""" """) result = operation.output.deserialize(document) assert result is None zeep-2.5.0/tests/test_wsdl_soap.py0000644000076500000240000003130013224161177021216 0ustar mvantellingenstaff00000000000000# -*- coding: utf-8 -*- import platform import pytest from lxml import etree from pretend import stub from tests.utils import load_xml from zeep import Client from zeep.exceptions import Fault, TransportError from zeep.wsdl import bindings def test_soap11_no_output(): client = Client('tests/wsdl_files/soap.wsdl') content = """ """.strip() response = stub( status_code=200, headers={}, content=content) operation = client.service._binding._operations['GetLastTradePriceNoOutput'] res = client.service._binding.process_reply(client, operation, response) assert res is None def test_soap11_process_error(): response = load_xml(""" fault-code fault-string detail-message detail-code """) binding = bindings.Soap11Binding( wsdl=None, name=None, port_name=None, transport=None, default_style=None) try: binding.process_error(response, None) assert False except Fault as exc: assert exc.message == 'fault-string' assert exc.code == 'fault-code' assert exc.actor is None assert exc.subcodes is None assert 'detail-message' in etree.tostring(exc.detail).decode('utf-8') def test_soap12_process_error(): response = """ fault-code %s us-error nl-error Invalid credit card details 999 """ subcode = """ %s %s """ binding = bindings.Soap12Binding( wsdl=None, name=None, port_name=None, transport=None, default_style=None) try: binding.process_error(load_xml(response % ""), None) assert False except Fault as exc: assert exc.message == 'us-error' assert exc.code == 'fault-code' assert exc.subcodes == [] try: binding.process_error( load_xml(response % subcode % ("fault-subcode1", "")), None) assert False except Fault as exc: assert exc.message == 'us-error' assert exc.code == 'fault-code' assert len(exc.subcodes) == 1 assert exc.subcodes[0].namespace == 'http://example.com/example1' assert exc.subcodes[0].localname == 'fault-subcode1' try: binding.process_error( load_xml(response % subcode % ("fault-subcode1", subcode % ("ex:fault-subcode2", ""))), None) assert False except Fault as exc: assert exc.message == 'us-error' assert exc.code == 'fault-code' assert len(exc.subcodes) == 2 assert exc.subcodes[0].namespace == 'http://example.com/example1' assert exc.subcodes[0].localname == 'fault-subcode1' assert exc.subcodes[1].namespace == 'http://example.com/example2' assert exc.subcodes[1].localname == 'fault-subcode2' def test_no_content_type(): data = """ 120.123 """.strip() client = Client('tests/wsdl_files/soap.wsdl') binding = client.service._binding response = stub( status_code=200, content=data, encoding='utf-8', headers={} ) result = binding.process_reply( client, binding.get('GetLastTradePrice'), response) assert result == 120.123 @pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason="Fails on PyPy") def test_wrong_content(): data = """ The request is answered something unexpected, like an html page or a raw internal stack trace """.strip() client = Client('tests/wsdl_files/soap.wsdl') binding = client.service._binding response = stub( status_code=200, content=data, encoding='utf-8', headers={} ) with pytest.raises(TransportError) as exc: binding.process_reply( client, binding.get('GetLastTradePrice'), response) assert 200 == exc.value.status_code assert data == exc.value.content @pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason="Fails on PyPy") def test_wrong_no_unicode_content(): data = """ The request is answered something unexpected, and the content charset is beyond unicode òñÇÿ """.strip() client = Client('tests/wsdl_files/soap.wsdl') binding = client.service._binding response = stub( status_code=200, content=data, encoding='utf-8', headers={} ) with pytest.raises(TransportError) as exc: binding.process_reply( client, binding.get('GetLastTradePrice'), response) assert 200 == exc.value.status_code assert data == exc.value.content @pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason="Fails on PyPy") def test_http_error(): data = """ Unauthorized! """.strip() client = Client('tests/wsdl_files/soap.wsdl') binding = client.service._binding response = stub( status_code=401, content=data, encoding='utf-8', headers={} ) with pytest.raises(TransportError) as exc: binding.process_reply( client, binding.get('GetLastTradePrice'), response) assert 401 == exc.value.status_code assert data == exc.value.content def test_mime_multipart(): data = '\r\n'.join(line.strip() for line in """ --MIME_boundary Content-Type: text/xml; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-ID: --MIME_boundary Content-Type: image/tiff Content-Transfer-Encoding: base64 Content-ID: Li4uQmFzZTY0IGVuY29kZWQgVElGRiBpbWFnZS4uLg== --MIME_boundary Content-Type: image/jpeg Content-Transfer-Encoding: binary Content-ID: ...Raw JPEG image.. --MIME_boundary-- """.splitlines()).encode('utf-8') client = Client('tests/wsdl_files/claim.wsdl') binding = client.service._binding response = stub( status_code=200, content=data, encoding='utf-8', headers={ 'Content-Type': 'multipart/related; type="text/xml"; start=""; boundary="MIME_boundary"' } ) result = binding.process_reply( client, binding.get('GetClaimDetails'), response) assert result.root is None assert len(result.attachments) == 2 assert result.attachments[0].content == b'...Base64 encoded TIFF image...' assert result.attachments[1].content == b'...Raw JPEG image..' def test_mime_multipart_no_encoding(): data = '\r\n'.join(line.strip() for line in """ --MIME_boundary Content-Type: text/xml Content-Transfer-Encoding: 8bit Content-ID: --MIME_boundary Content-Type: image/tiff Content-Transfer-Encoding: base64 Content-ID: Li4uQmFzZTY0IGVuY29kZWQgVElGRiBpbWFnZS4uLg== --MIME_boundary Content-Type: text/xml Content-ID: ...Raw JPEG image.. --MIME_boundary-- """.splitlines()).encode('utf-8') client = Client('tests/wsdl_files/claim.wsdl') binding = client.service._binding response = stub( status_code=200, content=data, encoding=None, headers={ 'Content-Type': 'multipart/related; type="text/xml"; start=""; boundary="MIME_boundary"' } ) result = binding.process_reply( client, binding.get('GetClaimDetails'), response) assert result.root is None assert len(result.attachments) == 2 assert result.attachments[0].content == b'...Base64 encoded TIFF image...' assert result.attachments[1].content == b'...Raw JPEG image..' def test_unexpected_headers(): data = """ uhoh 120.123 """.strip() client = Client('tests/wsdl_files/soap_header.wsdl') binding = client.service._binding response = stub( status_code=200, content=data, encoding='utf-8', headers={} ) result = binding.process_reply( client, binding.get('GetLastTradePrice'), response) assert result.body.price == 120.123 assert result.header.body is None assert len(result.header._raw_elements) == 1 def test_response_201(): client = Client('tests/wsdl_files/soap_header.wsdl') binding = client.service._binding response = stub( status_code=201, content='', encoding='utf-8', headers={} ) result = binding.process_reply( client, binding.get('GetLastTradePrice'), response) assert result is None def test_response_202(): client = Client('tests/wsdl_files/soap_header.wsdl') binding = client.service._binding response = stub( status_code=202, content='', encoding='utf-8', headers={} ) result = binding.process_reply( client, binding.get('GetLastTradePrice'), response) assert result is None zeep-2.5.0/tests/test_wsse_signature.py0000644000076500000240000000722513224161177022276 0ustar mvantellingenstaff00000000000000import os import sys import pytest from tests.utils import load_xml from zeep import wsse from zeep.exceptions import SignatureVerificationFailed from zeep.wsse import signature DS_NS = 'http://www.w3.org/2000/09/xmldsig#' KEY_FILE = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'cert_valid.pem') KEY_FILE_PW = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'cert_valid_pw.pem') @pytest.mark.skipif(sys.platform == 'win32', reason="does not run on windows") def test_sign(): envelope = load_xml(""" OK """) signature.sign_envelope(envelope, KEY_FILE, KEY_FILE) signature.verify_envelope(envelope, KEY_FILE) @pytest.mark.skipif(sys.platform == 'win32', reason="does not run on windows") def test_sign_pw(): envelope = load_xml(""" OK """) signature.sign_envelope(envelope, KEY_FILE_PW, KEY_FILE_PW, 'geheim') signature.verify_envelope(envelope, KEY_FILE_PW) @pytest.mark.skipif(sys.platform == 'win32', reason="does not run on windows") def test_verify_error(): envelope = load_xml(""" OK """) signature.sign_envelope(envelope, KEY_FILE, KEY_FILE) nsmap = {'tns': 'http://tests.python-zeep.org/'} for elm in envelope.xpath('//tns:Argument', namespaces=nsmap): elm.text = 'NOT!' with pytest.raises(SignatureVerificationFailed): signature.verify_envelope(envelope, KEY_FILE) @pytest.mark.skipif(sys.platform == 'win32', reason="does not run on windows") def test_signature(): envelope = load_xml(""" OK """) plugin = wsse.Signature(KEY_FILE_PW, KEY_FILE_PW, 'geheim') envelope, headers = plugin.apply(envelope, {}) plugin.verify(envelope) zeep-2.5.0/tests/test_wsse_username.py0000644000076500000240000002312113224161177022105 0ustar mvantellingenstaff00000000000000import datetime import os import pytest import requests_mock from freezegun import freeze_time from tests.utils import assert_nodes_equal, load_xml from zeep import client from zeep.wsse import UsernameToken @pytest.mark.requests def test_integration(): client_obj = client.Client( 'tests/wsdl_files/soap.wsdl', wsse=UsernameToken('username', 'password')) response = """ 120.123 """.strip() with requests_mock.mock() as m: m.post('http://example.com/stockquote', text=response) result = client_obj.service.GetLastTradePrice('foobar') assert result == 120.123 def test_password_text(): envelope = load_xml(""" foobar """) token = UsernameToken('michael', 'geheim') envelope, headers = token.apply(envelope, {}) expected = """ michael geheim foobar """ # noqa assert_nodes_equal(envelope, expected) @freeze_time('2016-05-08 12:00:00') def test_password_digest(monkeypatch): monkeypatch.setattr(os, 'urandom', lambda x: b'mocked-random') envelope = load_xml(""" foobar """) token = UsernameToken('michael', 'geheim', use_digest=True) envelope, headers = token.apply(envelope, {}) expected = """ michael hVicspAQSg70JNhe67OHqD9gexc= bW9ja2VkLXJhbmRvbQ== 2016-05-08T12:00:00+00:00 foobar """ # noqa assert_nodes_equal(envelope, expected) @freeze_time('2016-05-08 12:00:00') def test_password_digest_custom(monkeypatch): monkeypatch.setattr(os, 'urandom', lambda x: b'mocked-random') envelope = load_xml(""" foobar """) created = datetime.datetime(2016, 6, 4, 20, 10) token = UsernameToken( 'michael', password_digest='12345', use_digest=True, nonce='iets', created=created) envelope, headers = token.apply(envelope, {}) expected = """ michael 12345 aWV0cw== 2016-06-04T20:10:00+00:00 foobar """ # noqa assert_nodes_equal(envelope, expected) def test_password_prepared(): envelope = load_xml(""" foobar """) # noqa token = UsernameToken('michael', 'geheim') envelope, headers = token.apply(envelope, {}) expected = """ michael geheim foobar """ # noqa assert_nodes_equal(envelope, expected) zeep-2.5.0/tests/test_wsse_utils.py0000644000076500000240000000156613224161177021437 0ustar mvantellingenstaff00000000000000from lxml import etree from zeep.wsse import utils def test_get_security_header(): doc = etree.fromstring(""" foobar """.strip()) element = utils.get_security_header(doc) assert element.tag == '{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security' # noqa zeep-2.5.0/tests/test_xsd.py0000644000076500000240000004415013224161177020030 0ustar mvantellingenstaff00000000000000import pytest from lxml import etree from tests.utils import assert_nodes_equal, render_node from zeep import xsd def test_container_elements(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'username'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'password'), xsd.String()), xsd.Any(), ]) )) # sequences custom_type(username='foo', password='bar') def test_create_node(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'username'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'password'), xsd.String()), ]), [ xsd.Attribute('attr', xsd.String()), ] )) # sequences obj = custom_type(username='foo', password='bar', attr='x') expected = """ foo bar """ node = etree.Element('document') custom_type.render(node, obj) assert_nodes_equal(expected, node) def test_element_simple_type(): elm = xsd.Element( '{http://tests.python-zeep.org/}item', xsd.String()) obj = elm('foo') expected = """ foo """ node = etree.Element('document') elm.render(node, obj) assert_nodes_equal(expected, node) def test_complex_type(): custom_type = xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'username'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'password'), xsd.String()), ]) ) obj = custom_type('user', 'pass') assert {key: obj[key] for key in obj} == { 'username': 'user', 'password': 'pass' } def test_nil_elements(): custom_type = xsd.Element( '{http://tests.python-zeep.org/}container', xsd.ComplexType( xsd.Sequence([ xsd.Element( '{http://tests.python-zeep.org/}item_1', xsd.ComplexType( xsd.Sequence([ xsd.Element( '{http://tests.python-zeep.org/}item_1_1', xsd.String()) ]), ), nillable=True), xsd.Element( '{http://tests.python-zeep.org/}item_2', xsd.DateTime(), nillable=True), xsd.Element( '{http://tests.python-zeep.org/}item_3', xsd.String(), min_occurs=0, nillable=False), xsd.Element( '{http://tests.python-zeep.org/}item_4', xsd.ComplexType( xsd.Sequence([ xsd.Element( '{http://tests.python-zeep.org/}item_4_1', xsd.String(), nillable=True) ]) ) ), ]) )) obj = custom_type(item_1=None, item_2=None, item_3=None, item_4={}) expected = """ """ node = render_node(custom_type, obj) etree.cleanup_namespaces(node) assert_nodes_equal(expected, node) def test_invalid_kwarg(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'username'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'password'), xsd.String()), ]) )) with pytest.raises(TypeError): custom_type(something='is-wrong') def test_invalid_kwarg_simple_type(): elm = xsd.Element( '{http://tests.python-zeep.org/}item', xsd.String()) with pytest.raises(TypeError): elm(something='is-wrong') def test_any(): some_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'doei'), xsd.String()) complex_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'complex'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]) )) custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'hoi'), xsd.ComplexType( xsd.Sequence([ xsd.Any(), xsd.Any(), xsd.Any(), ]) )) any_1 = xsd.AnyObject(some_type, "DOEI!") any_2 = xsd.AnyObject( complex_type, complex_type(item_1='val_1', item_2='val_2')) any_3 = xsd.AnyObject( complex_type, [ complex_type(item_1='val_1_1', item_2='val_1_2'), complex_type(item_1='val_2_1', item_2='val_2_2'), ]) obj = custom_type(_value_1=any_1, _value_2=any_2, _value_3=any_3) expected = """ DOEI! val_1 val_2 val_1_1 val_1_2 val_2_1 val_2_2 """ node = etree.Element('document') custom_type.render(node, obj) assert_nodes_equal(expected, node) def test_any_type_check(): some_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'doei'), xsd.String()) custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'complex'), xsd.ComplexType( xsd.Sequence([ xsd.Any(), ]) )) with pytest.raises(TypeError): custom_type(_any_1=some_type) def test_choice_init(): root = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'kies'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'pre'), xsd.String()), xsd.Choice([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_3'), xsd.String()), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_4_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_4_2'), xsd.String()), ]) ], max_occurs=4), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'post'), xsd.String()), ]) ) ) obj = root( pre='foo', _value_1=[ {'item_1': 'value-1'}, {'item_2': 'value-2'}, {'item_1': 'value-3'}, {'item_4_1': 'value-4-1', 'item_4_2': 'value-4-2'}, ], post='bar') assert obj._value_1 == [ {'item_1': 'value-1'}, {'item_2': 'value-2'}, {'item_1': 'value-3'}, {'item_4_1': 'value-4-1', 'item_4_2': 'value-4-2'}, ] node = etree.Element('document') root.render(node, obj) assert etree.tostring(node) expected = """ foo value-1 value-2 value-3 value-4-1 value-4-2 bar """.strip() assert_nodes_equal(expected, node) def test_choice_determinst(): root = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'kies'), xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), ]), ]) ]) ) ) obj = root(item_1='item-1', item_2='item-2') node = etree.Element('document') root.render(node, obj) assert etree.tostring(node) expected = """ item-1 item-2 """.strip() assert_nodes_equal(expected, node) def test_sequence(): root = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'container'), xsd.ComplexType( xsd.Sequence([ xsd.Sequence([ xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ], min_occurs=2, max_occurs=2), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_3'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_4'), xsd.String()), ]), ]) ]) ) ) root( _value_1=[ { 'item_1': 'foo', 'item_2': 'bar', }, { 'item_1': 'foo', 'item_2': 'bar', }, ], item_3='foo', item_4='bar', ) def test_mixed_choice(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_3'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_4'), xsd.String()), ]), xsd.Choice([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_5'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_6'), xsd.String()), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_7'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_8'), xsd.String()), ]) ]) ]) )) item = custom_type( item_1='item-1', item_2='item-2', item_3='item-3', item_4='item-4', item_7='item-7', item_8='item-8', ) assert item.item_1 == 'item-1' assert item.item_2 == 'item-2' assert item.item_3 == 'item-3' assert item.item_4 == 'item-4' assert item.item_7 == 'item-7' assert item.item_8 == 'item-8' def test_xsi(): org_type = xsd.Element( '{https://tests.python-zeep.org/}original', xsd.ComplexType( xsd.Sequence([ xsd.Element('username', xsd.String()), xsd.Element('password', xsd.String()), ]) ) ) alt_type = xsd.Element( '{https://tests.python-zeep.org/}alternative', xsd.ComplexType( xsd.Sequence([ xsd.Element('username', xsd.String()), xsd.Element('password', xsd.String()), ]) ) ) instance = alt_type(username='mvantellingen', password='geheim') render_node(org_type, instance) def test_duplicate_element_names(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'container'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item'), xsd.String()), ]) )) # sequences expected = '{http://tests.python-zeep.org/}container(item: xsd:string, item__1: xsd:string, item__2: xsd:string)' assert custom_type.signature() == expected obj = custom_type(item='foo', item__1='bar', item__2='lala') expected = """ foo bar lala """ node = render_node(custom_type, obj) assert_nodes_equal(expected, node) def test_element_attribute_name_conflict(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'container'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item'), xsd.String()), ]), [ xsd.Attribute('foo', xsd.String()), xsd.Attribute('item', xsd.String()), ] )) # sequences expected = '{http://tests.python-zeep.org/}container(item: xsd:string, foo: xsd:string, attr__item: xsd:string)' assert custom_type.signature() == expected obj = custom_type(item='foo', foo='x', attr__item='bar') expected = """ foo """ node = render_node(custom_type, obj) assert_nodes_equal(expected, node) obj = custom_type.parse(node.getchildren()[0], None) assert obj.item == 'foo' assert obj.foo == 'x' assert obj.attr__item == 'bar' def test_attr_name(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'UserName'), xsd.String(), attr_name='username'), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'Password_x'), xsd.String(), attr_name='password'), ]) )) # sequences custom_type(username='foo', password='bar') zeep-2.5.0/tests/test_xsd_any.py0000644000076500000240000003307113224161177020677 0ustar mvantellingenstaff00000000000000import datetime import pytest from lxml import etree from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import xsd def get_any_schema(): return xsd.Schema(load_xml(""" """)) def test_default_xsd_type(): schema = xsd.Schema(load_xml(""" """)) assert schema container_cls = schema.get_element('ns0:container') data = container_cls() assert data == '' def test_any_simple(): schema = get_any_schema() item_elm = schema.get_element('{http://tests.python-zeep.org/}item') assert isinstance(item_elm.type, xsd.String) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') # Create via arg obj = container_elm(xsd.AnyObject(item_elm, item_elm('argh'))) node = etree.Element('document') container_elm.render(node, obj) expected = """ argh """ assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert item._value_1 == 'argh' # Create via kwarg _value_1 obj = container_elm(_value_1=xsd.AnyObject(item_elm, item_elm('argh'))) node = etree.Element('document') container_elm.render(node, obj) expected = """ argh """ assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert item._value_1 == 'argh' def test_any_value_element_tree(): schema = get_any_schema() item = etree.Element('{http://tests.python-zeep.org}lxml') etree.SubElement(item, 'node').text = 'foo' container_elm = schema.get_element('{http://tests.python-zeep.org/}container') # Create via arg obj = container_elm(item) node = etree.Element('document') container_elm.render(node, obj) expected = """ foo """ assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert isinstance(item._value_1, etree._Element) assert item._value_1.tag == '{http://tests.python-zeep.org}lxml' def test_any_value_invalid(): schema = get_any_schema() class SomeThing(object): pass container_elm = schema.get_element('{http://tests.python-zeep.org/}container') # Create via arg item = SomeThing() obj = container_elm(item) node = etree.Element('document') with pytest.raises(TypeError): container_elm.render(node, obj) def test_any_without_element(): schema = xsd.Schema(load_xml(""" """)) item_elm = schema.get_element('{http://tests.python-zeep.org/}item') item = item_elm(xsd.AnyObject(xsd.String(), 'foobar'), type='attr-1', title='attr-2') node = render_node(item_elm, item) expected = """ foobar """ assert_nodes_equal(expected, node) item = item_elm.parse(node.getchildren()[0], schema) assert item.type == 'attr-1' assert item.title == 'attr-2' assert item._value_1 is None def test_any_with_ref(): schema = xsd.Schema(load_xml(""" """)) item_elm = schema.get_element('{http://tests.python-zeep.org/}item') assert isinstance(item_elm.type, xsd.String) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') obj = container_elm( item='bar', _value_1=xsd.AnyObject(item_elm, item_elm('argh')), _value_2=xsd.AnyObject(item_elm, item_elm('ok'))) node = etree.Element('document') container_elm.render(node, obj) expected = """ bar argh ok """ assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert item.item == 'bar' assert item._value_1 == 'argh' def test_element_any_parse(): node = load_xml(""" """) schema = xsd.Schema(node) node = load_xml(""" text """) elm = schema.get_element('ns0:container') elm.parse(node, schema) def test_element_any_type(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') obj = container_elm(something=datetime.time(18, 29, 59)) node = etree.Element('document') container_elm.render(node, obj) expected = """ 18:29:59 """ assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert item.something == '18:29:59' def test_element_any_type_unknown_type(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') node = load_xml(""" bar """) item = container_elm.parse(node.getchildren()[0], schema) assert item.something == 'bar' def test_element_any_type_elements(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) Child = xsd.ComplexType( xsd.Sequence([ xsd.Element('{http://tests.python-zeep.org/}item_1', xsd.String()), xsd.Element('{http://tests.python-zeep.org/}item_2', xsd.String()), ]) ) child = Child(item_1='item-1', item_2='item-2') container_elm = schema.get_element('{http://tests.python-zeep.org/}container') obj = container_elm(something=child) node = etree.Element('document') container_elm.render(node, obj) expected = """ item-1 item-2 """ assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert len(item.something) == 2 assert item.something[0].text == 'item-1' assert item.something[1].text == 'item-2' def test_any_in_nested_sequence(): schema = xsd.Schema(load_xml(""" """)) # noqa container_elm = schema.get_element('{http://tests.python-zeep.org/}container') assert container_elm.signature(schema) == ( 'ns0:container(items: {_value_1: ANY}, version: xsd:string, _value_1: ANY[])') something = schema.get_element('{http://tests.python-zeep.org/}something') foobar = schema.get_element('{http://tests.python-zeep.org/}foobar') any_1 = xsd.AnyObject(something, datetime.date(2016, 7, 4)) any_2 = xsd.AnyObject(foobar, True) obj = container_elm( items={'_value_1': any_1}, version='str1234', _value_1=[any_1, any_2]) node = etree.Element('document') container_elm.render(node, obj) expected = """ 2016-07-04 str1234 2016-07-04 true """ assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert item.items._value_1 == datetime.date(2016, 7, 4) assert item.version == 'str1234' assert item._value_1 == [datetime.date(2016, 7, 4), True] zeep-2.5.0/tests/test_xsd_attributes.py0000644000076500000240000003673513224161177022310 0ustar mvantellingenstaff00000000000000from collections import OrderedDict from lxml import etree from tests.utils import assert_nodes_equal, load_xml from zeep import xsd def test_anyattribute(): schema = xsd.Schema(load_xml(""" """)) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') assert container_elm.signature(schema) == ( 'ns0:container(foo: xsd:string, _attr_1: {})') obj = container_elm(foo='bar', _attr_1=OrderedDict([ ('hiep', 'hoi'), ('hoi', 'hiep') ])) expected = """ bar """ node = etree.Element('document') container_elm.render(node, obj) assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert item._attr_1 == {'hiep': 'hoi', 'hoi': 'hiep'} assert item.foo == 'bar' def test_attribute_list_type(): schema = xsd.Schema(load_xml(""" """)) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') assert container_elm.signature(schema) == ( 'ns0:container(foo: xsd:string, lijst: xsd:int[])') obj = container_elm(foo='bar', lijst=[1, 2, 3]) expected = """ bar """ node = etree.Element('document') container_elm.render(node, obj) assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert item.lijst == [1, 2, 3] assert item.foo == 'bar' def test_ref_attribute_qualified(): schema = xsd.Schema(load_xml(""" """)) elm_cls = schema.get_element('{http://tests.python-zeep.org/}container') instance = elm_cls(attr="hoi") expected = """ """ node = etree.Element('document') elm_cls.render(node, instance) assert_nodes_equal(expected, node) def test_ref_attribute_unqualified(): schema = xsd.Schema(load_xml(""" """)) elm_cls = schema.get_element('{http://tests.python-zeep.org/}container') instance = elm_cls(attr="hoi") expected = """ """ node = etree.Element('document') elm_cls.render(node, instance) assert_nodes_equal(expected, node) def test_complex_type_with_attributes(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('Address') obj = address_type( NameFirst='John', NameLast='Doe', Email='j.doe@example.com', id='123') node = etree.Element('document') address_type.render(node, obj) expected = """
John Doe j.doe@example.com
""" assert_nodes_equal(expected, node) def test_qualified_attribute(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type(foo='bar', id="20", pos="30") expected = """ bar """ node = etree.Element('document') address_type.render(node, obj) assert_nodes_equal(expected, node) def test_group(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type(foo='bar', id="20", pos="30") expected = """ bar """ node = etree.Element('document') address_type.render(node, obj) assert_nodes_equal(expected, node) def test_group_nested(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type(foo='bar', id="20", pos="30", size="maat") expected = """ bar """ node = etree.Element('document') address_type.render(node, obj) assert_nodes_equal(expected, node) def test_nested_attribute(): schema = xsd.Schema(load_xml(""" """)) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') assert container_elm.signature(schema) == ( 'ns0:container(item: {x: xsd:string, y: xsd:string})') obj = container_elm(item={'x': 'foo', 'y': 'bar'}) expected = """ foo """ node = etree.Element('document') container_elm.render(node, obj) assert_nodes_equal(expected, node) def test_attribute_union_type(): schema = xsd.Schema(load_xml(""" """)) attr = schema.get_attribute('{http://tests.python-zeep.org/}something') assert attr('foo') == 'foo' def test_attribute_union_type_inline(): schema = xsd.Schema(load_xml(""" """)) attr = schema.get_attribute('{http://tests.python-zeep.org/}something') assert attr('foo') == 'foo' def test_attribute_value_retrieval(): schema = xsd.Schema(load_xml(""" """)) Addr = schema.get_type('{http://tests.python-zeep.org/}Address') address = Addr() address.Street = { 'ID': 100, 'Name': 'Foo', } expected = """ Foo """ node = etree.Element('document') Addr.render(node, address) assert_nodes_equal(expected, node) zeep-2.5.0/tests/test_xsd_builtins.py0000644000076500000240000003030713224161177021740 0ustar mvantellingenstaff00000000000000import datetime from decimal import Decimal as D import isodate import pytest import pytz import six from zeep.xsd.types import builtins class TestString: def test_xmlvalue(self): instance = builtins.String() result = instance.xmlvalue('foobar') assert result == 'foobar' def test_pythonvalue(self): instance = builtins.String() result = instance.pythonvalue('foobar') assert result == 'foobar' class TestBoolean: def test_xmlvalue(self): instance = builtins.Boolean() assert instance.xmlvalue(True) == 'true' assert instance.xmlvalue(False) == 'false' assert instance.xmlvalue(1) == 'true' assert instance.xmlvalue(0) == 'false' assert instance.xmlvalue('false') == 'false' assert instance.xmlvalue('0') == 'false' def test_pythonvalue(self): instance = builtins.Boolean() assert instance.pythonvalue('1') is True assert instance.pythonvalue('true') is True assert instance.pythonvalue('0') is False assert instance.pythonvalue('false') is False class TestDecimal: def test_xmlvalue(self): instance = builtins.Decimal() assert instance.xmlvalue(D('10.00')) == '10.00' assert instance.xmlvalue(D('10.000002')) == '10.000002' assert instance.xmlvalue(D('10.000002')) == '10.000002' assert instance.xmlvalue(D('10')) == '10' assert instance.xmlvalue(D('-10')) == '-10' def test_pythonvalue(self): instance = builtins.Decimal() assert instance.pythonvalue('10') == D('10') assert instance.pythonvalue('10.001') == D('10.001') assert instance.pythonvalue('+10.001') == D('10.001') assert instance.pythonvalue('-10.001') == D('-10.001') class TestFloat: def test_xmlvalue(self): instance = builtins.Float() assert instance.xmlvalue(float(10)) == '10.0' assert instance.xmlvalue(float(3.9999)) == '3.9999' assert instance.xmlvalue(float('inf')) == 'INF' assert instance.xmlvalue(float(12.78e-2)) == '0.1278' if six.PY2: assert instance.xmlvalue(float('1267.43233E12')) == '1.26743233E+15' else: assert instance.xmlvalue(float('1267.43233E12')) == '1267432330000000.0' def test_pythonvalue(self): instance = builtins.Float() assert instance.pythonvalue('10') == float('10') assert instance.pythonvalue('-1E4') == float('-1E4') assert instance.pythonvalue('1267.43233E12') == float('1267.43233E12') assert instance.pythonvalue('12.78e-2') == float('0.1278') assert instance.pythonvalue('12') == float(12) assert instance.pythonvalue('-0') == float(0) assert instance.pythonvalue('0') == float(0) assert instance.pythonvalue('INF') == float('inf') class TestDouble: def test_xmlvalue(self): instance = builtins.Double() assert instance.xmlvalue(float(10)) == '10.0' assert instance.xmlvalue(float(3.9999)) == '3.9999' assert instance.xmlvalue(float(12.78e-2)) == '0.1278' def test_pythonvalue(self): instance = builtins.Double() assert instance.pythonvalue('10') == float('10') assert instance.pythonvalue('12') == float(12) assert instance.pythonvalue('-0') == float(0) assert instance.pythonvalue('0') == float(0) class TestDuration: def test_xmlvalue(self): instance = builtins.Duration() value = isodate.parse_duration('P0Y1347M0D') assert instance.xmlvalue(value) == 'P1347M' def test_pythonvalue(self): instance = builtins.Duration() expected = isodate.parse_duration('P0Y1347M0D') value = 'P0Y1347M0D' assert instance.pythonvalue(value) == expected class TestDateTime: def test_xmlvalue(self): instance = builtins.DateTime() value = datetime.datetime(2016, 3, 4, 21, 14, 42) assert instance.xmlvalue(value) == '2016-03-04T21:14:42' value = datetime.datetime(2016, 3, 4, 21, 14, 42, tzinfo=pytz.utc) assert instance.xmlvalue(value) == '2016-03-04T21:14:42Z' value = datetime.datetime(2016, 3, 4, 21, 14, 42, 123456, tzinfo=pytz.utc) assert instance.xmlvalue(value) == '2016-03-04T21:14:42.123456Z' value = datetime.datetime(2016, 3, 4, 21, 14, 42, tzinfo=pytz.utc) value = value.astimezone(pytz.timezone('Europe/Amsterdam')) assert instance.xmlvalue(value) == '2016-03-04T22:14:42+01:00' def test_pythonvalue(self): instance = builtins.DateTime() value = datetime.datetime(2016, 3, 4, 21, 14, 42) assert instance.pythonvalue('2016-03-04T21:14:42') == value value = datetime.datetime(2016, 3, 4, 21, 14, 42, 123456) assert instance.pythonvalue('2016-03-04T21:14:42.123456') == value def test_pythonvalue_invalid(self): instance = builtins.DateTime() with pytest.raises(ValueError): assert instance.pythonvalue(' : : ') class TestTime: def test_xmlvalue(self): instance = builtins.Time() value = datetime.time(21, 14, 42) assert instance.xmlvalue(value) == '21:14:42' assert instance.xmlvalue("21:14:42") == '21:14:42' def test_pythonvalue(self): instance = builtins.Time() value = datetime.time(21, 14, 42) assert instance.pythonvalue('21:14:42') == value value = datetime.time(21, 14, 42, 120000) assert instance.pythonvalue('21:14:42.120') == value value = isodate.parse_time('21:14:42.120+0200') assert instance.pythonvalue('21:14:42.120+0200') == value def test_pythonvalue_invalid(self): instance = builtins.Time() with pytest.raises(ValueError): assert instance.pythonvalue(':') class TestDate: def test_xmlvalue(self): instance = builtins.Date() value = datetime.datetime(2016, 3, 4) assert instance.xmlvalue(value) == '2016-03-04' assert instance.xmlvalue('2016-03-04') == '2016-03-04' assert instance.xmlvalue('2016-04') == '2016-04' def test_pythonvalue(self): instance = builtins.Date() assert instance.pythonvalue('2016-03-04') == datetime.date(2016, 3, 4) assert instance.pythonvalue('2001-10-26+02:00') == datetime.date(2001, 10, 26) assert instance.pythonvalue('2001-10-26Z') == datetime.date(2001, 10, 26) assert instance.pythonvalue('2001-10-26+00:00') == datetime.date(2001, 10, 26) def test_pythonvalue_invalid(self): instance = builtins.Date() # negative dates are not supported for datetime.date objects so lets # hope no-one uses it for now.. with pytest.raises(ValueError): assert instance.pythonvalue('-2001-10-26') with pytest.raises(ValueError): assert instance.pythonvalue('-20000-04-01') class TestgYearMonth: def test_xmlvalue(self): instance = builtins.gYearMonth() assert instance.xmlvalue((2012, 10, None)) == '2012-10' assert instance.xmlvalue((2012, 10, pytz.utc)) == '2012-10Z' def test_pythonvalue(self): instance = builtins.gYearMonth() assert instance.pythonvalue('2001-10') == (2001, 10, None) assert instance.pythonvalue('2001-10+02:00') == (2001, 10, pytz.FixedOffset(120)) assert instance.pythonvalue('2001-10Z') == (2001, 10, pytz.utc) assert instance.pythonvalue('2001-10+00:00') == (2001, 10, pytz.utc) assert instance.pythonvalue('-2001-10') == (-2001, 10, None) assert instance.pythonvalue('-20001-10') == (-20001, 10, None) with pytest.raises(builtins.ParseError): assert instance.pythonvalue('10-10') class TestgYear: def test_xmlvalue(self): instance = builtins.gYear() instance.xmlvalue((2001, None)) == '2001' instance.xmlvalue((2001, pytz.utc)) == '2001Z' def test_pythonvalue(self): instance = builtins.gYear() assert instance.pythonvalue('2001') == (2001, None) assert instance.pythonvalue('2001+02:00') == (2001, pytz.FixedOffset(120)) assert instance.pythonvalue('2001Z') == (2001, pytz.utc) assert instance.pythonvalue('2001+00:00') == (2001, pytz.utc) assert instance.pythonvalue('-2001') == (-2001, None) assert instance.pythonvalue('-20000') == (-20000, None) with pytest.raises(builtins.ParseError): assert instance.pythonvalue('99') class TestgMonthDay: def test_xmlvalue(self): instance = builtins.gMonthDay() assert instance.xmlvalue((12, 30, None)) == '--12-30' def test_pythonvalue(self): instance = builtins.gMonthDay() assert instance.pythonvalue('--05-01') == (5, 1, None) assert instance.pythonvalue('--11-01Z') == (11, 1, pytz.utc) assert instance.pythonvalue('--11-01+02:00') == (11, 1, pytz.FixedOffset(120)) assert instance.pythonvalue('--11-01-04:00') == (11, 1, pytz.FixedOffset(-240)) assert instance.pythonvalue('--11-15') == (11, 15, None) assert instance.pythonvalue('--02-29') == (2, 29, None) with pytest.raises(builtins.ParseError): assert instance.pythonvalue('99') class TestgMonth: def test_xmlvalue(self): instance = builtins.gMonth() assert instance.xmlvalue((12, None)) == '--12' def test_pythonvalue(self): instance = builtins.gMonth() assert instance.pythonvalue('--05') == (5, None) assert instance.pythonvalue('--11Z') == (11, pytz.utc) assert instance.pythonvalue('--11+02:00') == (11, pytz.FixedOffset(120)) assert instance.pythonvalue('--11-04:00') == (11, pytz.FixedOffset(-240)) assert instance.pythonvalue('--11') == (11, None) assert instance.pythonvalue('--02') == (2, None) with pytest.raises(builtins.ParseError): assert instance.pythonvalue('99') class TestgDay: def test_xmlvalue(self): instance = builtins.gDay() value = (1, None) assert instance.xmlvalue(value) == '---01' value = (1, pytz.FixedOffset(120)) assert instance.xmlvalue(value) == '---01+02:00' value = (1, pytz.FixedOffset(-240)) assert instance.xmlvalue(value) == '---01-04:00' def test_pythonvalue(self): instance = builtins.gDay() assert instance.pythonvalue('---01') == (1, None) assert instance.pythonvalue('---01Z') == (1, pytz.utc) assert instance.pythonvalue('---01+02:00') == (1, pytz.FixedOffset(120)) assert instance.pythonvalue('---01-04:00') == (1, pytz.FixedOffset(-240)) assert instance.pythonvalue('---15') == (15, None) assert instance.pythonvalue('---31') == (31, None) with pytest.raises(builtins.ParseError): assert instance.pythonvalue('99') class TestHexBinary: def test_xmlvalue(self): instance = builtins.HexBinary() assert instance.xmlvalue(b'\xFF') == b'\xFF' def test_pythonvalue(self): instance = builtins.HexBinary() assert instance.pythonvalue(b'\xFF') == b'\xFF' class TestBase64Binary: def test_xmlvalue(self): instance = builtins.Base64Binary() assert instance.xmlvalue(b'hoi') == b'aG9p' def test_pythonvalue(self): instance = builtins.Base64Binary() assert instance.pythonvalue(b'aG9p') == b'hoi' class TestAnyURI: def test_xmlvalue(self): instance = builtins.AnyURI() assert instance.xmlvalue('http://test.python-zeep.org') == 'http://test.python-zeep.org' def test_pythonvalue(self): instance = builtins.AnyURI() assert instance.pythonvalue('http://test.python-zeep.org') == 'http://test.python-zeep.org' class TestInteger: def test_xmlvalue(self): instance = builtins.Integer() assert instance.xmlvalue(100) == '100' def test_pythonvalue(self): instance = builtins.Integer() assert instance.pythonvalue('100') == 100 class TestAnyType: def test_xmlvalue(self): instance = builtins.AnyType() assert instance.xmlvalue('http://test.python-zeep.org') == 'http://test.python-zeep.org' def test_pythonvalue(self): instance = builtins.AnyType() assert instance.pythonvalue('http://test.python-zeep.org') == 'http://test.python-zeep.org' zeep-2.5.0/tests/test_xsd_complex_types.py0000644000076500000240000002724413224161177023010 0ustar mvantellingenstaff00000000000000import pytest from lxml import etree from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import xsd def test_xml_xml_single_node(): schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') container_elm = schema.get_element('tns:container') obj = container_elm(item='bar') expected = """ bar """ result = render_node(container_elm, obj) assert_nodes_equal(result, expected) obj = container_elm.parse(result[0], schema) assert obj.item == 'bar' def test_xml_nested_sequence(): schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') container_elm = schema.get_element('tns:container') obj = container_elm(item={'x': 1, 'y': 2}) expected = """ 1 2 """ result = render_node(container_elm, obj) assert_nodes_equal(result, expected) obj = container_elm.parse(result[0], schema) assert obj.item.x == 1 assert obj.item.y == 2 def test_xml_restriction_self(): schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') container_elm = schema.get_element('tns:container') container_elm.signature(schema) def test_xml_single_node_array(): schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') container_elm = schema.get_element('tns:container') obj = container_elm(item=['item-1', 'item-2', 'item-3']) assert obj.item == ['item-1', 'item-2', 'item-3'] expected = """ item-1 item-2 item-3 """ result = render_node(container_elm, obj) assert_nodes_equal(result, expected) obj = container_elm.parse(result[0], schema) assert obj.item == ['item-1', 'item-2', 'item-3'] def test_xml_single_node_no_iterable(): schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') container_elm = schema.get_element('tns:container') obj = container_elm(item=['item-1', 'item-2', 'item-3']) assert obj.item == ['item-1', 'item-2', 'item-3'] with pytest.raises(ValueError): render_node(container_elm, obj) def test_xml_complex_any_types(): # see https://github.com/mvantellingen/python-zeep/issues/252 schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') KeyValueData = xsd.Element( '{http://xml.apache.org/xml-soap}KeyValueData', xsd.ComplexType( xsd.Sequence([ xsd.Element( 'key', xsd.AnyType(), ), xsd.Element( 'value', xsd.AnyType(), ), ]), ), ) Map = xsd.ComplexType( xsd.Sequence([ xsd.Element( 'item', xsd.AnyType(), min_occurs=1, max_occurs="unbounded"), ]), qname=etree.QName('{http://xml.apache.org/xml-soap}Map')) header_Username = KeyValueData(xsd.AnyObject(xsd.String(), 'Username'), value=xsd.AnyObject(xsd.String(), 'abc')) header_ShopId = KeyValueData(xsd.AnyObject(xsd.String(), 'ShopId'), value=xsd.AnyObject(xsd.Int(), 123)) auth = Map(item=[header_Username, header_ShopId]) header_LimitNum = KeyValueData(xsd.AnyObject(xsd.String(), 'LimitNum'), value=xsd.AnyObject(xsd.Int(), 2)) params = Map(item=[header_LimitNum]) container = schema.get_element('ns0:container') obj = container(auth=auth, params=params) result = render_node(container, obj) expected = load_xml(""" Username abc ShopId 123 LimitNum 2 """) # noqa assert_nodes_equal(result, expected) def test_xml_unparsed_elements(): schema = xsd.Schema(load_xml(""" """)) schema.strict = False schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') expected = load_xml(""" bar bar """) container_elm = schema.get_element('tns:container') obj = container_elm.parse(expected[0], schema) assert obj.item == 'bar' assert obj._raw_elements def test_xml_simple_content_nil(): schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') container_elm = schema.get_element('tns:container') obj = container_elm(xsd.Nil) result = render_node(container_elm, obj) expected = """ """ result = render_node(container_elm, obj) assert_nodes_equal(result, expected) obj = container_elm.parse(result[0], schema) assert obj._value_1 is None zeep-2.5.0/tests/test_xsd_extension.py0000644000076500000240000006110213224161177022120 0ustar mvantellingenstaff00000000000000import datetime import io from lxml import etree from tests.utils import ( DummyTransport, assert_nodes_equal, load_xml, render_node) from zeep import xsd def test_simple_content_extension(): schema = xsd.Schema(load_xml(""" """.strip())) shoe_type = schema.get_element('{http://tests.python-zeep.org/}ShoeSize') obj = shoe_type(20, sizing='EUR') node = render_node(shoe_type, obj) expected = """ 20 """ assert_nodes_equal(expected, node) obj = shoe_type.parse(node[0], schema) assert obj._value_1 == 20 assert obj.sizing == 'EUR' def test_complex_content_sequence_extension(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type( first_name='foo', last_name='bar', country='The Netherlands') node = etree.Element('document') address_type.render(node, obj) expected = """ foo bar The Netherlands """ assert_nodes_equal(expected, node) def test_complex_content_with_recursive_elements(): schema = xsd.Schema(load_xml(""" """)) pet_type = schema.get_element('{http://tests.python-zeep.org/}Pet') assert(pet_type.signature(schema=schema) == 'ns0:Pet(ns0:Pet)') assert(pet_type.type.signature(schema=schema) == 'ns0:Pet(name: xsd:string, common_name: xsd:string, children: ns0:Pet[])') obj = pet_type( name='foo', common_name='bar', children=[ pet_type(name='child-1', common_name='child-cname-1') ]) node = etree.Element('document') pet_type.render(node, obj) expected = """ foo bar child-1 child-cname-1 """ assert_nodes_equal(expected, node) def test_complex_content_sequence_extension_2(): schema = xsd.Schema(load_xml(""" """)) elm_cls = schema.get_element('{http://tests.python-zeep.org/}container') node = load_xml(""" item-1 item-2 item-3 """) data = elm_cls.parse(node, schema) assert data['item-1'] == 'item-1' assert data['item-2'] == 'item-2' assert data['item-3'] == 'item-3' def test_complex_type_with_extension_optional(): schema = xsd.Schema(load_xml(""" """)) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') obj = container_elm(main_1='foo') node = etree.Element('document') container_elm.render(node, obj) expected = """ foo """ assert_nodes_equal(expected, node) assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert item.main_1 == 'foo' def test_complex_with_simple(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('ns0:Address') assert address_type.type.signature() val = datetime.datetime(2016, 5, 29, 11, 13, 45) obj = address_type(val, name='foobie') expected = """ 2016-05-29T11:13:45 """ node = etree.Element('document') address_type.render(node, obj) assert_nodes_equal(expected, node) def test_sequence_with_type(): schema = xsd.Schema(load_xml(""" """)) seq = schema.get_type('ns0:polytype') sub_type = schema.get_type('ns0:subtype') value = seq(item=[sub_type(attr_1='test', name='name')]) node = etree.Element('document') seq.render(node, value) expected = """ name """ assert_nodes_equal(expected, node) def test_complex_simple_content(): schema = xsd.Schema(load_xml(""" """)) # noqa value_elm = schema.get_element('ns0:value') value = value_elm('00163e0c-0ea1-1ed6-93af-e818529bc1f1') node = etree.Element('document') value_elm.render(node, value) expected = """ 00163e0c-0ea1-1ed6-93af-e818529bc1f1 """ # noqa assert_nodes_equal(expected, node) item = value_elm.parse(node.getchildren()[0], schema) assert item._value_1 == '00163e0c-0ea1-1ed6-93af-e818529bc1f1' def test_issue_221(): transport = DummyTransport() transport.bind( 'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd', load_xml(io.open('tests/wsdl_files/xmldsig-core-schema.xsd', 'r').read().encode('utf-8'))) schema = xsd.Schema(load_xml(""" x """), transport=transport) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') elm = schema.get_element('tns:exportOrgRegistryRequest') # Args obj = elm(None, {'OGRN': '123123123123', 'isRegistered': True}) node = etree.Element('document') elm.render(node, obj) expected = """ 123123123123 true """ assert_nodes_equal(expected, node) obj = elm(SearchCriteria={'orgVersionGUID': '1234', 'isRegistered': False}) node = etree.Element('document') elm.render(node, obj) expected = """ 1234 false """ assert_nodes_equal(expected, node) obj = elm(SearchCriteria={'OGRNIP': '123123123123', 'isRegistered': True}) node = etree.Element('document') elm.render(node, obj) expected = """ 123123123123 true """ assert_nodes_equal(expected, node) def test_complex_content_extension_with_sequence(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}SpecialPackage') obj = address_type( id='testString', pkg_id='nameId', otherElement='foobar') node = etree.Element('document') address_type.render(node, obj) expected = """ foobar """ assert_nodes_equal(expected, node) def test_extension_abstract_complex_type(): schema = xsd.Schema(load_xml(""" """)) package_cls = schema.get_element('{http://tests.python-zeep.org/}SpecialPackage') obj = package_cls(item='foo') node = etree.Element('document') package_cls.render(node, obj) expected = """ foo """ assert_nodes_equal(expected, node) def test_extension_base_anytype(): schema = xsd.Schema(load_xml(""" """)) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') assert container_elm.signature() == ( '{http://tests.python-zeep.org/}container(attr: xsd:unsignedInt, _attr_1: {})') obj = container_elm(attr='foo') node = render_node(container_elm, obj) expected = """ """ assert_nodes_equal(expected, node) def test_extension_on_ref(): schema = xsd.Schema(load_xml(""" """)) type_cls = schema.get_type('ns0:type') assert type_cls.signature() def test_restrict_on_ref(): schema = xsd.Schema(load_xml(""" """)) type_cls = schema.get_type('ns0:type') assert type_cls.signature() zeep-2.5.0/tests/test_xsd_indicators_all.py0000644000076500000240000000365113224161177023100 0ustar mvantellingenstaff00000000000000from lxml import etree from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import xsd def test_build_occurs_1(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.All([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]) )) obj = custom_type(item_1='foo', item_2='bar') result = render_node(custom_type, obj) expected = load_xml(""" foo bar """) assert_nodes_equal(result, expected) obj = custom_type.parse(result[0], None) assert obj.item_1 == 'foo' assert obj.item_2 == 'bar' def test_build_pare_other_order(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.All([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]) )) xml = load_xml(""" bar foo """) obj = custom_type.parse(xml[0], None) assert obj.item_1 == 'foo' assert obj.item_2 == 'bar' zeep-2.5.0/tests/test_xsd_indicators_choice.py0000644000076500000240000013571413224161177023570 0ustar mvantellingenstaff00000000000000from collections import deque import pytest from lxml import etree from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import xsd from zeep.exceptions import ValidationError, XMLParseError from zeep.helpers import serialize_object def test_choice_element(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) element = schema.get_element('ns0:container') value = element(item_1="foo") assert value.item_1 == 'foo' assert value.item_2 is None assert value.item_3 is None expected = """ foo """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) assert value.item_1 == 'foo' assert value.item_2 is None assert value.item_3 is None def test_choice_element_second_elm(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) element = schema.get_element('ns0:container') value = element(item_2="foo") assert value.item_1 is None assert value.item_2 == 'foo' assert value.item_3 is None expected = """ foo """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) assert value.item_1 is None assert value.item_2 == 'foo' assert value.item_3 is None def test_choice_element_second_elm_positional(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) child = schema.get_type('ns0:type_2')(child_1='ha', child_2='ho') element = schema.get_element('ns0:container') with pytest.raises(TypeError): value = element(child) value = element(item_2=child) element = schema.get_element('ns0:containerArray') with pytest.raises(TypeError): value = element(child) value = element(item_2=child) element = schema.get_element('ns0:container') value = element(item_2=child) assert value.item_1 is None assert value.item_2 == child expected = """ ha ho """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) assert value.item_1 is None assert value.item_2.child_1 == 'ha' assert value.item_2.child_2 == 'ho' def test_choice_element_multiple(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) element = schema.get_element('ns0:container') value = element(_value_1=[ {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'three'}, ]) assert value._value_1 == [ {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'three'}, ] expected = """ foo bar three """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) assert value._value_1 == [ {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'three'}, ] def test_choice_element_optional(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) element = schema.get_element('ns0:container') value = element(item_4="foo") expected = """ foo """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) assert value.item_4 == 'foo' def test_choice_element_with_any(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) element = schema.get_element('ns0:container') value = element(item_1="foo", name="foo", something="bar") expected = """ foo """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) result = element.parse(node[0], schema) assert result.name == 'foo' assert result.something is True assert result.item_1 == 'foo' def test_choice_element_with_any_max_occurs(): schema = xsd.Schema(load_xml(""" """)) element = schema.get_element('ns0:container') value = element( item_2="item-2", _value_1=[ xsd.AnyObject(schema.get_element('ns0:item_any'), 'any-content') ]) expected = """ item-2 any-content """ node = render_node(element, value) assert_nodes_equal(node, expected) result = element.parse(node[0], schema) assert result.item_2 == 'item-2' assert result._value_1 == ['any-content'] def test_choice_optional_values(): schema = load_xml(""" """) schema = xsd.Schema(schema) node = load_xml("") elm = schema.get_type('ns0:Transport') elm.parse_xmlelement(node, schema) def test_choice_in_sequence(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) container_elm = schema.get_element('ns0:container') assert container_elm.type.signature(schema=schema) == ( 'ns0:container(something: xsd:string, ({item_1: xsd:string} | {item_2: xsd:string} | {item_3: xsd:string}))') value = container_elm(something='foobar', item_1='item-1') expected = """ foobar item-1 """ node = etree.Element('document') container_elm.render(node, value) assert_nodes_equal(expected, node) value = container_elm.parse(node[0], schema) def test_choice_with_sequence(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:container') assert element.type.signature(schema=schema) == ( 'ns0:container(({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string}))') value = element(item_1='foo', item_2='bar') expected = """ foo bar """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) def test_choice_with_sequence_once(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:container') assert element.type.signature(schema=schema) == ( 'ns0:container(item_0: xsd:string, ({item_1: xsd:string, item_2: xsd:string}))') value = element(item_0='nul', item_1='foo', item_2='bar') expected = """ nul foo bar """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) def test_choice_with_sequence_unbounded(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:container') assert element.type.signature(schema=schema) == ( 'ns0:container(({[item_0: xsd:string, item_1: xsd:string, item_2: ns0:obj]}))') value = element(_value_1=[ {'item_0': 'nul', 'item_1': 'foo', 'item_2': {'_value_1': [{'item_2_1': 'bar'}]}}, ]) expected = """ nul foo bar """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) assert value._value_1[0]['item_0'] == 'nul' assert value._value_1[0]['item_1'] == 'foo' assert value._value_1[0]['item_2']._value_1[0]['item_2_1'] == 'bar' assert not hasattr(value._value_1[0]['item_2'], 'item_2_1') def test_choice_with_sequence_missing_elements(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:container') assert element.type.signature(schema=schema) == ( 'ns0:container(({item_1: xsd:string, item_2: xsd:string})[])') value = element(_value_1={'item_1': 'foo'}) with pytest.raises(ValidationError): render_node(element, value) def test_choice_with_sequence_once_extra_data(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:container') assert element.type.signature(schema=schema) == ( 'ns0:container(item_0: xsd:string, ({item_1: xsd:string, item_2: xsd:string}), item_3: xsd:string)') value = element(item_0='nul', item_1='foo', item_2='bar', item_3='item-3') expected = """ nul foo bar item-3 """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) def test_choice_with_sequence_second(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:container') assert element.type.signature(schema=schema) == ( 'ns0:container(({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string}))') value = element(item_3='foo', item_4='bar') expected = """ foo bar """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) def test_choice_with_sequence_invalid(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:container') assert element.type.signature(schema=schema) == ( 'ns0:container(({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string}))') with pytest.raises(TypeError): element(item_1='foo', item_4='bar') def test_choice_with_sequence_change(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:ElementName') elm = element(item_1='foo', item_2='bar') assert serialize_object(elm) == { 'item_3': None, 'item_2': 'bar', 'item_1': 'foo', 'item_4': None, 'nee': None } elm.item_1 = 'bla-1' elm.item_2 = 'bla-2' expected = """ bla-1 bla-2 """ node = etree.Element('document') element.render(node, elm) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) assert value.item_1 == 'bla-1' assert value.item_2 == 'bla-2' def test_choice_with_sequence_change_named(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:ElementName') elm = element(item_3='foo') elm = element(item_1='foo', item_2='bar') assert elm['item_1'] == 'foo' assert elm['item_2'] == 'bar' elm['item_1'] = 'bla-1' elm['item_2'] = 'bla-2' expected = """ bla-1 bla-2 """ node = etree.Element('document') element.render(node, elm) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) assert value.item_1 == 'bla-1' assert value.item_2 == 'bla-2' def test_choice_with_sequence_multiple(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:container') assert element.type.signature(schema=schema) == ( 'ns0:container(({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string})[])') value = element(_value_1=[ dict(item_1='foo', item_2='bar'), dict(item_3='foo', item_4='bar'), ]) expected = """ foo bar foo bar """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) def test_choice_with_sequence_and_element(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:container') assert element.type.signature(schema=schema) == ( 'ns0:container(({item_1: xsd:string} | {({item_2: xsd:string} | {item_3: xsd:string})}))') value = element(item_2='foo') expected = """ foo """ node = etree.Element('document') element.render(node, value) assert_nodes_equal(expected, node) value = element.parse(node[0], schema) def test_element_ref_in_choice(): node = etree.fromstring(""" """.strip()) schema = xsd.Schema(node) foo_type = schema.get_element('{http://tests.python-zeep.org/}foo') assert isinstance(foo_type.type, xsd.String) custom_type = schema.get_element('{http://tests.python-zeep.org/}container') value = custom_type(foo='bar') assert value.foo == 'bar' assert value.bar is None node = etree.Element('document') custom_type.render(node, value) expected = """ bar """ assert_nodes_equal(expected, node) def test_parse_dont_loop(): schema = xsd.Schema(load_xml(""" """)) element = schema.get_element('ns0:container') expected = load_xml(""" foo bar foo bar """) with pytest.raises(XMLParseError): element.parse(expected, schema) def test_parse_check_unexpected(): schema = xsd.Schema(load_xml(""" """)) element = schema.get_element('ns0:container') expected = load_xml(""" foo bar foo """) with pytest.raises(XMLParseError): element.parse(expected, schema) def test_parse_check_mixed(): schema = xsd.Schema(load_xml(""" """)) element = schema.get_element('ns0:container') expected = load_xml(""" foo bar foo """) element.parse(expected, schema) def test_parse_check_mixed_choices(): schema = xsd.Schema(load_xml(""" """)) element = schema.get_element('ns0:container') # item_1_1 value = element(item_1_1="foo") assert value.item_1_1 == 'foo' node = etree.Element('document') element.render(node, value) expected = """ foo """ assert_nodes_equal(expected, node) # item_1_2a value = element(item_1_2a="foo") node = etree.Element('document') element.render(node, value) expected = """ foo """ assert_nodes_equal(expected, node) # item_1_2a & item_1_2b value = element(item_1_2a="foo", item_1_2b="bar") node = etree.Element('document') element.render(node, value) expected = """ foo bar """ assert_nodes_equal(expected, node) # item_2 value = element(item_2="foo") assert value.item_2 == 'foo' node = etree.Element('document') element.render(node, value) expected = """ foo """ assert_nodes_equal(expected, node) # item_3 value = element(item_3="foo") assert value.item_3 == 'foo' node = etree.Element('document') element.render(node, value) expected = """ foo """ assert_nodes_equal(expected, node) def test_choice_extend(): schema = xsd.Schema(load_xml(""" """)) element = schema.get_element('ns0:container') node = load_xml(""" foo bar """) value = element.parse(node, schema) node = load_xml(""" foo bar xafoo xabar """) value = element.parse(node, schema) assert value['item-1-1'] == 'foo' assert value['item-1-2'] == 'bar' assert value['_value_1'][0] == {'item-2-1': 'xafoo'} assert value['_value_1'][1] == {'item-2-2': 'xabar'} def test_nested_choice(): schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') container_type = schema.get_element('tns:container') item = container_type(_value_1=[{'a': 'item-1'}, {'a': 'item-2'}]) assert item._value_1[0] == {'a': 'item-1'} assert item._value_1[1] == {'a': 'item-2'} expected = load_xml(""" item-1 item-2 """) node = render_node(container_type, item) assert_nodes_equal(node, expected) result = container_type.parse(expected[0], schema) assert result._value_1[0] == {'a': 'item-1'} assert result._value_1[1] == {'a': 'item-2'} expected = load_xml(""" 1 """) result = container_type.parse(expected, schema) assert result.b == '1' def test_unit_choice_parse_xmlelements_max_1(): schema = xsd.Schema(load_xml(""" """)) element = schema.get_element('ns0:container') def create_elm(name, text): elm = etree.Element(name) elm.text = text return elm data = deque([ create_elm('item_1', 'item-1'), create_elm('item_2', 'item-2'), create_elm('item_1', 'item-3'), ]) result = element.type._element.parse_xmlelements(data, schema) assert result == {'item_1': 'item-1'} assert len(data) == 2 def test_unit_choice_parse_xmlelements_max_2(): schema = xsd.Schema(load_xml(""" """)) element = schema.get_element('ns0:container') def create_elm(name, text): elm = etree.Element(name) elm.text = text return elm data = deque([ create_elm('item_1', 'item-1'), create_elm('item_2', 'item-2'), create_elm('item_1', 'item-3'), ]) result = element.type._element.parse_xmlelements(data, schema, name='items') assert result == { 'items': [ {'item_1': 'item-1'}, {'item_2': 'item-2'}, ] } assert len(data) == 1 zeep-2.5.0/tests/test_xsd_indicators_group.py0000644000076500000240000003633013224161177023464 0ustar mvantellingenstaff00000000000000import pytest from lxml import etree from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import xsd def test_build_objects(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'username'), xsd.String()), xsd.Group( etree.QName('http://tests.python-zeep.org/', 'groupie'), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'password'), xsd.String(), ) ]) ) ]) )) assert custom_type.signature() obj = custom_type(username='foo', password='bar') expected = """ foo bar """ node = etree.Element('document') custom_type.render(node, obj) assert_nodes_equal(expected, node) obj = custom_type.parse(node[0], None) assert obj.username == 'foo' assert obj.password == 'bar' def test_build_group_min_occurs_1(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Group( etree.QName('http://tests.python-zeep.org/', 'foobar'), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]), min_occurs=1) )) obj = custom_type(item_1='foo', item_2='bar') assert obj.item_1 == 'foo' assert obj.item_2 == 'bar' result = render_node(custom_type, obj) expected = load_xml(""" foo bar """) assert_nodes_equal(result, expected) obj = custom_type.parse(result[0], None) assert obj.item_1 == 'foo' assert obj.item_2 == 'bar' assert not hasattr(obj, 'foobar') def test_build_group_min_occurs_1_parse_args(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Group( etree.QName('http://tests.python-zeep.org/', 'foobar'), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]), min_occurs=1) )) obj = custom_type('foo', 'bar') assert obj.item_1 == 'foo' assert obj.item_2 == 'bar' def test_build_group_min_occurs_2(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Group( etree.QName('http://tests.python-zeep.org/', 'foobar'), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]), min_occurs=2, max_occurs=2) )) obj = custom_type(_value_1=[ {'item_1': 'foo', 'item_2': 'bar'}, {'item_1': 'foo', 'item_2': 'bar'}, ]) assert obj._value_1 == [ {'item_1': 'foo', 'item_2': 'bar'}, {'item_1': 'foo', 'item_2': 'bar'}, ] result = render_node(custom_type, obj) expected = load_xml(""" foo bar foo bar """) assert_nodes_equal(result, expected) obj = custom_type.parse(result[0], None) assert obj._value_1 == [ {'item_1': 'foo', 'item_2': 'bar'}, {'item_1': 'foo', 'item_2': 'bar'}, ] assert not hasattr(obj, 'foobar') def test_build_group_min_occurs_2_sequence_min_occurs_2(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Group( etree.QName('http://tests.python-zeep.org/', 'foobar'), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ], min_occurs=2, max_occurs=2), min_occurs=2, max_occurs=2) )) expected = etree.fromstring(""" foo bar foo bar foo bar foo bar """) obj = custom_type.parse(expected, None) assert obj._value_1 == [ {'_value_1': [ {'item_1': 'foo', 'item_2': 'bar'}, {'item_1': 'foo', 'item_2': 'bar'}, ]}, {'_value_1': [ {'item_1': 'foo', 'item_2': 'bar'}, {'item_1': 'foo', 'item_2': 'bar'}, ]}, ] assert not hasattr(obj, 'foobar') def test_build_group_occurs_1_invalid_kwarg(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Group( etree.QName('http://tests.python-zeep.org/', 'foobar'), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]), min_occurs=1, max_occurs=1) )) with pytest.raises(TypeError): custom_type(item_1='foo', item_2='bar', error=True) def test_build_group_min_occurs_2_invalid_kwarg(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Group( etree.QName('http://tests.python-zeep.org/', 'foobar'), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]), min_occurs=2, max_occurs=2) )) with pytest.raises(TypeError): custom_type(_value_1=[ {'item_1': 'foo', 'item_2': 'bar', 'error': True}, {'item_1': 'foo', 'item_2': 'bar'}, ]) def test_xml_group_via_ref(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type(first_name='foo', last_name='bar') node = etree.Element('document') address_type.render(node, obj) expected = """ foo bar """ assert_nodes_equal(expected, node) def test_xml_group_via_ref_max_occurs_unbounded(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type( _value_1=[ {'first_name': 'foo-1', 'last_name': 'bar-1'}, {'first_name': 'foo-2', 'last_name': 'bar-2'}, ]) node = etree.Element('document') address_type.render(node, obj) expected = """ foo-1 bar-1 foo-2 bar-2 """ assert_nodes_equal(expected, node) obj = address_type.parse(node[0], None) assert obj._value_1[0]['first_name'] == 'foo-1' assert obj._value_1[0]['last_name'] == 'bar-1' assert obj._value_1[1]['first_name'] == 'foo-2' assert obj._value_1[1]['last_name'] == 'bar-2' def test_xml_multiple_groups_in_sequence(): schema = xsd.Schema(load_xml(""" blub """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type( first_name='foo', last_name='bar', city='Utrecht', country='The Netherlands') node = etree.Element('document') address_type.render(node, obj) expected = """ foo bar Utrecht The Netherlands """ assert_nodes_equal(expected, node) def test_xml_group_methods(): schema = xsd.Schema(load_xml(""" blub """)) Group = schema.get_group('{http://tests.python-zeep.org/}Group') assert Group.signature(schema) == ( 'ns0:Group(city: xsd:string, country: xsd:string)') assert str(Group) == ( '{http://tests.python-zeep.org/}Group(city: xsd:string, country: xsd:string)') assert len(list(Group)) == 2 def test_xml_group_extension(): schema = xsd.Schema(load_xml(""" """)) SubGroup = schema.get_type('{http://tests.python-zeep.org/}SubGroup') assert SubGroup.signature(schema) == ( 'ns0:SubGroup(item_1: xsd:string, item_2: xsd:string, item_3: xsd:string)') SubGroup(item_1='een', item_2='twee', item_3='drie') zeep-2.5.0/tests/test_xsd_indicators_sequence.py0000644000076500000240000003761013224161177024142 0ustar mvantellingenstaff00000000000000import pytest from lxml import etree from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import xsd def test_build_occurs_1(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]) )) obj = custom_type(item_1='foo', item_2='bar') assert obj.item_1 == 'foo' assert obj.item_2 == 'bar' result = render_node(custom_type, obj) expected = load_xml(""" foo bar """) assert_nodes_equal(result, expected) obj = custom_type.parse(expected[0], None) assert obj.item_1 == 'foo' assert obj.item_2 == 'bar' def test_build_occurs_1_skip_value(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]) )) obj = custom_type(item_1=xsd.SkipValue, item_2='bar') assert obj.item_1 == xsd.SkipValue assert obj.item_2 == 'bar' result = render_node(custom_type, obj) expected = load_xml(""" bar """) assert_nodes_equal(result, expected) def test_build_min_occurs_2_max_occurs_2(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ], min_occurs=2, max_occurs=2) )) assert custom_type.signature() elm = custom_type(_value_1=[ {'item_1': 'foo-1', 'item_2': 'bar-1'}, {'item_1': 'foo-2', 'item_2': 'bar-2'}, ]) assert elm._value_1 == [ {'item_1': 'foo-1', 'item_2': 'bar-1'}, {'item_1': 'foo-2', 'item_2': 'bar-2'}, ] expected = load_xml(""" foo bar foo bar """) obj = custom_type.parse(expected, None) assert obj._value_1 == [ { 'item_1': 'foo', 'item_2': 'bar', }, { 'item_1': 'foo', 'item_2': 'bar', }, ] def test_build_min_occurs_2_max_occurs_2_error(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ], min_occurs=2, max_occurs=2) )) with pytest.raises(TypeError): custom_type(_value_1={ 'item_1': 'foo-1', 'item_2': 'bar-1', 'error': True }) def test_build_sequence_and_attributes(): custom_element = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]), [ xsd.Attribute( etree.QName('http://tests.python-zeep.org/', 'attr_1'), xsd.String()), xsd.Attribute('attr_2', xsd.String()), ] )) expected = load_xml(""" foo bar """) obj = custom_element.parse(expected, None) assert obj.item_1 == 'foo' assert obj.item_2 == 'bar' assert obj.attr_1 == 'x' assert obj.attr_2 == 'y' def test_build_sequence_with_optional_elements(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'container'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2_1'), xsd.String(), nillable=True) ]) ) ), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_3'), xsd.String(), max_occurs=2), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_4'), xsd.String(), min_occurs=0), ]) )) expected = etree.fromstring(""" 1 3 """) obj = custom_type.parse(expected, None) assert obj.item_1 == '1' assert obj.item_2 is None assert obj.item_3 == ['3'] assert obj.item_4 is None def test_build_max_occurs_unbounded(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ], max_occurs='unbounded') )) expected = etree.fromstring(""" foo bar """) obj = custom_type.parse(expected, None) assert obj._value_1 == [ { 'item_1': 'foo', 'item_2': 'bar', } ] def test_xml_sequence_with_choice(): schema = xsd.Schema(load_xml(""" """)) xml = load_xml(""" blabla haha """) elm = schema.get_element('{http://tests.python-zeep.org/tst}container') result = elm.parse(xml, schema) assert result.item_1 == 'blabla' assert result.item_3 == 'haha' def test_xml_sequence_with_choice_max_occurs_2(): schema = xsd.Schema(load_xml(""" """)) xml = load_xml(""" item-1-1 item-1-2 item-3 """) elm = schema.get_element('{http://tests.python-zeep.org/tst}container') result = elm.parse(xml, schema) assert result._value_1 == [ {'item_1': 'item-1-1'}, {'item_1': 'item-1-2'}, ] assert result.item_3 == 'item-3' def test_xml_sequence_with_choice_max_occurs_3(): schema = xsd.Schema(load_xml(""" """)) xml = load_xml(""" text-1 text-2 text-1 text-2 text-3 text-4 """) elm = schema.get_element('{http://tests.python-zeep.org/tst}container') result = elm.parse(xml, schema) assert result._value_1 == [ {'item_1': 'text-1', 'item_2': 'text-2'}, {'item_1': 'text-1', 'item_2': 'text-2'}, {'item_3': 'text-3'}, ] assert result.item_4 == 'text-4' def test_xml_sequence_with_nil_element(): schema = xsd.Schema(load_xml(""" """)) xml = load_xml(""" text-1 text-2 text-4 text-5 """) elm = schema.get_element('{http://tests.python-zeep.org/}container') result = elm.parse(xml, schema) assert result.item == [ 'text-1', 'text-2', None, 'text-4', 'text-5', ] def test_xml_sequence_unbounded(): schema = xsd.Schema(load_xml(""" """)) elm_type = schema.get_type('{http://tests.python-zeep.org/}ValueListType') with pytest.raises(TypeError): elm_type(Value='bla') elm_type(_value_1={'Value': 'bla'}) def test_xml_sequence_recover_from_missing_element(): schema = xsd.Schema(load_xml(""" """), strict=False) xml = load_xml(""" text-1 text-3 text-4 """) elm_type = schema.get_type('{http://tests.python-zeep.org/}container') result = elm_type.parse_xmlelement(xml, schema) assert result.item_1 == 'text-1' assert result.item_2 is None assert result.item_3 == 'text-3' assert result.item_4 == 'text-4' zeep-2.5.0/tests/test_xsd_integration.py0000644000076500000240000007223513224161177022440 0ustar mvantellingenstaff00000000000000import copy import pytest from lxml import etree from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import xsd def test_xml_complex_type_nested_wrong_type(): schema = xsd.Schema(load_xml(""" """)) container_elm = schema.get_element('ns0:container') with pytest.raises(TypeError): container_elm(item={'bar': 1}) def test_xml_element_with_annotation(): schema = xsd.Schema(load_xml(""" HOI! """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') address_type = schema.get_element('tns:Address') address_type(foo='bar') def test_xml_complex_type_parsexml(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') input_node = load_xml("""
bar
""") obj = address_type.parse(input_node, None) assert obj.foo == 'bar' def test_xml_array(): schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') address_type = schema.get_element('tns:Address') obj = address_type() assert obj.foo == [] obj.foo.append('foo') obj.foo.append('bar') expected = """ foo bar """ node = etree.Element('document', nsmap=schema._prefix_map_custom) address_type.render(node, obj) assert_nodes_equal(expected, node) def test_xml_complex_type_unbounded_one(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type(foo=['foo']) expected = """ foo """ node = etree.Element('document') address_type.render(node, obj) assert_nodes_equal(expected, node) def test_xml_complex_type_unbounded_named(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type() assert obj.foo == [] obj.foo.append('foo') obj.foo.append('bar') expected = """ foo bar """ node = etree.Element('document') address_type.render(node, obj) assert_nodes_equal(expected, node) def test_xml_complex_type_array_to_other_complex_object(): schema = xsd.Schema(load_xml(""" """)) address_array = schema.get_element('ArrayOfAddress') obj = address_array() assert obj.Address == [] obj.Address.append(schema.get_type('Address')(foo='foo')) obj.Address.append(schema.get_type('Address')(foo='bar')) expected = """
foo
bar
""" result = render_node(address_array, obj) assert_nodes_equal(expected, result) def test_xml_complex_type_init_kwargs(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type( NameFirst='John', NameLast='Doe', Email='j.doe@example.com') assert obj.NameFirst == 'John' assert obj.NameLast == 'Doe' assert obj.Email == 'j.doe@example.com' def test_xml_complex_type_init_args(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type('John', 'Doe', 'j.doe@example.com') assert obj.NameFirst == 'John' assert obj.NameLast == 'Doe' assert obj.Email == 'j.doe@example.com' def test_xml_element_ref_missing_namespace(): # For buggy soap servers (#170) schema = xsd.Schema(load_xml(""" """)) custom_type = schema.get_element('{http://tests.python-zeep.org/}bar') input_xml = load_xml(""" bar """) item = custom_type.parse(input_xml, schema) assert item.foo == 'bar' def test_xml_element_ref(): schema = xsd.Schema(load_xml(""" """)) foo_type = schema.get_element('{http://tests.python-zeep.org/}foo') assert isinstance(foo_type.type, xsd.String) custom_type = schema.get_element('{http://tests.python-zeep.org/}bar') custom_type.signature() obj = custom_type(foo='bar') node = etree.Element('document') custom_type.render(node, obj) expected = """ bar """ assert_nodes_equal(expected, node) def test_xml_element_ref_occurs(): schema = xsd.Schema(load_xml(""" """)) foo_type = schema.get_element('{http://tests.python-zeep.org/}foo') assert isinstance(foo_type.type, xsd.String) custom_type = schema.get_element('{http://tests.python-zeep.org/}bar') custom_type.signature() obj = custom_type(bar='foo') node = etree.Element('document') custom_type.render(node, obj) expected = """ foo """ assert_nodes_equal(expected, node) def test_xml_unqualified(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type(foo='bar') expected = """ bar """ node = etree.Element('document') address_type.render(node, obj) assert_nodes_equal(expected, node) def test_xml_defaults(): schema = xsd.Schema(load_xml(""" """)) container_type = schema.get_element( '{http://tests.python-zeep.org/}container') obj = container_type() assert obj.item_1 == "hoi" assert obj.item_2 is None assert obj.attr_1 == "hoi" expected = """ hoi """ node = etree.Element('document') container_type.render(node, obj) assert_nodes_equal(expected, node) obj.item_2 = 'ok' expected = """ hoi ok """ node = etree.Element('document') container_type.render(node, obj) assert_nodes_equal(expected, node) def test_xml_defaults_parse_boolean(): schema = xsd.Schema(load_xml(""" """)) container_type = schema.get_element( '{http://tests.python-zeep.org/}container') obj = container_type() assert obj.foo == "false" assert obj.bar == "0" expected = """ false """ node = etree.Element('document') container_type.render(node, obj) assert_nodes_equal(expected, node) def test_xml_defaults_parse(): schema = xsd.Schema(load_xml(""" """)) container_elm = schema.get_element( '{http://tests.python-zeep.org/}container') node = load_xml(""" hoi """) item = container_elm.parse(node, schema) assert item.attr_1 == 'hoi' def test_xml_init_with_dicts(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type(name='foo', container={'service': [{'name': 'foo'}]}) expected = """ foo foo """ node = etree.Element('document') address_type.render(node, obj) assert_nodes_equal(expected, node) def test_xml_sequence_in_sequence(): schema = xsd.Schema(load_xml(""" """)) element = schema.get_element('ns0:container') value = element(item_1="foo", item_2="bar") node = etree.Element('document') element.render(node, value) expected = """ foo bar """ assert_nodes_equal(expected, node) def test_xml_sequence_in_sequence_many(): node = load_xml(""" """) schema = xsd.Schema(node) element = schema.get_element('ns0:container') value = element(_value_1=[ {'item_1': "value-1-1", 'item_2': "value-1-2"}, {'item_1': "value-2-1", 'item_2': "value-2-2"}, ]) assert value._value_1 == [ {'item_1': "value-1-1", 'item_2': "value-1-2"}, {'item_1': "value-2-1", 'item_2': "value-2-2"}, ] node = etree.Element('document') element.render(node, value) expected = """ value-1-1 value-1-2 value-2-1 value-2-2 """ assert_nodes_equal(expected, node) def test_xml_complex_type_empty(): schema = xsd.Schema(load_xml(""" """)) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') obj = container_elm(something={}) node = etree.Element('document') container_elm.render(node, obj) expected = """ """ assert_nodes_equal(expected, node) item = container_elm.parse(node.getchildren()[0], schema) assert item.something is None def test_xml_schema_as_payload(): schema = xsd.Schema(load_xml(""" """)) elm_class = schema.get_element('{http://tests.python-zeep.org/}container') node = load_xml(""" value-1 value-2 """) value = elm_class.parse(node, schema) assert value._value_1['item-1'] == 'value-1' assert value._value_1['item-2'] == 'value-2' def test_xml_nill(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('ns0:container') obj = address_type() expected = """ """ node = etree.Element('document') address_type.render(node, obj) etree.cleanup_namespaces(node) assert_nodes_equal(expected, node) def test_xml_empty_xmlns(): schema = xsd.Schema(load_xml(""" """)) container_elm = schema.get_element('{http://tests.python-zeep.org/}container') node = load_xml(""" foo """) item = container_elm.parse(node, schema) assert item._value_1 == 'foo' def test_xml_keep_objects_intact(): schema = xsd.Schema(load_xml(""" """)) address_type = schema.get_element('{http://tests.python-zeep.org/}Address') obj = address_type(name='foo', container={'service': [{'name': 'foo'}]}) org_obj = copy.deepcopy(obj) node = etree.Element('document') address_type.render(node, obj) print(org_obj) print(obj) assert org_obj['container']['service'] == obj['container']['service'] zeep-2.5.0/tests/test_xsd_parse.py0000644000076500000240000004003013224161177021213 0ustar mvantellingenstaff00000000000000import datetime from lxml import etree from tests.utils import load_xml from zeep import xsd from zeep.xsd.schema import Schema def test_sequence_parse_regression(): schema_doc = load_xml(b""" """) response_doc = load_xml(b""" """) schema = xsd.Schema(schema_doc) elm = schema.get_element('{http://tests.python-zeep.org/attr}Response') node = response_doc.xpath( '//ns0:Response', namespaces={ 'xsd': 'http://www.w3.org/2001/XMLSchema', 'ns0': 'http://tests.python-zeep.org/attr', }) response = elm.parse(node[0], None) assert response.Result.id == 2 def test_sequence_parse_anytype(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'container'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.AnyType()), ]) )) expected = etree.fromstring(""" foo """) obj = custom_type.parse(expected, None) assert obj.item_1 == 'foo' def test_sequence_parse_anytype_nil(): schema = xsd.Schema(load_xml(b""" """)) container = schema.get_element('{http://tests.python-zeep.org/}container') expected = etree.fromstring(""" """) obj = container.parse(expected, schema) assert obj.item_1 is None def test_sequence_parse_anytype_obj(): value_type = xsd.ComplexType( xsd.Sequence([ xsd.Element( '{http://tests.python-zeep.org/}value', xsd.Integer()), ]) ) schema = Schema( etree.Element( '{http://www.w3.org/2001/XMLSchema}Schema', targetNamespace='http://tests.python-zeep.org/')) root = schema.root_document root.register_type('{http://tests.python-zeep.org/}something', value_type) custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'container'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.AnyType()), ]) )) expected = etree.fromstring(""" 100 """) obj = custom_type.parse(expected, schema) assert obj.item_1.value == 100 def test_sequence_parse_anytype_regression_17(): schema_doc = load_xml(b""" """) xml = load_xml(b""" blabla solution text/html Test Solution false """) schema = xsd.Schema(schema_doc) elm = schema.get_element( '{http://tests.python-zeep.org/tst}getCustomFieldResponse' ) result = elm.parse(xml, schema) assert result.getCustomFieldReturn.value.content == 'Test Solution' def test_nested_complex_type(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.ComplexType( xsd.Sequence([ xsd.Element( '{http://tests.python-zeep.org/}item_2a', xsd.String()), xsd.Element( '{http://tests.python-zeep.org/}item_2b', xsd.String()), ]) ) ) ]) )) expected = etree.fromstring(""" foo 2a 2b """) obj = custom_type.parse(expected, None) assert obj.item_1 == 'foo' assert obj.item_2.item_2a == '2a' assert obj.item_2.item_2b == '2b' def test_nested_complex_type_optional(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Element( '{http://tests.python-zeep.org/}item_2a1', xsd.String(), min_occurs=0), xsd.Element( '{http://tests.python-zeep.org/}item_2a2', xsd.String(), min_occurs=0), ]), xsd.Element( '{http://tests.python-zeep.org/}item_2b', xsd.String()), ]) ), min_occurs=0, max_occurs='unbounded' ) ]) )) expected = etree.fromstring(""" foo """) obj = custom_type.parse(expected, None) assert obj.item_1 == 'foo' assert obj.item_2 == [] expected = etree.fromstring(""" foo """) obj = custom_type.parse(expected, None) assert obj.item_1 == 'foo' assert obj.item_2 == [None] expected = etree.fromstring(""" foo x """) obj = custom_type.parse(expected, None) assert obj.item_1 == 'foo' assert obj.item_2[0].item_2a1 == 'x' assert obj.item_2[0].item_2b is None expected = etree.fromstring(""" foo x """) obj = custom_type.parse(expected, None) assert obj.item_1 == 'foo' assert obj.item_2[0].item_2a1 == 'x' assert obj.item_2[0].item_2b is None def test_nested_choice_optional(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Choice([ xsd.Element( '{http://tests.python-zeep.org/}item_2', xsd.String()), xsd.Element( '{http://tests.python-zeep.org/}item_3', xsd.String()), ], min_occurs=0, max_occurs=1 ), ]) )) expected = etree.fromstring(""" foo bar """) obj = custom_type.parse(expected, None) assert obj.item_1 == 'foo' assert obj.item_2 == 'bar' expected = etree.fromstring(""" foo """) obj = custom_type.parse(expected, None) assert obj.item_1 == 'foo' assert obj.item_2 is None assert obj.item_3 is None def test_union(): schema_doc = load_xml(b""" """) xml = load_xml(b""" Idle """) schema = xsd.Schema(schema_doc) elm = schema.get_element('{http://tests.python-zeep.org/tst}State') result = elm.parse(xml, schema) assert result._value_1 == 'Idle' def test_parse_invalid_values(): schema = xsd.Schema(load_xml(b""" """)) xml = load_xml(b""" foo 2016-10-20 """) elm = schema.get_element('{http://tests.python-zeep.org/}container') result = elm.parse(xml, schema) assert result.item_1 is None assert result.item_2 == datetime.date(2016, 10, 20) assert result.attr_1 is None assert result.attr_2 == datetime.date(2013, 10, 20) def test_xsd_missing_localname(): schema = xsd.Schema(load_xml(b""" """)) schema.get_element('{http://tests.python-zeep.org/}container') zeep-2.5.0/tests/test_xsd_schemas.py0000644000076500000240000007107213224161177021536 0ustar mvantellingenstaff00000000000000import pytest from lxml import etree from tests.utils import ( DummyTransport, assert_nodes_equal, load_xml, render_node) from zeep import exceptions, xsd from zeep.xsd import Schema from zeep.xsd.types.unresolved import UnresolvedType def test_default_types(): schema = xsd.Schema() xsd_string = schema.get_type('{http://www.w3.org/2001/XMLSchema}string') assert xsd_string == xsd.String() def test_default_types_not_found(): schema = xsd.Schema() with pytest.raises(exceptions.LookupError): schema.get_type('{http://www.w3.org/2001/XMLSchema}bar') def test_default_elements(): schema = xsd.Schema() xsd_schema = schema.get_element('{http://www.w3.org/2001/XMLSchema}schema') isinstance(xsd_schema, Schema) def test_default_elements_not_found(): schema = xsd.Schema() with pytest.raises(exceptions.LookupError): schema.get_element('{http://www.w3.org/2001/XMLSchema}bar') def test_invalid_namespace_handling(): schema = xsd.Schema() qname = '{http://tests.python-zeep.org/404}foo' with pytest.raises(exceptions.NamespaceError) as exc: schema.get_element(qname) assert qname in str(exc.value.message) with pytest.raises(exceptions.NamespaceError) as exc: schema.get_type(qname) assert qname in str(exc.value.message) with pytest.raises(exceptions.NamespaceError) as exc: schema.get_group(qname) assert qname in str(exc.value.message) with pytest.raises(exceptions.NamespaceError) as exc: schema.get_attribute(qname) assert qname in str(exc.value.message) with pytest.raises(exceptions.NamespaceError) as exc: schema.get_attribute_group(qname) assert qname in str(exc.value.message) def test_invalid_localname_handling(): schema = xsd.Schema(load_xml(""" """)) qname = '{http://tests.python-zeep.org/}foo' namespace = 'http://tests.python-zeep.org/' localname = 'foo' with pytest.raises(exceptions.LookupError) as exc: schema.get_element(qname) assert namespace in str(exc.value.message) assert localname in str(exc.value.message) with pytest.raises(exceptions.LookupError) as exc: schema.get_type(qname) assert namespace in str(exc.value.message) assert localname in str(exc.value.message) with pytest.raises(exceptions.LookupError) as exc: schema.get_group(qname) assert namespace in str(exc.value.message) assert localname in str(exc.value.message) with pytest.raises(exceptions.LookupError) as exc: schema.get_attribute(qname) assert namespace in str(exc.value.message) assert localname in str(exc.value.message) with pytest.raises(exceptions.LookupError) as exc: schema.get_attribute_group(qname) assert namespace in str(exc.value.message) assert localname in str(exc.value.message) def test_schema_repr_none(): schema = xsd.Schema() assert repr(schema) == "" def test_schema_repr_val(): schema = xsd.Schema(load_xml(""" """)) assert repr(schema) == "" def test_schema_doc_repr_val(): schema = xsd.Schema(load_xml(""" """)) docs = schema._get_schema_documents('http://tests.python-zeep.org/') assert len(docs) == 1 doc = docs[0] assert repr(doc) == "" def test_multiple_extension(): node_a = etree.fromstring(""" """.strip()) node_b = etree.fromstring(""" """.strip()) node_c = etree.fromstring(""" """.strip()) etree.XMLSchema(node_c) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/b.xsd', node_b) transport.bind('http://tests.python-zeep.org/c.xsd', node_c) schema = xsd.Schema(node_a, transport=transport) type_a = schema.get_type('ns0:type_a') type_a(wat='x') def test_global_element_and_type(): node_a = etree.fromstring(""" """.strip()) node_b = etree.fromstring(""" """.strip()) node_c = etree.fromstring(""" """.strip()) etree.XMLSchema(node_c) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/b.xsd', node_b) transport.bind('http://tests.python-zeep.org/c.xsd', node_c) schema = xsd.Schema(node_a, transport=transport) type_a = schema.get_type('{http://tests.python-zeep.org/c}type_a') type_a = schema.get_type('{http://tests.python-zeep.org/c}type_a') type_a(item_a='x') elm = schema.get_element('{http://tests.python-zeep.org/c}item') elm('x') elm = schema.get_type('{http://tests.python-zeep.org/a}refs') elm(ref_elm='foo', ref_attr='bar') def test_cyclic_imports(): schema_a = etree.fromstring(""" """.strip()) schema_b = etree.fromstring(""" """.strip()) schema_c = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/a.xsd', schema_a) transport.bind('http://tests.python-zeep.org/b.xsd', schema_b) transport.bind('http://tests.python-zeep.org/c.xsd', schema_c) xsd.Schema(schema_a, transport=transport, location='http://tests.python-zeep.org/a.xsd') def test_get_type_through_import(): schema_a = etree.fromstring(""" """.strip()) schema_b = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/a.xsd', schema_a) transport.bind('http://tests.python-zeep.org/b.xsd', schema_b) xsd.Schema(schema_a, transport=transport) def test_duplicate_target_namespace(): schema_a = etree.fromstring(""" """.strip()) schema_b = etree.fromstring(""" """.strip()) schema_c = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/a.xsd', schema_a) transport.bind('http://tests.python-zeep.org/b.xsd', schema_b) transport.bind('http://tests.python-zeep.org/c.xsd', schema_c) schema = xsd.Schema(schema_a, transport=transport) elm_b = schema.get_element('{http://tests.python-zeep.org/duplicate}elm-in-b') elm_c = schema.get_element('{http://tests.python-zeep.org/duplicate}elm-in-c') assert not isinstance(elm_b.type, UnresolvedType) assert not isinstance(elm_c.type, UnresolvedType) def test_multiple_no_namespace(): node_a = etree.fromstring(""" """.strip()) node_b = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/b.xsd', node_b) transport.bind('http://tests.python-zeep.org/c.xsd', node_b) xsd.Schema(node_a, transport=transport) def test_multiple_only_target_ns(): node_a = etree.fromstring(""" """.strip()) node_b = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/b.xsd', node_b) transport.bind('http://tests.python-zeep.org/c.xsd', node_b) xsd.Schema(node_a, transport=transport) def test_schema_error_handling(): node_a = etree.fromstring(""" """.strip()) transport = DummyTransport() schema = xsd.Schema(node_a, transport=transport) with pytest.raises(ValueError): schema.get_element('nonexisting:something') with pytest.raises(ValueError): schema.get_type('nonexisting:something') with pytest.raises(exceptions.NamespaceError): schema.get_element('{nonexisting}something') with pytest.raises(exceptions.NamespaceError): schema.get_type('{nonexisting}something') with pytest.raises(exceptions.LookupError): schema.get_element('ns0:something') with pytest.raises(exceptions.LookupError): schema.get_type('ns0:something') def test_schema_import_xmlsoap(): node_a = etree.fromstring(""" """.strip()) transport = DummyTransport() xsd.Schema(node_a, transport=transport) def test_schema_import_unresolved(): node_a = etree.fromstring(""" """.strip()) transport = DummyTransport() xsd.Schema(node_a, transport=transport) def test_no_target_namespace(): node_a = etree.fromstring(""" """.strip()) node_b = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/b.xsd', node_b) xsd.Schema(node_a, transport=transport) def test_include_recursion(): node_a = etree.fromstring(""" """.strip()) node_b = etree.fromstring(""" """.strip()) node_c = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/b.xsd', node_b) transport.bind('http://tests.python-zeep.org/c.xsd', node_c) schema = xsd.Schema(node_a, transport=transport) schema.get_element('{http://tests.python-zeep.org/b}foo') schema.get_element('{http://tests.python-zeep.org/b}bar') def test_include_relative(): node_a = etree.fromstring(""" """.strip()) node_b = etree.fromstring(""" """.strip()) node_c = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/subdir/b.xsd', node_b) transport.bind('http://tests.python-zeep.org/subdir/c.xsd', node_c) schema = xsd.Schema(node_a, transport=transport) schema.get_element('{http://tests.python-zeep.org/a}foo') schema.get_element('{http://tests.python-zeep.org/a}bar') def test_include_no_default_namespace(): node_a = etree.fromstring(""" """.strip()) # include without default namespace, other xsd prefix node_b = etree.fromstring(""" """.strip()) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/b.xsd', node_b) schema = xsd.Schema(node_a, transport=transport) item = schema.get_element('{http://tests.python-zeep.org/tns}container') assert item def test_include_different_form_defaults(): node_a = etree.fromstring(""" """.strip()) # include without default namespace, other xsd prefix node_b = load_xml(""" """) transport = DummyTransport() transport.bind('http://tests.python-zeep.org/b.xsd', node_b) schema = xsd.Schema(node_a, transport=transport) item = schema.get_element('{http://tests.python-zeep.org/}container') obj = item(item='foo', attr='bar') node = render_node(item, obj) expected = load_xml(""" foo """) assert_nodes_equal(expected, node) def test_merge(): node_a = etree.fromstring(""" """.strip()) node_b = etree.fromstring(""" """.strip()) schema_a = xsd.Schema(node_a) schema_b = xsd.Schema(node_b) schema_a.merge(schema_b) schema_a.get_element('{http://tests.python-zeep.org/a}foo') schema_a.get_element('{http://tests.python-zeep.org/b}foo') def test_xml_namespace(): xmlns = load_xml(""" """) transport = DummyTransport() transport.bind('http://www.w3.org/2001/xml.xsd', xmlns) xsd.Schema(load_xml(""" """), transport=transport) zeep-2.5.0/tests/test_xsd_signatures.py0000644000076500000240000002021313224161177022266 0ustar mvantellingenstaff00000000000000from lxml import etree from tests.utils import load_xml from zeep import xsd def test_signature_complex_type_choice(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Choice([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), ]) )) assert custom_type.signature() == '{http://tests.python-zeep.org/}authentication(({item_1: xsd:string} | {item_2: xsd:string}))' def test_signature_complex_type_choice_sequence(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Choice([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2_2'), xsd.String()), ]) ]) )) assert custom_type.signature() == ( '{http://tests.python-zeep.org/}authentication(({item_1: xsd:string} | {item_2_1: xsd:string, item_2_2: xsd:string}))') def test_signature_nested_sequences(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_3'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_4'), xsd.String()), ]), xsd.Choice([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_5'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_6'), xsd.String()), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_5'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_6'), xsd.String()), ]) ]) ]) )) assert custom_type.signature() == ( '{http://tests.python-zeep.org/}authentication(item_1: xsd:string, item_2: xsd:string, item_3: xsd:string, item_4: xsd:string, ({item_5: xsd:string} | {item_6: xsd:string} | {item_5: xsd:string, item_6: xsd:string}))' ) def test_signature_nested_sequences_multiple(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.String()), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_3'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_4'), xsd.String()), ]), xsd.Choice([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_5'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_6'), xsd.String()), xsd.Sequence([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_5'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_6'), xsd.String()), ]) ], min_occurs=2, max_occurs=3) ]) )) assert custom_type.signature() == ( '{http://tests.python-zeep.org/}authentication(item_1: xsd:string, item_2: xsd:string, item_3: xsd:string, item_4: xsd:string, ({item_5: xsd:string} | {item_6: xsd:string} | {item_5: xsd:string, item_6: xsd:string})[])' ) def test_signature_complex_type_any(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Choice([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Any() ]) )) assert custom_type.signature() == '{http://tests.python-zeep.org/}authentication(({item_1: xsd:string} | {_value_1: ANY}))' custom_type(item_1='foo') def test_signature_complex_type_sequence_with_any(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Choice([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.ComplexType( xsd.Sequence([ xsd.Any() ]) ) ) ]) )) assert custom_type.signature() == ( '{http://tests.python-zeep.org/}authentication(({item_1: xsd:string} | {item_2: {_value_1: ANY}}))') def test_signature_complex_type_sequence_with_anys(): custom_type = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), xsd.ComplexType( xsd.Choice([ xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_1'), xsd.String()), xsd.Element( etree.QName('http://tests.python-zeep.org/', 'item_2'), xsd.ComplexType( xsd.Sequence([ xsd.Any(), xsd.Any(), ]) ) ) ]) )) assert custom_type.signature() == ( '{http://tests.python-zeep.org/}authentication(' + '({item_1: xsd:string} | {item_2: {_value_1: ANY, _value_2: ANY}})' + ')') def test_schema_recursive_ref(): schema = xsd.Schema(load_xml(""" """)) elm = schema.get_element('ns0:Container') elm.signature(schema) zeep-2.5.0/tests/test_xsd_simple_types.py0000644000076500000240000001426313224161177022627 0ustar mvantellingenstaff00000000000000from lxml import etree from tests.utils import assert_nodes_equal, load_xml from zeep import xsd def test_simple_type(): schema = xsd.Schema(load_xml(""" """)) item_cls = schema.get_element('{http://tests.python-zeep.org/}item') item = item_cls(something=12345678901234567890) node = etree.Element('document') item_cls.render(node, item) expected = """ 12345678901234567890 """ assert_nodes_equal(expected, node) item = item_cls.parse(node.getchildren()[0], schema) assert item.something == 12345678901234567890 def test_simple_type_optional(): schema = xsd.Schema(load_xml(""" """)) item_cls = schema.get_element('{http://tests.python-zeep.org/}item') item = item_cls() assert item.something is None node = etree.Element('document') item_cls.render(node, item) expected = """ """ assert_nodes_equal(expected, node) item = item_cls.parse(node.getchildren()[0], schema) assert item.something is None def test_restriction_global(): schema = xsd.Schema(load_xml(""" """)) type_cls = schema.get_type('{http://tests.python-zeep.org/}foo') assert type_cls.qname.text == '{http://tests.python-zeep.org/}foo' def test_restriction_anon(): schema = xsd.Schema(load_xml(""" """)) element_cls = schema.get_element('{http://tests.python-zeep.org/}something') assert element_cls.type.qname == etree.QName( '{http://tests.python-zeep.org/}something') obj = element_cls(75) node = etree.Element('document') element_cls.render(node, obj) expected = """ 75 """ assert_nodes_equal(expected, node) def test_simple_type_list(): schema = xsd.Schema(load_xml(""" """)) element_cls = schema.get_element('{http://tests.python-zeep.org/}something') obj = element_cls([1, 2, 3]) assert obj == [1, 2, 3] node = etree.Element('document') element_cls.render(node, obj) expected = """ 1 2 3 """ assert_nodes_equal(expected, node) def test_simple_type_list_custom_type(): schema = xsd.Schema(load_xml(""" """)) element_cls = schema.get_element('{http://tests.python-zeep.org/}something') obj = element_cls(['Code', 'City']) assert obj == ['Code', 'City'] node = etree.Element('document') element_cls.render(node, obj) expected = """ Code City """ assert_nodes_equal(expected, node) zeep-2.5.0/tests/test_xsd_types.py0000644000076500000240000000307013224161177021250 0ustar mvantellingenstaff00000000000000import pytest import six from lxml import etree from zeep.xsd import types def test_base_type(): # Basically just for coverage... ;-) base = types.Type() with pytest.raises(NotImplementedError): base.accept('x') with pytest.raises(NotImplementedError): base.parse_xmlelement(None) with pytest.raises(NotImplementedError): base.parsexml(None) with pytest.raises(NotImplementedError): base.render(None, None) with pytest.raises(NotImplementedError): base.resolve() base.signature() == '' def test_simpletype_eq(): type_1 = types.AnySimpleType() type_2 = types.AnySimpleType() assert type_1 == type_2 def test_simpletype_parse(): node = etree.Element('foobar') item = types.AnySimpleType() assert item.parse_xmlelement(node) is None def test_simpletype_pythonvalue(): item = types.AnySimpleType() with pytest.raises(NotImplementedError): item.pythonvalue(None) def test_simpletype_call_wrong_arg_count(): item = types.AnySimpleType() with pytest.raises(TypeError): item('foo', 'bar') def test_simpletype_call_wrong_kwarg(): item = types.AnySimpleType() with pytest.raises(TypeError): item(uhhh='x') def test_simpletype_str(): item = types.AnySimpleType() item.name = u'foobar' assert six.text_type(item) == 'AnySimpleType(value)' def test_complextype_parse_xmlelement_no_childs(): xmlelement = etree.Element('foobar') item = types.ComplexType() assert item.parse_xmlelement(xmlelement, None) is None zeep-2.5.0/tests/test_xsd_union.py0000644000076500000240000000552613224161177021244 0ustar mvantellingenstaff00000000000000from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import xsd def test_union_same_types(): schema = xsd.Schema(load_xml(""" """)) elm = schema.get_element('ns0:item') node = render_node(elm, '102018') expected = """ 102018 """ assert_nodes_equal(expected, node) value = elm.parse(node.getchildren()[0], schema) assert value == 102018 def test_union_mixed(): schema = xsd.Schema(load_xml(""" """)) elm = schema.get_element('ns0:item') node = render_node(elm, '102018') expected = """ 102018 """ assert_nodes_equal(expected, node) value = elm.parse(node.getchildren()[0], schema) assert value == '102018' node = render_node(elm, '2018') expected = """ 2018 """ assert_nodes_equal(expected, node) value = elm.parse(node.getchildren()[0], schema) assert value == '2018' zeep-2.5.0/tests/test_xsd_validation.py0000644000076500000240000000511413224161177022237 0ustar mvantellingenstaff00000000000000import pytest from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import exceptions, xsd def test_validate_element_value(): schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') container_elm = schema.get_element('tns:container') obj = container_elm() expected = """ bar """ with pytest.raises(exceptions.ValidationError) as exc: result = render_node(container_elm, obj) assert 'Missing element item (container.item)' in str(exc) obj.item = 'bar' result = render_node(container_elm, obj) assert_nodes_equal(result, expected) obj = container_elm.parse(result[0], schema) assert obj.item == 'bar' def test_validate_required_attribute(): schema = xsd.Schema(load_xml(""" """)) schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') container_elm = schema.get_element('tns:container') obj = container_elm() expected = """ """ with pytest.raises(exceptions.ValidationError) as exc: result = render_node(container_elm, obj) assert 'The attribute item is not valid: Value is required (container.item)' in str(exc) obj.item = 'bar' result = render_node(container_elm, obj) assert_nodes_equal(result, expected) obj = container_elm.parse(result[0], schema) assert obj.item == 'bar' zeep-2.5.0/tests/test_xsd_valueobjects.py0000644000076500000240000002756413224161177022610 0ustar mvantellingenstaff00000000000000import pickle import pytest import six from lxml.etree import QName from zeep import xsd from zeep.xsd import valueobjects def test_simple_args(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ])) args = tuple(['value-1', 'value-2']) kwargs = {} result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { 'item_1': 'value-1', 'item_2': 'value-2', } def test_simple_args_attributes(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ]), [ xsd.Attribute('attr_1', xsd.String()) ] ) args = tuple(['value-1', 'value-2', 'bla']) kwargs = {} result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { 'item_1': 'value-1', 'item_2': 'value-2', 'attr_1': 'bla', } def test_simple_args_attributes_as_kwargs(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ]), [ xsd.Attribute('attr_1', xsd.String()) ] ) args = tuple(['value-1', 'value-2']) kwargs = {'attr_1': 'bla'} result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { 'item_1': 'value-1', 'item_2': 'value-2', 'attr_1': 'bla', } def test_simple_args_too_many(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ])) args = tuple(['value-1', 'value-2', 'value-3']) kwargs = {} try: valueobjects._process_signature(xsd_type, args, kwargs) except TypeError as exc: assert six.text_type(exc) == ( '__init__() takes at most 2 positional arguments (3 given)') else: assert False, "TypeError not raised" def test_simple_args_too_few(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ])) args = tuple(['value-1']) kwargs = {} valueobjects._process_signature(xsd_type, args, kwargs) def test_simple_kwargs(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ])) args = tuple([]) kwargs = {'item_1': 'value-1', 'item_2': 'value-2'} result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { 'item_1': 'value-1', 'item_2': 'value-2', } def test_simple_mixed(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ])) args = tuple(['value-1']) kwargs = {'item_2': 'value-2'} result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { 'item_1': 'value-1', 'item_2': 'value-2', } def test_choice(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ]) ]) ) args = tuple([]) kwargs = {'item_2': 'value-2'} result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == {'item_1': None, 'item_2': 'value-2'} def test_choice_max_occurs_simple_interface(): fields = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ], max_occurs=2) ]) ) args = tuple([]) kwargs = { '_value_1': [{'item_1': 'foo'}, {'item_2': 'bar'}] } result = valueobjects._process_signature(fields, args, kwargs) assert result == { '_value_1': [ {'item_1': 'foo'}, {'item_2': 'bar'}, ] } def test_choice_max_occurs(): fields = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ], max_occurs=3) ]) ) args = tuple([]) kwargs = { '_value_1': [ {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'bla'} ] } result = valueobjects._process_signature(fields, args, kwargs) assert result == { '_value_1': [ {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'bla'}, ] } def test_choice_max_occurs_on_choice(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Element('item_1', xsd.String(), max_occurs=2), xsd.Element('item_2', xsd.String()) ], max_occurs=2) ]) ) args = tuple([]) kwargs = { '_value_1': [ {'item_1': ['foo', 'bar']}, {'item_2': 'bla'}, ] } result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { '_value_1': [ {'item_1': ['foo', 'bar']}, {'item_2': 'bla'} ] } def test_choice_mixed(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()), ]), xsd.Element('item_2', xsd.String()) ]), qname=QName('http://tests.python-zeep.org', 'container') ) expected = '{http://tests.python-zeep.org}container(({item_1: xsd:string} | {item_2: xsd:string}), item_2__1: xsd:string)' assert xsd_type.signature() == expected args = tuple([]) kwargs = {'item_1': 'value-1', 'item_2__1': 'value-2'} result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { 'item_1': 'value-1', 'item_2': None, 'item_2__1': 'value-2', } def test_choice_sequences_simple(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ]), xsd.Sequence([ xsd.Element('item_3', xsd.String()), xsd.Element('item_4', xsd.String()) ]), ]) ]) ) args = tuple([]) kwargs = {'item_1': 'value-1', 'item_2': 'value-2'} result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { 'item_1': 'value-1', 'item_2': 'value-2', 'item_3': None, 'item_4': None, } def test_choice_sequences_no_match(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ]), xsd.Sequence([ xsd.Element('item_3', xsd.String()), xsd.Element('item_4', xsd.String()) ]), ]) ]) ) args = tuple([]) with pytest.raises(TypeError): kwargs = {'item_1': 'value-1', 'item_3': 'value-3'} valueobjects._process_signature(xsd_type, args, kwargs) def test_choice_sequences_no_match_last(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ]), xsd.Sequence([ xsd.Element('item_3', xsd.String()), xsd.Element('item_4', xsd.String()) ]), ]) ]) ) args = tuple([]) with pytest.raises(TypeError): kwargs = {'item_2': 'value-2', 'item_4': 'value-4'} valueobjects._process_signature(xsd_type, args, kwargs) def test_choice_sequences_no_match_nested(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ]), ]) ]) ) args = tuple([]) kwargs = {'item_1': 'value-1'} value = valueobjects._process_signature(xsd_type, args, kwargs) assert value == { 'item_1': 'value-1', 'item_2': None, } def test_choice_sequences_optional_elms(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String(), min_occurs=0) ]), xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()), xsd.Element('item_3', xsd.String()) ]), ]) ]) ) args = tuple([]) kwargs = {'item_1': 'value-1'} result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { 'item_1': 'value-1', 'item_2': None, 'item_3': None, } def test_choice_sequences_max_occur(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ]), xsd.Sequence([ xsd.Element('item_2', xsd.String()), xsd.Element('item_3', xsd.String()), ]), ], max_occurs=2) ]), ) args = tuple([]) kwargs = { '_value_1': [ {'item_1': 'value-1', 'item_2': 'value-2'}, {'item_2': 'value-2', 'item_3': 'value-3'}, ] } result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { '_value_1': [ {'item_1': 'value-1', 'item_2': 'value-2'}, {'item_2': 'value-2', 'item_3': 'value-3'}, ] } def test_choice_sequences_init_dict(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Choice([ xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ]), xsd.Sequence([ xsd.Element('item_2', xsd.String()), xsd.Element('item_3', xsd.String()), ]), ], max_occurs=2) ]), ) args = tuple([]) kwargs = { '_value_1': {'item_1': 'value-1', 'item_2': 'value-2'}, } result = valueobjects._process_signature(xsd_type, args, kwargs) assert result == { '_value_1': [ {'item_1': 'value-1', 'item_2': 'value-2'} ] } def test_pickle(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ])) obj = xsd_type(item_1='x', item_2='y') data = pickle.dumps(obj) obj_rt = pickle.loads(data) assert obj.item_1 == 'x' assert obj.item_2 == 'y' assert obj_rt.item_1 == 'x' assert obj_rt.item_2 == 'y' def test_json(): xsd_type = xsd.ComplexType( xsd.Sequence([ xsd.Element('item_1', xsd.String()), xsd.Element('item_2', xsd.String()) ])) obj = xsd_type(item_1='x', item_2='y') assert obj.__json__() == { 'item_1': 'x', 'item_2': 'y', } zeep-2.5.0/tests/test_xsd_visitor.py0000644000076500000240000004653413224161177021617 0ustar mvantellingenstaff00000000000000import pytest from lxml import etree from tests.utils import assert_nodes_equal, load_xml, render_node from zeep import exceptions, xsd from zeep.xsd.schema import Schema def parse_schema_node(node): schema = Schema( node=node, transport=None, location=None) return schema def test_schema_empty(): node = load_xml(""" """) schema = parse_schema_node(node) root = schema._get_schema_documents('http://tests.python-zeep.org/')[0] assert root._element_form == 'qualified' assert root._attribute_form == 'unqualified' def test_element_simle_types(): node = load_xml(""" """) schema = parse_schema_node(node) assert schema.get_element('{http://tests.python-zeep.org/}foo') assert schema.get_element('{http://tests.python-zeep.org/}bar') def test_element_simple_type_annotation(): node = load_xml(""" HOI! """) schema = parse_schema_node(node) element = schema.get_element('{http://tests.python-zeep.org/}foo') assert element def test_element_default_type(): node = load_xml(""" """) schema = parse_schema_node(node) element = schema.get_element('{http://tests.python-zeep.org/}foo') assert isinstance(element.type, xsd.AnyType) def test_element_simple_type_unresolved(): node = load_xml(""" HOI! """) schema = parse_schema_node(node) assert schema.get_type('{http://tests.python-zeep.org/}unresolved') def test_element_max_occurs(): node = load_xml(""" """) schema = parse_schema_node(node) elm = schema.get_element('{http://tests.python-zeep.org/}container') elements = dict(elm.type.elements) assert isinstance(elements['e1'], xsd.Element) assert elements['e1'].max_occurs == 1 assert isinstance(elements['e2'], xsd.Element) assert elements['e2'].max_occurs == 1 assert isinstance(elements['e3'], xsd.Element) assert elements['e3'].max_occurs == 2 assert isinstance(elements['e4'], xsd.Element) assert elements['e4'].max_occurs == 'unbounded' def test_simple_content(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_type = schema.get_type('{http://tests.python-zeep.org/}container') assert xsd_type(10, sizing='qwe') def test_attribute_optional(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_element('{http://tests.python-zeep.org/}foo') value = xsd_element() node = render_node(xsd_element, value) expected = """ """ assert_nodes_equal(expected, node) def test_attribute_required(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_element('{http://tests.python-zeep.org/}foo') value = xsd_element() with pytest.raises(exceptions.ValidationError): node = render_node(xsd_element, value) value.base = 'foo' node = render_node(xsd_element, value) expected = """ """ assert_nodes_equal(expected, node) def test_attribute_default(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_element('{http://tests.python-zeep.org/}foo') value = xsd_element() node = render_node(xsd_element, value) expected = """ """ assert_nodes_equal(expected, node) def test_attribute_simple_type(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_element('{http://tests.python-zeep.org/}foo') assert xsd_element(bar='hoi') def test_attribute_any_type(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_element('{http://tests.python-zeep.org/}foo') value = xsd_element(base='hoi') node = render_node(xsd_element, value) expected = """ """ assert_nodes_equal(expected, node) def test_complex_content_mixed(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_element('{http://tests.python-zeep.org/}foo') result = xsd_element('basetype', bar='hoi') node = etree.Element('document') xsd_element.render(node, result) expected = """ basetype """ assert_nodes_equal(expected, node) def test_complex_content_extension(): node = load_xml(""" """) schema = parse_schema_node(node) record_type = schema.get_type('{http://tests.python-zeep.org/}SubType1') assert len(record_type.attributes) == 2 assert len(record_type.elements) == 1 record_type = schema.get_type('{http://tests.python-zeep.org/}SubType2') assert len(record_type.attributes) == 3 assert len(record_type.elements) == 1 xsd_element = schema.get_element('{http://tests.python-zeep.org/}test') xsd_type = schema.get_type('{http://tests.python-zeep.org/}SubType2') value = xsd_type(attr_a='a', attr_b='b', attr_c='c') node = render_node(xsd_element, value) expected = """ """ assert_nodes_equal(expected, node) def test_simple_content_extension(): node = load_xml(""" """) schema = parse_schema_node(node) record_type = schema.get_type('{http://tests.python-zeep.org/}SubType1') assert len(record_type.attributes) == 2 assert len(record_type.elements) == 1 record_type = schema.get_type('{http://tests.python-zeep.org/}SubType2') assert len(record_type.attributes) == 3 assert len(record_type.elements) == 1 def test_list_type(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_element( '{http://tests.python-zeep.org/}foo') value = xsd_element(arg=[1, 2, 3, 4, 5]) node = render_node(xsd_element, value) expected = """ 1 2 3 4 5 """ assert_nodes_equal(expected, node) def test_list_type_unresolved(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_element( '{http://tests.python-zeep.org/}foo') value = xsd_element(arg=[1, 2, 3, 4, 5]) node = render_node(xsd_element, value) expected = """ 1 2 3 4 5 """ assert_nodes_equal(expected, node) def test_list_type_simple_type(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_element( '{http://tests.python-zeep.org/}foo') value = xsd_element(arg=[1, 2, 3, 4, 5]) node = render_node(xsd_element, value) expected = """ 1 2 3 4 5 """ assert_nodes_equal(expected, node) def test_union_type(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_element('{http://tests.python-zeep.org/}foo') assert xsd_element(arg='hoi') def test_simple_type_restriction(): node = load_xml(""" """) schema = parse_schema_node(node) xsd_element = schema.get_type('{http://tests.python-zeep.org/}type_3') assert xsd_element(100) == '100' zeep-2.5.0/tests/utils.py0000644000076500000240000000223213224161177017326 0ustar mvantellingenstaff00000000000000import six from lxml import etree from six import binary_type, string_types def load_xml(xml): parser = etree.XMLParser( remove_blank_text=True, remove_comments=True, resolve_entities=False) return etree.fromstring(xml.strip(), parser=parser) def assert_nodes_equal(result, expected): def _convert_node(node): if isinstance(node, (string_types, binary_type)): return load_xml(node) return node # assert node_1 == node_2 result = etree.tostring(_convert_node(result), pretty_print=True) expected = etree.tostring(_convert_node(expected), pretty_print=True) if six.PY3: result = result.decode('utf-8') expected = expected.decode('utf-8') assert result == expected def render_node(element, value): node = etree.Element('document') element.render(node, value) return node class DummyTransport(object): def __init__(self): self._items = {} def bind(self, url, node): self._items[url] = node def load(self, url): data = self._items[url] if isinstance(data, string_types): return data return etree.tostring(data) zeep-2.5.0/tests/wsdl_files/0000755000076500000240000000000013224162010017733 5ustar mvantellingenstaff00000000000000zeep-2.5.0/tests/wsdl_files/claim.wsdl0000644000076500000240000000422513224161177021733 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/tests/wsdl_files/http.wsdl0000644000076500000240000000437113224161177021627 0ustar mvantellingenstaff00000000000000 My first service zeep-2.5.0/tests/wsdl_files/soap-enc.xsd0000644000076500000240000004435313224161177022206 0ustar mvantellingenstaff00000000000000 'root' can be used to distinguish serialization roots from other elements that are present in a serialization but are not roots of a serialized value graph Attributes common to all elements that function as accessors or represent independent (multi-ref) values. The href attribute is intended to be used in a manner like CONREF. That is, the element content should be empty iff the href attribute appears 'Array' is a complex type for accessors identified by position zeep-2.5.0/tests/wsdl_files/soap.wsdl0000644000076500000240000001053113224161177021605 0ustar mvantellingenstaff00000000000000 My first service zeep-2.5.0/tests/wsdl_files/soap_header.wsdl0000644000076500000240000000623513224161177023123 0ustar mvantellingenstaff00000000000000 My first service zeep-2.5.0/tests/wsdl_files/soap_import_2.wsdl0000644000076500000240000000331613224161177023423 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/tests/wsdl_files/soap_import_main.wsdl0000644000076500000240000000247413224161177024212 0ustar mvantellingenstaff00000000000000 My first service zeep-2.5.0/tests/wsdl_files/soap_transport_err.wsdl0000644000076500000240000001066013224161177024574 0ustar mvantellingenstaff00000000000000 My first service zeep-2.5.0/tests/wsdl_files/test_import_2.xsd0000644000076500000240000000325213224161177023264 0ustar mvantellingenstaff00000000000000 zeep-2.5.0/tests/wsdl_files/xmldsig-core-schema.xsd0000644000076500000240000002342413224161177024330 0ustar mvantellingenstaff00000000000000