Skip to content

Commit e20aa1f

Browse files
committed
Merge remote-tracking branch 'origin/master' into mvantellingen#1212
2 parents 4eb6d5c + 2f35b7d commit e20aa1f

32 files changed

+153
-55
lines changed

.bumpversion.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 4.0.0
2+
current_version = 4.1.0
33
commit = true
44
tag = true
55
tag_name = {new_version}

.github/workflows/python-test.yml

+8-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
steps:
1010
- uses: actions/checkout@v2
1111
- name: Set up Python 3.7
12-
uses: actions/setup-python@v1
12+
uses: actions/setup-python@v2
1313
with:
1414
python-version: 3.7
1515
- name: Install dependencies
@@ -20,10 +20,14 @@ jobs:
2020
test:
2121
runs-on: ${{ matrix.platform }}
2222
strategy:
23+
fail-fast: false
2324
max-parallel: 4
2425
matrix:
2526
platform: [ubuntu-latest, macos-latest, windows-latest]
26-
python-version: ["3.6", "3.7", "3.8"]
27+
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
28+
# TODO: Remove Windows exclusion when binary wheel available for lxml
29+
exclude:
30+
- { platform: windows-latest, python-version: "3.10" }
2731

2832
steps:
2933
- name: Install system dependencies
@@ -37,7 +41,7 @@ jobs:
3741
brew install libxmlsec1 libxslt pkgconfig
3842
- uses: actions/checkout@v2
3943
- name: Set up Python ${{ matrix.python-version }}
40-
uses: actions/setup-python@v1
44+
uses: actions/setup-python@v2
4145
with:
4246
python-version: ${{ matrix.python-version }}
4347
- name: Install dependencies
@@ -65,7 +69,7 @@ jobs:
6569
name: coverage-data
6670
path: .
6771
- name: Set up Python 3.7
68-
uses: actions/setup-python@v1
72+
uses: actions/setup-python@v2
6973
with:
7074
python-version: 3.7
7175
- name: Install dependencies

CHANGES

+11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
4.1.0 (2021-08-15)
2+
------------------
3+
- Remove last dependency on `six` (#1250)
4+
- Use `platformdirs` instead of the `appsdirs` dependency (#1244)
5+
- Pass digest method when signing timestamp node(#1201)
6+
- Fix settings context manager when an exception is raised (#1193)
7+
- Don't render decimals using scientific notation (#1191)
8+
- Remove dependency on `defusedxml` (deprecated) (#1179)
9+
- Improve handling of str values for Duration (#1165)
10+
11+
112
4.0.0 (2020-10-12)
213
------------------
314
- Drop support for Python 2.7, 3.3, 3.4 and 3.5

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2016-2017 Michael van Tellingen
3+
Copyright (c) 2016-2021 Michael van Tellingen
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ include CHANGES
77
include CONTRIBUTORS.rst
88
include LICENSE
99
include README.rst
10+
include pyproject.toml
1011
include setup.cfg
1112
include setup.py
1213

README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Zeep: Python SOAP client
55
A fast and modern Python SOAP client
66

77
Highlights:
8-
* Compatible with Python 3.6, 3.7, 3.8 and PyPy
8+
* Compatible with Python 3.6, 3.7, 3.8, 3.9, 3.10 and PyPy3
99
* Build on top of lxml and requests
1010
* Support for Soap 1.1, Soap 1.2 and HTTP bindings
1111
* Support for WS-Addressing headers

docs/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
# built documents.
6363
#
6464
# The short X.Y version.
65-
version = '4.0.0'
65+
version = '4.1.0'
6666
release = version
6767

6868
# The language for content autogenerated by Sphinx. Refer to documentation

docs/index.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ See ``python -mzeep --help`` for more information about this command.
106106

107107
.. note:: Zeep follows `semver`_ for versioning, however bugs can always occur.
108108
So as always pin the version of zeep you tested with
109-
(e.g. ``zeep==4.0.0``').
109+
(e.g. ``zeep==4.1.0``').
110110

111111

112112
.. _semver: http://semver.org/

docs/plugins.rst

+4-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ Writing a plugin is really simple and best explained via an example.
2727
2828
2929
The plugin can implement two methods: ``ingress`` and ``egress``. Both methods
30-
should always return an envelop (lxml element) and the http headers.
30+
should always return an envelop (lxml element) and the http headers. The
31+
envelope in the ``egress`` plugin will only contain the body of the soap message.
32+
This is important to remember if you want to inspect or do something
33+
with the headers.
3134

3235
To register this plugin you need to pass it to the client. Plugins are always
3336
executed sequentially.

setup.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
from setuptools import setup
44

55
install_requires = [
6-
"appdirs>=1.4.0",
76
"attrs>=17.2.0",
8-
"cached-property>=1.3.0",
7+
"cached-property>=1.3.0; python_version<'3.8'",
98
"isodate>=0.5.4",
109
"lxml>=4.6.0",
10+
"platformdirs>=1.4.0",
1111
"requests>=2.7.0",
1212
"requests-toolbelt>=0.7.1",
1313
"requests-file>=1.5.1",
@@ -27,12 +27,11 @@
2727
tests_require = [
2828
"coverage[toml]==5.2.1",
2929
"freezegun==0.3.15",
30-
"mock==2.0.0",
3130
"pretend==1.0.9",
3231
"pytest-cov==2.8.1",
3332
"pytest-httpx",
3433
"pytest-asyncio",
35-
"pytest==6.0.1",
34+
"pytest==6.2.5",
3635
"requests_mock>=0.7.0",
3736
# Linting
3837
"isort==5.3.2",
@@ -50,12 +49,12 @@
5049

5150
setup(
5251
name="zeep",
53-
version="4.0.0",
52+
version="4.1.0",
5453
description="A modern/fast Python SOAP client based on lxml / requests",
5554
long_description=long_description,
5655
author="Michael van Tellingen",
5756
author_email="[email protected]",
58-
url="http://docs.python-zeep.org",
57+
url="https://docs.python-zeep.org",
5958
python_requires=">=3.6",
6059
install_requires=install_requires,
6160
tests_require=tests_require,
@@ -74,10 +73,12 @@
7473
"Development Status :: 5 - Production/Stable",
7574
"License :: OSI Approved :: MIT License",
7675
"Programming Language :: Python :: 3",
76+
"Programming Language :: Python :: 3 :: Only",
7777
"Programming Language :: Python :: 3.6",
7878
"Programming Language :: Python :: 3.7",
7979
"Programming Language :: Python :: 3.8",
8080
"Programming Language :: Python :: 3.9",
81+
"Programming Language :: Python :: 3.10",
8182
"Programming Language :: Python :: Implementation :: CPython",
8283
"Programming Language :: Python :: Implementation :: PyPy",
8384
],

src/zeep/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
from zeep.transports import Transport # noqa
55
from zeep.xsd.valueobjects import AnyObject # noqa
66

7-
__version__ = "4.0.0"
7+
__version__ = "4.1.0"

src/zeep/cache.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import typing
88
from contextlib import contextmanager
99

10-
import appdirs
10+
import platformdirs
1111
import pytz
1212

1313
# The sqlite3 is not available on Google App Engine so we handle the
@@ -176,7 +176,7 @@ def _is_expired(value, timeout):
176176

177177

178178
def _get_default_cache_path():
179-
path = appdirs.user_cache_dir("zeep", False)
179+
path = platformdirs.user_cache_dir("zeep", False)
180180
try:
181181
os.makedirs(path)
182182
except OSError as exc:

src/zeep/loader.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
import typing
33
from urllib.parse import urljoin, urlparse, urlunparse
44

5-
from exceptions import DTDForbidden, EntitiesForbidden
65
from lxml import etree
7-
from lxml.etree import fromstring, XMLParser, XMLSyntaxError, Resolver
6+
from lxml.etree import Resolver, XMLParser, XMLSyntaxError, fromstring
87

9-
from zeep.exceptions import XMLSyntaxError
8+
from zeep.exceptions import DTDForbidden, EntitiesForbidden, XMLSyntaxError
109
from zeep.settings import Settings
1110

1211

@@ -48,19 +47,20 @@ def parse_xml(content: str, transport, base_url=None, settings=None):
4847
)
4948
parser.resolvers.add(ImportResolver(transport))
5049
try:
51-
elementtree = fromstring(content, parser=parser,base_url=base_url)
50+
elementtree = fromstring(content, parser=parser, base_url=base_url)
5251
docinfo = elementtree.getroottree().docinfo
5352
if docinfo.doctype:
5453
if settings.forbid_dtd:
55-
raise DTDForbidden(docinfo.doctype, docinfo.system_url, docinfo.public_id)
54+
raise DTDForbidden(
55+
docinfo.doctype, docinfo.system_url, docinfo.public_id
56+
)
5657
if settings.forbid_entities:
5758
for dtd in docinfo.internalDTD, docinfo.externalDTD:
5859
if dtd is None:
5960
continue
6061
for entity in dtd.iterentities():
6162
raise EntitiesForbidden(entity.name, entity.content)
6263

63-
6464
return elementtree
6565
except etree.XMLSyntaxError as exc:
6666
raise XMLSyntaxError(

src/zeep/proxy.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ def __getitem__(self, key):
9999
raise AttributeError("Service has no operation %r" % key)
100100

101101
def __iter__(self):
102-
""" Return iterator over the services and their callables. """
102+
"""Return iterator over the services and their callables."""
103103
return iter(self._operations.items())
104104

105105
def __dir__(self):
106-
""" Return the names of the operations. """
106+
"""Return the names of the operations."""
107107
return list(itertools.chain(dir(super()), self._operations))
108108

109109

src/zeep/transports.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def __init__(self, cache=None, timeout=300, operation_timeout=None, session=None
3737
self.operation_timeout = operation_timeout
3838
self.logger = logging.getLogger(__name__)
3939

40+
self.__close_session = not session
4041
self.session = session or requests.Session()
4142
self.session.mount("file://", FileAdapter())
4243
self.session.headers["User-Agent"] = "Zeep/%s (www.python-zeep.org)" % (
@@ -154,6 +155,10 @@ def settings(self, timeout=None):
154155
yield
155156
self.operation_timeout = old_timeout
156157

158+
def __del__(self):
159+
if self.__close_session:
160+
self.session.close()
161+
157162

158163
class AsyncTransport(Transport):
159164
"""Asynchronous Transport class using httpx.
@@ -170,7 +175,6 @@ def __init__(
170175
cache=None,
171176
timeout=300,
172177
operation_timeout=None,
173-
session=None,
174178
verify_ssl=True,
175179
proxy=None,
176180
):

src/zeep/wsdl/attachments.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66

77
import base64
88

9-
from cached_property import cached_property
9+
try:
10+
from functools import cached_property
11+
except ImportError:
12+
from cached_property import cached_property
13+
1014
from requests.structures import CaseInsensitiveDict
1115

1216

src/zeep/wsdl/bindings/soap.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -322,15 +322,15 @@ def process_error(self, doc, operation):
322322
)
323323

324324
def get_text(name):
325-
child = fault_node.find(name)
325+
child = fault_node.find(name, namespaces=fault_node.nsmap)
326326
if child is not None:
327327
return child.text
328328

329329
raise Fault(
330330
message=get_text("faultstring"),
331331
code=get_text("faultcode"),
332332
actor=get_text("faultactor"),
333-
detail=fault_node.find("detail"),
333+
detail=fault_node.find("detail", namespaces=fault_node.nsmap),
334334
)
335335

336336
def _set_http_headers(self, serialized, operation):

src/zeep/wsse/signature.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def _signature_prepare(envelope, key, signature_method, digest_method):
244244
_sign_node(ctx, signature, envelope.find(QName(soap_env, "Body")), digest_method)
245245
timestamp = security.find(QName(ns.WSU, "Timestamp"))
246246
if timestamp != None:
247-
_sign_node(ctx, signature, timestamp)
247+
_sign_node(ctx, signature, timestamp, digest_method)
248248
ctx.sign(signature)
249249

250250
# Place the X509 data inside a WSSE SecurityTokenReference within

src/zeep/wsse/username.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
import hashlib
33
import os
44

5-
import six
6-
75
from zeep import ns
86
from zeep.wsse import utils
97

@@ -108,7 +106,7 @@ def _create_password_digest(self):
108106
nonce = os.urandom(16)
109107
timestamp = utils.get_timestamp(self.created, self.zulu_timestamp)
110108

111-
if isinstance(self.password, six.string_types):
109+
if isinstance(self.password, str):
112110
password = self.password.encode("utf-8")
113111
else:
114112
password = self.password

src/zeep/xsd/elements/indicators.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
import typing
1717
from collections import OrderedDict, defaultdict, deque
1818

19-
from cached_property import threaded_cached_property
19+
try:
20+
from functools import cached_property as threaded_cached_property
21+
except ImportError:
22+
from cached_property import threaded_cached_property
23+
2024
from lxml import etree
2125

2226
from zeep.exceptions import UnexpectedElementError, ValidationError

src/zeep/xsd/types/any.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import logging
22
import typing
33

4-
from cached_property import threaded_cached_property
4+
try:
5+
from functools import cached_property as threaded_cached_property
6+
except ImportError:
7+
from cached_property import threaded_cached_property
8+
59
from lxml import etree
610

711
from zeep.utils import qname_attr

src/zeep/xsd/types/builtins.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,12 @@ def pythonvalue(self, value):
124124

125125
class Duration(BuiltinType):
126126
_default_qname = xsd_ns("duration")
127-
accepted_types = [isodate.duration.Duration, str]
127+
accepted_types = [isodate.duration.Duration, datetime.timedelta, str]
128128

129129
@check_no_collection
130130
def xmlvalue(self, value):
131+
if isinstance(value, str):
132+
value = isodate.parse_duration(value)
131133
return isodate.duration_isoformat(value)
132134

133135
@treat_whitespace("collapse")

src/zeep/xsd/types/complex.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
from collections import OrderedDict, deque
55
from itertools import chain
66

7-
from cached_property import threaded_cached_property
7+
try:
8+
from functools import cached_property as threaded_cached_property
9+
except ImportError:
10+
from cached_property import threaded_cached_property
11+
812
from lxml import etree
913

1014
from zeep.exceptions import UnexpectedElementError, XMLParseError

0 commit comments

Comments
 (0)