diff -Nru python-zeep-3.1.0/CHANGES python-zeep-3.2.0/CHANGES --- python-zeep-3.1.0/CHANGES 2018-07-28 07:49:57.000000000 +0000 +++ python-zeep-3.2.0/CHANGES 2018-12-17 08:47:28.000000000 +0000 @@ -1,3 +1,15 @@ +3.2.0 (2018-12-17) +------------------ + - Fix abstract message check for NoneType before attempting to access parts + - Improve support for 'Chameleon' XSD schemas (#879, #888) + - Fix resolving qualified references (#879) + - Fix issue with duplicate soap:body tags when multiple parts used (#879) + - Fix Choice with unbound Any element (#871) + - Add xsd_ignore_sequence_order flag (#880) + - Add support for timestamp token in WSS headers (#817) + - Accept strings for xsd.DateTime (#886) + + 3.1.0 (2018-07-28) ------------------ - Fix SSL issue on with `TornadoAsyncTransport` (#792) diff -Nru python-zeep-3.1.0/debian/changelog python-zeep-3.2.0/debian/changelog --- python-zeep-3.1.0/debian/changelog 2018-11-22 16:20:02.000000000 +0000 +++ python-zeep-3.2.0/debian/changelog 2019-01-09 18:02:54.000000000 +0000 @@ -1,3 +1,12 @@ +python-zeep (3.2.0-1) unstable; urgency=medium + + * Move source.lintian-overrides to source/lintian-overrides. + * Merging upstream version 3.2.0. + * Refresh debian/patches/01-remove-failing-tests.patch. + * Update to Standards-Version: 4.3.0. + + -- Mathias Behrle Wed, 09 Jan 2019 19:02:54 +0100 + python-zeep (3.1.0-1) unstable; urgency=medium * Updating to standards version 4.2.1, no changes needed. diff -Nru python-zeep-3.1.0/debian/control python-zeep-3.2.0/debian/control --- python-zeep-3.1.0/debian/control 2018-11-22 14:57:27.000000000 +0000 +++ python-zeep-3.2.0/debian/control 2019-01-09 18:00:26.000000000 +0000 @@ -50,7 +50,7 @@ python3-setuptools, python3-six (>= 1.9.0-3~), python3-tz -Standards-Version: 4.2.1 +Standards-Version: 4.3.0 Vcs-Browser: https://salsa.debian.org/tryton-team/python-zeep Vcs-Git: https://salsa.debian.org/tryton-team/python-zeep.git Homepage: https://github.com/mvantellingen/python-zeep diff -Nru python-zeep-3.1.0/debian/patches/01-remove-failing-tests.patch python-zeep-3.2.0/debian/patches/01-remove-failing-tests.patch --- python-zeep-3.1.0/debian/patches/01-remove-failing-tests.patch 2018-11-22 15:02:51.000000000 +0000 +++ python-zeep-3.2.0/debian/patches/01-remove-failing-tests.patch 2019-01-09 17:55:25.000000000 +0000 @@ -12,7 +12,7 @@ --- a/tests/test_wsse_signature.py +++ /dev/null -@@ -1,112 +0,0 @@ +@@ -1,137 +0,0 @@ -import os -import sys - @@ -31,9 +31,14 @@ -KEY_FILE_PW = os.path.join( - os.path.dirname(os.path.realpath(__file__)), 'cert_valid_pw.pem') - +-# Check whether xmlsec library has been installed +-from zeep.wsse.signature import xmlsec as xmlsec_installed +-skip_if_no_xmlsec = pytest.mark.skipif(sys.platform == 'win32', +- reason="does not run on windows") and \ +- pytest.mark.skipif(xmlsec_installed is None, +- reason="xmlsec library not installed") - --@pytest.mark.skipif(sys.platform == 'win32', -- reason="does not run on windows") +-@skip_if_no_xmlsec -def test_sign(): - envelope = load_xml(""" - +- +- +- +- OK +- +- +- +- """) +- +- plugin = wsse.BinarySignature(KEY_FILE_PW, KEY_FILE_PW, 'geheim') +- envelope, headers = plugin.apply(envelope, {}) +- plugin.verify(envelope) --- a/tests/test_asyncio_transport.py +++ /dev/null @@ -1,100 +0,0 @@ @@ -242,6 +267,22 @@ -from zeep.tornado import TornadoAsyncTransport - - +-@pytest.mark.requests +-@patch('tornado.httpclient.HTTPClient.fetch') +-def test_tornado_load(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' +- +- -class TornadoAsyncTransportTest(AsyncTestCase): - @pytest.mark.requests - def test_no_cache(self): @@ -249,22 +290,6 @@ - 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): diff -Nru python-zeep-3.1.0/debian/source/lintian-overrides python-zeep-3.2.0/debian/source/lintian-overrides --- python-zeep-3.1.0/debian/source/lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ python-zeep-3.2.0/debian/source/lintian-overrides 2019-01-09 17:50:23.000000000 +0000 @@ -0,0 +1,4 @@ +# Missing test files in the tarball on pypi, but present in the +# github repository need to be mentioned in copyright. +python-zeep source: wildcard-matches-nothing-in-dep5-copyright +python-zeep source: unused-file-paragraph-in-dep5-copyright diff -Nru python-zeep-3.1.0/debian/source.lintian-overrides python-zeep-3.2.0/debian/source.lintian-overrides --- python-zeep-3.1.0/debian/source.lintian-overrides 2018-11-22 14:47:21.000000000 +0000 +++ python-zeep-3.2.0/debian/source.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -# Missing test files in the tarball on pypi, but present in the -# github repository need to be mentioned in copyright. -python-zeep source: wildcard-matches-nothing-in-dep5-copyright -python-zeep source: unused-file-paragraph-in-dep5-copyright diff -Nru python-zeep-3.1.0/PKG-INFO python-zeep-3.2.0/PKG-INFO --- python-zeep-3.1.0/PKG-INFO 2018-07-28 07:50:37.000000000 +0000 +++ python-zeep-3.2.0/PKG-INFO 2018-12-17 09:05:53.000000000 +0000 @@ -1,6 +1,6 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: zeep -Version: 3.1.0 +Version: 3.2.0 Summary: A modern/fast Python SOAP client based on lxml / requests Home-page: http://docs.python-zeep.org Author: Michael van Tellingen @@ -13,7 +13,7 @@ A fast and modern Python SOAP client Highlights: - * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6 and PyPy + * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6, 3.7 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 @@ -62,10 +62,7 @@ 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) + Please only report bugs and not support requests to the GitHub issue tracker. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable @@ -76,5 +73,11 @@ Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy +Provides-Extra: async +Provides-Extra: tornado +Provides-Extra: docs +Provides-Extra: xmlsec +Provides-Extra: test diff -Nru python-zeep-3.1.0/README.rst python-zeep-3.2.0/README.rst --- python-zeep-3.1.0/README.rst 2017-11-04 08:15:21.000000000 +0000 +++ python-zeep-3.2.0/README.rst 2018-12-17 08:52:45.000000000 +0000 @@ -5,7 +5,7 @@ A fast and modern Python SOAP client Highlights: - * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6 and PyPy + * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6, 3.7 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 @@ -77,7 +77,4 @@ 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) +Please only report bugs and not support requests to the GitHub issue tracker. diff -Nru python-zeep-3.1.0/setup.cfg python-zeep-3.2.0/setup.cfg --- python-zeep-3.1.0/setup.cfg 2018-07-28 07:50:37.000000000 +0000 +++ python-zeep-3.2.0/setup.cfg 2018-12-17 09:05:53.000000000 +0000 @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.1.0 +current_version = 3.2.0 commit = true tag = true tag_name = {new_version} diff -Nru python-zeep-3.1.0/setup.py python-zeep-3.2.0/setup.py --- python-zeep-3.1.0/setup.py 2018-07-28 07:50:09.000000000 +0000 +++ python-zeep-3.2.0/setup.py 2018-12-17 08:59:37.000000000 +0000 @@ -59,7 +59,7 @@ setup( name='zeep', - version='3.1.0', + version='3.2.0', description='A modern/fast Python SOAP client based on lxml / requests', long_description=long_description, author="Michael van Tellingen", @@ -89,6 +89,7 @@ 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], diff -Nru python-zeep-3.1.0/src/zeep/asyncio/transport.py python-zeep-3.2.0/src/zeep/asyncio/transport.py --- python-zeep-3.1.0/src/zeep/asyncio/transport.py 2018-06-16 07:52:47.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/asyncio/transport.py 2018-12-03 16:52:42.000000000 +0000 @@ -31,7 +31,7 @@ ] def __init__(self, loop, cache=None, timeout=300, operation_timeout=None, - session=None): + session=None, verify_ssl=True, proxy=None): self.loop = loop if loop else asyncio.get_event_loop() self.cache = cache @@ -39,6 +39,8 @@ self.operation_timeout = operation_timeout self.logger = logging.getLogger(__name__) + self.verify_ssl = verify_ssl + self.proxy = proxy self.session = session or aiohttp.ClientSession(loop=self.loop) self._close_session = session is None self.session._default_headers['User-Agent'] = ( @@ -81,7 +83,8 @@ self.logger.debug("HTTP Post to %s:\n%s", address, message) with aio_timeout(self.operation_timeout): response = await self.session.post( - address, data=message, headers=headers) + address, data=message, headers=headers, + verify_ssl=self.verify_ssl, proxy=self.proxy) self.logger.debug( "HTTP Response from %s (status: %d):\n%s", address, response.status, await response.read()) @@ -95,7 +98,8 @@ async def get(self, address, params, headers): with aio_timeout(self.operation_timeout): response = await self.session.get( - address, params=params, headers=headers) + address, params=params, headers=headers, + verify_ssl=self.verify_ssl, proxy=self.proxy) return await self.new_response(response) diff -Nru python-zeep-3.1.0/src/zeep/__init__.py python-zeep-3.2.0/src/zeep/__init__.py --- python-zeep-3.1.0/src/zeep/__init__.py 2018-07-28 07:50:09.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/__init__.py 2018-12-17 08:59:27.000000000 +0000 @@ -4,4 +4,4 @@ from zeep.settings import Settings # noqa from zeep.xsd.valueobjects import AnyObject # noqa -__version__ = '3.1.0' +__version__ = '3.2.0' diff -Nru python-zeep-3.1.0/src/zeep/proxy.py python-zeep-3.2.0/src/zeep/proxy.py --- python-zeep-3.1.0/src/zeep/proxy.py 2018-06-16 07:53:25.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/proxy.py 2018-12-06 16:41:39.000000000 +0000 @@ -70,6 +70,10 @@ except KeyError: raise AttributeError('Service has no operation %r' % key) + def __iter__(self): + """ Return iterator over the services and their callables. """ + return iter(self._operations.items()) + def __dir__(self): """ Return the names of the operations. """ return list(itertools.chain( diff -Nru python-zeep-3.1.0/src/zeep/settings.py python-zeep-3.2.0/src/zeep/settings.py --- python-zeep-3.1.0/src/zeep/settings.py 2018-06-16 07:53:25.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/settings.py 2018-12-07 20:38:28.000000000 +0000 @@ -34,6 +34,10 @@ approach to add http headers for specific calls. :type extra_headers: list + :param xsd_ignore_sequence_order: boolean to indicate whether to enforce sequence + order when parsing complex types. This is a workaround for servers that + don't respect sequence order. + :type xsd_ignore_sequence_order: boolean """ strict = attr.ib(default=True) raw_response = attr.ib(default=False) @@ -48,6 +52,9 @@ forbid_entities = attr.ib(default=True) forbid_external = attr.ib(default=True) + # xsd workarounds + xsd_ignore_sequence_order = attr.ib(default=False) + _tls = attr.ib(default=attr.Factory(threading.local)) @contextmanager diff -Nru python-zeep-3.1.0/src/zeep/tornado/transport.py python-zeep-3.2.0/src/zeep/tornado/transport.py --- python-zeep-3.1.0/src/zeep/tornado/transport.py 2018-07-28 06:21:16.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/tornado/transport.py 2018-11-18 10:58:12.000000000 +0000 @@ -124,11 +124,12 @@ kwargs['body'] = message http_req = httpclient.HTTPRequest(address, **kwargs) - response = yield async_client.fetch(http_req) + response = yield async_client.fetch(http_req, raise_error=False) raise gen.Return(self.new_response(response)) - def new_response(self, response): + @staticmethod + def new_response(response): """Convert an tornado.HTTPResponse object to a requests.Response object""" new = Response() new._content = response.body diff -Nru python-zeep-3.1.0/src/zeep/transports.py python-zeep-3.2.0/src/zeep/transports.py --- python-zeep-3.1.0/src/zeep/transports.py 2018-07-28 07:46:50.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/transports.py 2018-11-18 10:17:39.000000000 +0000 @@ -75,7 +75,7 @@ else: log_message = response.content if isinstance(log_message, bytes): - log_message = log_message.decode('utf-8') + log_message = log_message.decode(response.encoding or 'utf-8') self.logger.debug( "HTTP Response from %s (status: %d):\n%s", diff -Nru python-zeep-3.1.0/src/zeep/utils.py python-zeep-3.2.0/src/zeep/utils.py --- python-zeep-3.1.0/src/zeep/utils.py 2018-05-13 08:13:32.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/utils.py 2018-12-17 08:25:21.000000000 +0000 @@ -86,4 +86,4 @@ 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 + return main_value.lower() diff -Nru python-zeep-3.1.0/src/zeep/wsdl/messages/soap.py python-zeep-3.2.0/src/zeep/wsdl/messages/soap.py --- python-zeep-3.1.0/src/zeep/wsdl/messages/soap.py 2018-06-16 09:27:54.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/wsdl/messages/soap.py 2018-12-07 20:34:15.000000000 +0000 @@ -41,6 +41,7 @@ self.abstract = None # Set during resolve() self.type = type + self._is_body_wrapped = False self.body = None self.header = None self.envelope = None @@ -54,21 +55,30 @@ soap = ElementMaker(namespace=self.nsmap['soap-env'], nsmap=nsmap) + # Create the soap:envelope + envelope = soap.Envelope() + # Create the soap:header element headers_value = kwargs.pop('_soapheaders', None) header = self._serialize_header(headers_value, nsmap) + if header is not None: + envelope.append(header) - # Create the soap:body element - body = soap.Body() + # Create the soap:body element. The _is_body_wrapped attribute signals + # that the self.body element is of type soap:body, so we don't have to + # create it in that case. Otherwise we create a Element soap:body and + # render the content into this. 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) + if self._is_body_wrapped: + self.body.render(envelope, body_value) + else: + body = soap.Body() + envelope.append(body) + self.body.render(body, body_value) + else: + body = soap.Body() + 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 @@ -279,7 +289,7 @@ # 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': + if (abstract_message is None or not abstract_message.parts) and self.type != 'input': return self.abstract = abstract_message @@ -399,7 +409,6 @@ def __init__(self, *args, **kwargs): super(DocumentMessage, self).__init__(*args, **kwargs) - self._is_body_wrapped = False def _deserialize_body(self, xmlelement): diff -Nru python-zeep-3.1.0/src/zeep/wsdl/parse.py python-zeep-3.2.0/src/zeep/wsdl/parse.py --- python-zeep-3.1.0/src/zeep/wsdl/parse.py 2018-06-16 09:13:16.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/wsdl/parse.py 2018-12-17 08:25:21.000000000 +0000 @@ -39,8 +39,8 @@ 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) + part_element = qname_attr(part, 'element') + part_type = qname_attr(part, 'type') try: if part_element is not None: diff -Nru python-zeep-3.1.0/src/zeep/wsse/__init__.py python-zeep-3.2.0/src/zeep/wsse/__init__.py --- python-zeep-3.1.0/src/zeep/wsse/__init__.py 2017-03-12 12:53:30.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/wsse/__init__.py 2018-11-18 09:08:17.000000000 +0000 @@ -1,3 +1,3 @@ from .compose import Compose # noqa -from .signature import Signature # noqa +from .signature import BinarySignature, Signature # noqa from .username import UsernameToken # noqa diff -Nru python-zeep-3.1.0/src/zeep/wsse/signature.py python-zeep-3.2.0/src/zeep/wsse/signature.py --- python-zeep-3.1.0/src/zeep/wsse/signature.py 2017-11-04 08:15:21.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/wsse/signature.py 2018-11-18 10:17:39.000000000 +0000 @@ -71,6 +71,17 @@ _read_file(key_file), _read_file(certfile), password) +class BinarySignature(Signature): + """Sign given SOAP envelope with WSSE sig usign given key file and cert file. + + Place the key information into BinarySecurityElement.""" + + def apply(self, envelope, headers): + key = _make_sign_key(self.key_data, self.cert_data, self.password) + _sign_envelope_with_key_binary(envelope, key) + return envelope, headers + + def check_xmlsec_import(): if xmlsec is None: raise ImportError( @@ -173,7 +184,8 @@ return _sign_envelope_with_key(envelope, key) -def _sign_envelope_with_key(envelope, key): +def _signature_prepare(envelope, key): + """Prepare envelope and sign.""" soap_env = detect_soap_env(envelope) # Create the Signature node. @@ -208,9 +220,31 @@ # the X509 data (because it doesn't understand WSSE). sec_token_ref = etree.SubElement( key_info, QName(ns.WSSE, 'SecurityTokenReference')) + return security, sec_token_ref, x509_data + + +def _sign_envelope_with_key(envelope, key): + _, sec_token_ref, x509_data = _signature_prepare(envelope, key) sec_token_ref.append(x509_data) +def _sign_envelope_with_key_binary(envelope, key): + security, sec_token_ref, x509_data = _signature_prepare(envelope, key) + ref = etree.SubElement(sec_token_ref, QName(ns.WSSE, 'Reference'), + {'ValueType': 'http://docs.oasis-open.org/wss/2004/01/' + 'oasis-200401-wss-x509-token-profile-1.0#X509v3'}) + ref_id = ensure_id(ref) + bintok = etree.Element(QName(ns.WSSE, 'BinarySecurityToken'), { + QName(ns.WSU, 'Id'): ref_id, + 'ValueType': 'http://docs.oasis-open.org/wss/2004/01/' + 'oasis-200401-wss-x509-token-profile-1.0#X509v3', + 'EncodingType': 'http://docs.oasis-open.org/wss/2004/01/' + 'oasis-200401-wss-soap-message-security-1.0#Base64Binary'}) + bintok.text = x509_data.find(QName(ns.DS, 'X509Certificate')).text + security.insert(1, bintok) + x509_data.getparent().remove(x509_data) + + def verify_envelope(envelope, certfile): """Verify WS-Security signature on given SOAP envelope with given cert. diff -Nru python-zeep-3.1.0/src/zeep/wsse/username.py python-zeep-3.2.0/src/zeep/wsse/username.py --- python-zeep-3.1.0/src/zeep/wsse/username.py 2017-09-29 15:15:39.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/wsse/username.py 2018-12-07 20:40:00.000000000 +0000 @@ -38,13 +38,14 @@ 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): + use_digest=False, nonce=None, created=None, timestamp_token=None): self.username = username self.password = password self.password_digest = password_digest self.nonce = nonce self.created = created self.use_digest = use_digest + self.timestamp_token = timestamp_token def apply(self, envelope, headers): security = utils.get_security_header(envelope) @@ -56,6 +57,9 @@ token = utils.WSSE.UsernameToken() security.append(token) + if self.timestamp_token is not None: + security.append(self.timestamp_token) + # Create the sub elements of the UsernameToken element elements = [ utils.WSSE.Username(self.username) diff -Nru python-zeep-3.1.0/src/zeep/xsd/elements/element.py python-zeep-3.2.0/src/zeep/xsd/elements/element.py --- python-zeep-3.1.0/src/zeep/xsd/elements/element.py 2018-06-17 06:56:38.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/xsd/elements/element.py 2018-12-07 20:38:28.000000000 +0000 @@ -159,6 +159,18 @@ item = self.parse( xmlelement, schema, allow_none=True, context=context) result.append(item) + elif ( + schema is not None and + schema.settings.xsd_ignore_sequence_order and + list(filter(lambda elem: etree.QName(elem.tag).localname == self.qname.localname, xmlelements)) + ): + # Search for the field in remaining elements, not only the leftmost + xmlelement = list(filter(lambda elem: etree.QName(elem.tag).localname == self.qname.localname, xmlelements))[0] + xmlelements.remove(xmlelement) + 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 diff -Nru python-zeep-3.1.0/src/zeep/xsd/elements/indicators.py python-zeep-3.2.0/src/zeep/xsd/elements/indicators.py --- python-zeep-3.1.0/src/zeep/xsd/elements/indicators.py 2018-05-11 07:16:20.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/xsd/elements/indicators.py 2018-12-03 16:52:42.000000000 +0000 @@ -426,7 +426,10 @@ result.append(choice_value) break else: - if element.name in value: + if isinstance(element, Any): + result.append(value) + break + elif element.name in value: choice_value = value.get(element.name) result.append({element.name: choice_value}) break @@ -539,7 +542,7 @@ if name is not None: try: choice_value = value[name] - except KeyError: + except (KeyError, TypeError): choice_value = value else: choice_value = value diff -Nru python-zeep-3.1.0/src/zeep/xsd/schema.py python-zeep-3.2.0/src/zeep/xsd/schema.py --- python-zeep-3.1.0/src/zeep/xsd/schema.py 2018-07-28 06:18:55.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/xsd/schema.py 2018-12-17 08:25:21.000000000 +0000 @@ -102,6 +102,13 @@ seen.add(type_.qname) def add_documents(self, schema_nodes, location): + """ + + :type schema_nodes: List[lxml.etree._Element] + :type location: str + :type target_namespace: Optional[str] + + """ resolve_queue = [] for node in schema_nodes: document = self.create_new_document(node, location) @@ -133,6 +140,7 @@ :rtype: zeep.xsd.ComplexType or zeep.xsd.AnySimpleType + """ qname = self._create_qname(qname) try: @@ -191,16 +199,17 @@ return 'soap-env' return namespace - def create_new_document(self, node, url, base_url=None): + def create_new_document(self, node, url, base_url=None, target_namespace=None): """ :rtype: zeep.xsd.schema.SchemaDocument """ namespace = node.get('targetNamespace') if node is not None else None + if not namespace: + namespace = target_namespace if base_url is None: base_url = url - schema = SchemaDocument(namespace, url, base_url) self.documents.add(schema) schema.load(self, node) @@ -377,6 +386,7 @@ self._location = location self._target_namespace = namespace self._is_internal = False + self._has_empty_import = False # Containers for specific types self._attribute_groups = {} diff -Nru python-zeep-3.1.0/src/zeep/xsd/types/builtins.py python-zeep-3.2.0/src/zeep/xsd/types/builtins.py --- python-zeep-3.1.0/src/zeep/xsd/types/builtins.py 2018-05-12 10:09:30.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/xsd/types/builtins.py 2018-12-17 08:25:21.000000000 +0000 @@ -123,6 +123,8 @@ @check_no_collection def xmlvalue(self, value): + if isinstance(value, six.string_types): + return 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 diff -Nru python-zeep-3.1.0/src/zeep/xsd/visitor.py python-zeep-3.2.0/src/zeep/xsd/visitor.py --- python-zeep-3.1.0/src/zeep/xsd/visitor.py 2018-07-28 06:17:11.000000000 +0000 +++ python-zeep-3.2.0/src/zeep/xsd/visitor.py 2018-12-17 08:25:21.000000000 +0000 @@ -91,6 +91,8 @@ if not ref: return + ref = self._create_qname(ref) + if node.tag == tags.element: cls = xsd_elements.RefElement elif node.tag == tags.attribute: @@ -129,7 +131,12 @@ """ assert node is not None - self.document._target_namespace = node.get('targetNamespace') + # A schema should always have a targetNamespace attribute, otherwise + # it is called a chameleon schema. In that case the schema will inherit + # the namespace of the enclosing schema/node. + tns = node.get('targetNamespace') + if tns: + self.document._target_namespace = tns self.document._element_form = node.get('elementFormDefault', 'unqualified') self.document._attribute_form = node.get('attributeFormDefault', 'unqualified') @@ -168,6 +175,16 @@ filename=self._document.location, sourceline=node.sourceline) + # We found an empty statement, this needs to trigger 4.1.2 + # from https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#src-resolve + # for QName resolving. + # In essence this means we will resolve QNames without a namespace to no + # namespace instead of the target namespace. + # The following code snippet works because imports have to occur before we + # visit elements. + if not namespace and not location: + self.document._has_empty_import = True + # 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) @@ -207,7 +224,13 @@ filename=self.document._location, sourceline=node.sourceline) - schema = self.schema.create_new_document(schema_node, location) + # If the imported schema doesn't define a target namespace and the + # node doesn't specify it either then inherit the existing target + # namespace. + elif not schema_tns and not namespace: + namespace = self.document._target_namespace + + schema = self.schema.create_new_document(schema_node, location, target_namespace=namespace) self.register_import(namespace, schema) return schema @@ -1157,6 +1180,9 @@ namespace=name.namespace, schemaLocation=name.namespace) self.visit_import(import_node, None) + if (not name.namespace and self.document._element_form == 'qualified' and + self.document._target_namespace and not self.document._has_empty_import): + name = etree.QName(self.document._target_namespace, name.localname) return name def _pop_annotation(self, items): diff -Nru python-zeep-3.1.0/src/zeep.egg-info/PKG-INFO python-zeep-3.2.0/src/zeep.egg-info/PKG-INFO --- python-zeep-3.1.0/src/zeep.egg-info/PKG-INFO 2018-07-28 07:50:37.000000000 +0000 +++ python-zeep-3.2.0/src/zeep.egg-info/PKG-INFO 2018-12-17 09:05:52.000000000 +0000 @@ -1,6 +1,6 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: zeep -Version: 3.1.0 +Version: 3.2.0 Summary: A modern/fast Python SOAP client based on lxml / requests Home-page: http://docs.python-zeep.org Author: Michael van Tellingen @@ -13,7 +13,7 @@ A fast and modern Python SOAP client Highlights: - * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6 and PyPy + * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6, 3.7 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 @@ -62,10 +62,7 @@ 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) + Please only report bugs and not support requests to the GitHub issue tracker. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable @@ -76,5 +73,11 @@ Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy +Provides-Extra: async +Provides-Extra: tornado +Provides-Extra: docs +Provides-Extra: xmlsec +Provides-Extra: test diff -Nru python-zeep-3.1.0/src/zeep.egg-info/requires.txt python-zeep-3.2.0/src/zeep.egg-info/requires.txt --- python-zeep-3.1.0/src/zeep.egg-info/requires.txt 2018-07-28 07:50:37.000000000 +0000 +++ python-zeep-3.2.0/src/zeep.egg-info/requires.txt 2018-12-17 09:05:52.000000000 +0000 @@ -31,7 +31,7 @@ aioresponses>=0.4.1 [tornado] -tornado>=4.0.2,<5 +tornado<5,>=4.0.2 [xmlsec] xmlsec>=0.6.1 diff -Nru python-zeep-3.1.0/src/zeep.egg-info/SOURCES.txt python-zeep-3.2.0/src/zeep.egg-info/SOURCES.txt --- python-zeep-3.1.0/src/zeep.egg-info/SOURCES.txt 2018-07-28 07:50:37.000000000 +0000 +++ python-zeep-3.2.0/src/zeep.egg-info/SOURCES.txt 2018-12-17 09:05:52.000000000 +0000 @@ -109,6 +109,7 @@ tests/test_wsdl_messages_document.py tests/test_wsdl_messages_http.py tests/test_wsdl_messages_rpc.py +tests/test_wsdl_no_output_message_part.py tests/test_wsdl_soap.py tests/test_wsse_signature.py tests/test_wsse_username.py diff -Nru python-zeep-3.1.0/tests/test_tornado_transport.py python-zeep-3.2.0/tests/test_tornado_transport.py --- python-zeep-3.1.0/tests/test_tornado_transport.py 2017-11-04 08:15:21.000000000 +0000 +++ python-zeep-3.2.0/tests/test_tornado_transport.py 2018-12-03 16:13:14.000000000 +0000 @@ -9,6 +9,22 @@ from zeep.tornado import TornadoAsyncTransport +@pytest.mark.requests +@patch('tornado.httpclient.HTTPClient.fetch') +def test_tornado_load(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' + + class TornadoAsyncTransportTest(AsyncTestCase): @pytest.mark.requests def test_no_cache(self): @@ -16,22 +32,6 @@ 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): diff -Nru python-zeep-3.1.0/tests/test_wsdl_messages_document.py python-zeep-3.2.0/tests/test_wsdl_messages_document.py --- python-zeep-3.1.0/tests/test_wsdl_messages_document.py 2017-11-04 08:15:21.000000000 +0000 +++ python-zeep-3.2.0/tests/test_wsdl_messages_document.py 2018-12-07 20:32:00.000000000 +0000 @@ -332,6 +332,78 @@ assert_nodes_equal(expected, serialized.content) +def test_serialize_multiple_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') + + serialized = operation.input.serialize( + request1={'arg1': 'ah1', 'arg2': 'ah2'}, + request2={'arg1': 'ah1', 'arg2': 'ah2'} + ) + expected = """ + + + + + ah1 + ah2 + + + ah1 + ah2 + + + + """ + assert_nodes_equal(expected, serialized.content) + + def test_serialize_with_header(): wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """) + # parse the content + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + # General assertions on the input + 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' diff -Nru python-zeep-3.1.0/tests/test_wsdl.py python-zeep-3.2.0/tests/test_wsdl.py --- python-zeep-3.1.0/tests/test_wsdl.py 2018-06-16 07:53:25.000000000 +0000 +++ python-zeep-3.2.0/tests/test_wsdl.py 2018-12-17 08:25:21.000000000 +0000 @@ -1034,3 +1034,125 @@ assert headers['Authorization'] == 'Bearer 1234' + +def test_wsdl_no_schema_namespace(): + wsdl_main = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """) + client = stub(settings=Settings(), plugins=[], wsse=None) + + transport = DummyTransport() + document = wsdl.Document(wsdl_main, transport) + binding = document.services.get('CalculatorService').ports.get('ICalculator').binding + + envelope, headers = binding._create( + 'Add', + args=[3, 4], + kwargs={}, + client=client, + options={'address': 'http://tests.python-zeep.org/test'}) + + expected = """ + + + + 3 + 4 + + + + """ + assert_nodes_equal(expected, envelope) + + +def test_namespaced_wsdl_with_empty_import(): + # See https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#src-resolve for details + wsdl_main = StringIO(""" + + + + + + + + + + + + + + + + + + """) + + transport = DummyTransport() + document = wsdl.Document(wsdl_main, transport) + document.dump() + diff -Nru python-zeep-3.1.0/tests/test_wsse_signature.py python-zeep-3.2.0/tests/test_wsse_signature.py --- python-zeep-3.1.0/tests/test_wsse_signature.py 2017-11-04 08:15:21.000000000 +0000 +++ python-zeep-3.2.0/tests/test_wsse_signature.py 2018-11-18 09:08:17.000000000 +0000 @@ -16,9 +16,14 @@ KEY_FILE_PW = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'cert_valid_pw.pem') +# Check whether xmlsec library has been installed +from zeep.wsse.signature import xmlsec as xmlsec_installed +skip_if_no_xmlsec = pytest.mark.skipif(sys.platform == 'win32', + reason="does not run on windows") and \ + pytest.mark.skipif(xmlsec_installed is None, + reason="xmlsec library not installed") -@pytest.mark.skipif(sys.platform == 'win32', - reason="does not run on windows") +@skip_if_no_xmlsec def test_sign(): envelope = load_xml(""" + + + + OK + + + + """) + + plugin = wsse.BinarySignature(KEY_FILE_PW, KEY_FILE_PW, 'geheim') + envelope, headers = plugin.apply(envelope, {}) + plugin.verify(envelope) diff -Nru python-zeep-3.1.0/tests/test_wsse_username.py python-zeep-3.2.0/tests/test_wsse_username.py --- python-zeep-3.1.0/tests/test_wsse_username.py 2017-09-29 15:24:42.000000000 +0000 +++ python-zeep-3.2.0/tests/test_wsse_username.py 2018-12-07 20:40:00.000000000 +0000 @@ -1,13 +1,13 @@ import datetime import os - import pytest import requests_mock -from freezegun import freeze_time +from freezegun import freeze_time from tests.utils import assert_nodes_equal, load_xml from zeep import client from zeep.wsse import UsernameToken +from zeep.wsse.utils import WSU @pytest.mark.requests @@ -236,3 +236,67 @@ """ # noqa assert_nodes_equal(envelope, expected) + + +def test_timestamp_token(): + envelope = load_xml(""" + + + + + + + + + foobar + + + + + """) # noqa + + timestamp_token = WSU.Timestamp() + timestamp_token.attrib['Id'] = 'id-21a27a50-9ebf-49cc-96bf-fcf7131e7858' + some_date_obj = datetime.datetime(2018, 11, 18, 15, 44, 27, 440252) + timestamp_elements = [WSU.Created(some_date_obj.strftime("%Y-%m-%dT%H:%M:%SZ")), + WSU.Expires((some_date_obj + datetime.timedelta(minutes=10)).strftime("%Y-%m-%dT%H:%M:%SZ"))] + timestamp_token.extend(timestamp_elements) + + token = UsernameToken('Vishu', 'Guntupalli', timestamp_token=timestamp_token) + envelope, headers = token.apply(envelope, {}) + expected = """ + + + + + Vishu + Guntupalli + + + 2018-11-18T15:44:27Z + 2018-11-18T15:54:27Z + + + + + + foobar + + + + + """ # noqa + assert_nodes_equal(envelope, expected) diff -Nru python-zeep-3.1.0/tests/test_xsd_builtins.py python-zeep-3.2.0/tests/test_xsd_builtins.py --- python-zeep-3.1.0/tests/test_xsd_builtins.py 2018-05-12 10:06:55.000000000 +0000 +++ python-zeep-3.2.0/tests/test_xsd_builtins.py 2018-12-17 08:25:21.000000000 +0000 @@ -131,6 +131,9 @@ value = value.astimezone(pytz.timezone('Europe/Amsterdam')) assert instance.xmlvalue(value) == '2016-03-04T22:14:42+01:00' + assert instance.xmlvalue('2016-03-04T22:14:42+01:00') == '2016-03-04T22:14:42+01:00' + assert instance.xmlvalue('2016-03-04') == '2016-03-04' + def test_pythonvalue(self): instance = builtins.DateTime() value = datetime.datetime(2016, 3, 4, 21, 14, 42) diff -Nru python-zeep-3.1.0/tests/test_xsd_complex_types.py python-zeep-3.2.0/tests/test_xsd_complex_types.py --- python-zeep-3.1.0/tests/test_xsd_complex_types.py 2018-05-11 07:16:20.000000000 +0000 +++ python-zeep-3.2.0/tests/test_xsd_complex_types.py 2018-12-07 20:38:28.000000000 +0000 @@ -2,7 +2,7 @@ from lxml import etree from tests.utils import assert_nodes_equal, load_xml, render_node -from zeep import xsd +from zeep import exceptions, xsd def test_xml_xml_single_node(): @@ -330,3 +330,56 @@ obj = container_elm.parse(result[0], schema) assert obj._value_1 is None + + +def test_ignore_sequence_order(): + 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') + schema.settings.xsd_ignore_sequence_order = False + + node = response_doc.xpath( + '//ns0:Response', namespaces={ + 'xsd': 'http://www.w3.org/2001/XMLSchema', + 'ns0': 'http://tests.python-zeep.org/attr', + }) + with pytest.raises(exceptions.XMLParseError) as exc: + response = elm.parse(node[0], schema) + assert str(exc) is not None + + schema.settings.xsd_ignore_sequence_order = True + + response = elm.parse(node[0], schema) + assert response.Baz.id == 3 diff -Nru python-zeep-3.1.0/tests/test_xsd_indicators_choice.py python-zeep-3.2.0/tests/test_xsd_indicators_choice.py --- python-zeep-3.1.0/tests/test_xsd_indicators_choice.py 2018-05-12 08:46:19.000000000 +0000 +++ python-zeep-3.2.0/tests/test_xsd_indicators_choice.py 2018-12-03 16:52:42.000000000 +0000 @@ -308,6 +308,50 @@ assert result.item_1 == 'foo' +def test_choice_element_with_only_any(): + node = etree.fromstring(""" + + + + + + + + + + + + + + + """.strip()) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + item_1 = schema.get_element('ns0:item_1') + any_object = xsd.AnyObject(item_1, item_1('foo')) + value = element(_value_1=[any_object], 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._value_1 == ['foo'] + + def test_choice_element_with_any_max_occurs(): schema = xsd.Schema(load_xml("""