Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions .github/workflows/python-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ jobs:
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Set up Python 3.14
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
python-version: "3.14"

- name: Install build requirements
run: python -m pip install wheel build
enable-cache: true

- name: Build package
run: python -m build
run: uv build

- name: Publish package
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
58 changes: 27 additions & 31 deletions .github/workflows/python-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@ jobs:
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Set up Python 3.14
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
python-version: "3.14"

- name: Install dependencies
run: pip install tox
enable-cache: true

- name: Validate formatting
run: tox -e format
run: |
uv run ruff check src tests
uv run ruff format --check src tests

test:
runs-on: ${{ matrix.platform }}
Expand All @@ -33,10 +32,6 @@ jobs:
matrix:
platform: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
# # TODO: Remove Windows exclusion when binary wheel available for lxml
# exclude:
# - { platform: windows-latest, python-version: "3.14" }


steps:
- name: Install system dependencies
Expand All @@ -52,25 +47,28 @@ jobs:

- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
enable-cache: true
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions

- name: Test with tox
run: tox
# xmlsec has no wheels for Windows / Python 3.14, so it is only installed
# on mac/linux for Python <= 3.13 (matching the system deps above).
run: >-
uv sync --frozen --extra async
${{ (matrix.platform != 'windows-latest' && matrix.python-version != '3.14') && '--extra xmlsec' || '' }}

- name: Test with pytest
run: uv run --frozen coverage run --parallel -m pytest
env:
PLATFORM: ${{ matrix.platform }}

- name: Prepare artifacts
run: mkdir .coverage-data && mv .coverage.* .coverage-data/

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-data-${{ matrix.platform }}-${{ matrix.python-version }}
path: .coverage-data/.coverage.*
Expand All @@ -81,23 +79,21 @@ jobs:
runs-on: ubuntu-latest
needs: [test]
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
pattern: coverage-data-*
merge-multiple: true
path: .

- name: Set up Python 3.14
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
python-version: "3.14"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox
enable-cache: true

- name: Prepare Coverage report
run: tox -e coverage-report
run: |
uv run --frozen coverage combine
uv run --frozen coverage xml
uv run --frozen coverage report
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ env/
*.egg-info
*.pyc
__pycache__
.tox
.coverage
.coverage.*
.eggs
.cache
.python-version
.venv
.venv-py*
.idea/
/build/
/dist/
Expand Down
31 changes: 16 additions & 15 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
.PHONY: install clean test retest coverage docs
.PHONY: install clean test test-all retest coverage docs lint format build

install:
pip install -e .[docs,test,async]
pip install bumpversion twine wheel
uv sync --extra docs --extra async

lint:
flake8 src/
flake8 --ignore=E501 tests/
isort --recursive --check-only --diff src tests
uv run ruff check src tests
uv run ruff format --check src tests

clean:
find . -name '*.pyc' -delete
find . -name '__pycache__' -delete

test:
pytest -vvv
uv run pytest -vvv

test-all:
for v in 3.10 3.11 3.12 3.13 3.14; do \
UV_PROJECT_ENVIRONMENT=.venv-py$$v uv run --python $$v --extra async pytest || exit 1; \
done

retest:
pytest -vvv --lf
uv run pytest -vvv --lf

coverage:
pytest --cov=zeep --cov-report=term-missing --cov-report=html
uv run pytest --cov=zeep --cov-report=term-missing --cov-report=html

format:
isort src tests setup.py
black src/ tests/ setup.py
uv run ruff check --fix src tests
uv run ruff format src tests

docs:
$(MAKE) -C docs html

release:
pip install twine wheel
build:
rm -rf build/* dist/*
python setup.py sdist bdist_wheel
twine upload -s dist/*
uv build
49 changes: 25 additions & 24 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,14 @@ Changelog = "https://github.com/mvantellingen/python-zeep/blob/main/CHANGES"

[project.optional-dependencies]
docs = ["sphinx>=1.4.0"]
test = [
async = [
"httpx>=0.15.0",
"packaging",
]
xmlsec = ["xmlsec>=0.6.1"]

[dependency-groups]
dev = [
"coverage[toml]==7.14.1",
"freezegun==1.5.1",
"pretend==1.0.9",
Expand All @@ -45,18 +52,8 @@ test = [
"pytest-asyncio",
"pytest==9.1.0",
"requests_mock==1.12.1",
# Linting
"isort==5.13.2",
"flake8==7.1.1",
"flake8-blind-except==0.2.1",
"flake8-debugger==4.1.2",
"flake8-imports==0.1.1",
]
async = [
"httpx>=0.15.0",
"packaging",
"ruff==0.15.17",
]
xmlsec = ["xmlsec>=0.6.1"]

[build-system]
requires = ["setuptools>=40.6.0", "wheel"]
Expand All @@ -65,21 +62,28 @@ build-backend = "setuptools.build_meta"
[tool.coverage.run]
branch = true
source = ["zeep"]
# Store paths relative to the repo root so coverage data collected on
# different OSes (uv installs the project editable -> src/zeep/...) can be
# combined without absolute-path mismatches.
relative_files = true

[tool.coverage.paths]
source = ["src", "*/site-packages/"]
source = ["src", "*/src", "*/site-packages"]

[tool.coverage.report]
show_missing = true

[tool.isort]
line_length = 88
multi_line_output = 3
include_trailing_comma = true
balanced_wrapping = true
default_section = "THIRDPARTY"
known_first_party = ["zeep", "tests"]
use_parentheses = true
[tool.ruff]
line-length = 88

[tool.ruff.lint]
# E/W: pycodestyle, F: pyflakes, I: isort,
# BLE: flake8-blind-except, T10: flake8-debugger
select = ["E", "W", "F", "I", "BLE", "T10"]
ignore = ["E501"]

[tool.ruff.lint.isort]
known-first-party = ["zeep", "tests"]

[tool.pytest.ini_options]
minversion = "6.0"
Expand All @@ -91,6 +95,3 @@ markers = [
"requests",
"network: test case requires network connection",
]

[tool.flake8]
max-line-length = 99
6 changes: 3 additions & 3 deletions src/zeep/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ def _version_string(self):
"""Expose the version prefix to be used in content serialization.
:rtype: bytes
"""
assert (
getattr(self, "_version", None) is not None
), "A version must be provided in order to use the VersionedCacheBase backend."
assert getattr(self, "_version", None) is not None, (
"A version must be provided in order to use the VersionedCacheBase backend."
)
prefix = "$ZEEP:%s$" % self._version
return bytes(prefix.encode("ascii"))

Expand Down
2 changes: 1 addition & 1 deletion src/zeep/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from urllib.parse import urljoin, urlparse, urlunparse

from lxml import etree
from lxml.etree import Resolver, XMLParser, XMLSyntaxError, fromstring
from lxml.etree import Resolver, XMLParser, fromstring

from zeep.exceptions import (
DTDForbidden,
Expand Down
2 changes: 1 addition & 1 deletion src/zeep/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def _merge_soap_headers(self, operation_soap_headers):
# Merge the default _soapheaders with the passed _soapheaders
if default_headers and operation_soap_headers:
merged = copy.deepcopy(default_headers)
if type(merged) != type(operation_soap_headers):
if type(merged) is not type(operation_soap_headers):
raise ValueError("Incompatible soapheaders definition")

if isinstance(operation_soap_headers, list):
Expand Down
5 changes: 1 addition & 4 deletions src/zeep/wsdl/wsdl.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ def __init__(
self.transport = transport

# Dict with all definition objects within this WSDL
self._definitions = (
{}
) # type: typing.Dict[typing.Tuple[str, str], "Definition"]
self._definitions = {} # type: typing.Dict[typing.Tuple[str, str], "Definition"]
self.types = Schema(
node=None,
transport=self.transport,
Expand Down Expand Up @@ -436,7 +434,6 @@ def parse_binding(
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:
Expand Down
2 changes: 1 addition & 1 deletion src/zeep/wsse/signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def _signature_prepare(envelope, key, signature_method, digest_method):
ctx.key = key
_sign_node(ctx, signature, envelope.find(QName(soap_env, "Body")), digest_method)
timestamp = security.find(QName(ns.WSU, "Timestamp"))
if timestamp != None:
if timestamp is not None:
_sign_node(ctx, signature, timestamp, digest_method)
ctx.sign(signature)

Expand Down
3 changes: 2 additions & 1 deletion src/zeep/xsd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

"""

from zeep.xsd.const import Nil, SkipValue
from zeep.xsd.const import Nil as Nil
from zeep.xsd.const import SkipValue as SkipValue
from zeep.xsd.elements import * # noqa
from zeep.xsd.schema import Schema as Schema
from zeep.xsd.types import * # noqa
Expand Down
1 change: 0 additions & 1 deletion src/zeep/xsd/elements/any.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ def _render_value_item(self, parent, 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(
Expand Down
11 changes: 6 additions & 5 deletions src/zeep/xsd/elements/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,17 +181,19 @@ def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
and schema.settings.xsd_ignore_sequence_order
and list(
filter(
lambda elem: etree.QName(elem.tag).localname
== self.qname.localname,
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,
lambda elem: (
etree.QName(elem.tag).localname == self.qname.localname
),
xmlelements,
)
)[0]
Expand Down Expand Up @@ -258,7 +260,6 @@ def _render_value_item(self, parent, value, 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(
Expand Down
1 change: 0 additions & 1 deletion src/zeep/xsd/elements/indicators.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,6 @@ def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
# Choose out of multiple
options = []
for element_name, element in self.elements_nested:

local_xmlelements = copy.copy(xmlelements)

try:
Expand Down
1 change: 0 additions & 1 deletion src/zeep/xsd/types/builtins.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import base64
import datetime
import math
import re
from decimal import Decimal as _Decimal

Expand Down
Loading
Loading