From c9562fe759b96943b8df5c38b432715417d2a090 Mon Sep 17 00:00:00 2001 From: Amin Solhizadeh Date: Fri, 23 Oct 2026 14:24:59 +0200 Subject: [PATCH 1/2] Add Python 3.15 support and drop Python 3.9 - Build cp315 wheels: bump cibuildwheel 3.3 -> 4.3.4 (cp315 is built by default since 4.2) and add cp315-* to the build list. - Bump lxml to 6.1.3, the first release with cp315 wheels. - Add 3.15 to the manylinux, macOS, sdist, linuxbrew and Travis matrices. - Drop Python 3.9 (EOL): requires-python >=3.10, classifiers, cibuildwheel build list, CI matrices, and ruff target-version py310 (adds strict=True to the one zip() flagged by B905). - Leak harness: count surviving young-generation objects with len(gc.get_objects(0)) instead of gc.get_count()[0]. With the GC disabled, CPython 3.15's young-generation counter drifts upward even when no objects survive, which tripped "unreferenced objects detected" in TestCallbacks. The new check still catches retained and unreachable reference cycles. --- .github/workflows/linuxbrew.yml | 2 +- .github/workflows/macosx.yml | 2 +- .github/workflows/manylinux.yml | 2 +- .github/workflows/sdist.yml | 2 +- .github/workflows/wheels.yml | 4 ++-- .travis.yml | 2 +- pyproject.toml | 10 +++++----- requirements.txt | 2 +- tests/base.py | 6 +++--- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/linuxbrew.yml b/.github/workflows/linuxbrew.yml index 51b0db1e..4f48befe 100644 --- a/.github/workflows/linuxbrew.yml +++ b/.github/workflows/linuxbrew.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: - python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] env: # For some unknown reason, linuxbrew tries to use "gcc-11" by default, which doesn't exist. diff --git a/.github/workflows/macosx.yml b/.github/workflows/macosx.yml index c9d8034e..503a0291 100644 --- a/.github/workflows/macosx.yml +++ b/.github/workflows/macosx.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: - python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] static_deps: ["static", ""] steps: diff --git a/.github/workflows/manylinux.yml b/.github/workflows/manylinux.yml index fe31b66e..52619c67 100644 --- a/.github/workflows/manylinux.yml +++ b/.github/workflows/manylinux.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: - python-abi: [cp39-cp39, cp310-cp310, cp311-cp311, cp312-cp312, cp313-cp313, cp314-cp314] + python-abi: [cp310-cp310, cp311-cp311, cp312-cp312, cp313-cp313, cp314-cp314, cp315-cp315] image: - manylinux2014_x86_64 - manylinux_2_28_x86_64 diff --git a/.github/workflows/sdist.yml b/.github/workflows/sdist.yml index f48ca02c..e20c45a3 100644 --- a/.github/workflows/sdist.yml +++ b/.github/workflows/sdist.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: - python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 422b038e..9f659c45 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -84,7 +84,7 @@ jobs: - name: Install cibuildwheel # N.B. Keep cibuildwheel version pin consistent with "build_wheels" job below. - run: pipx install cibuildwheel==3.3 + run: pipx install cibuildwheel==4.3.4 - id: set-matrix run: | @@ -145,7 +145,7 @@ jobs: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v3.3.0 + uses: pypa/cibuildwheel@v4.3.4 with: only: ${{ matrix.only }} env: diff --git a/.travis.yml b/.travis.yml index 8d3ca07e..3def9029 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,12 +9,12 @@ notifications: email: false python: - - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" - "3.14" + - "3.15" env: global: diff --git a/pyproject.toml b/pyproject.toml index 89ace626..a93d5200 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ name = "xmlsec" dynamic = ["version"] description = "Python bindings for the XML Security Library" readme = {file = "README.md", content-type = "text/markdown"} -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = ["lxml>=3.8"] keywords = ["xmlsec"] authors = [ @@ -24,12 +24,12 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: C", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Topic :: Text Processing :: Markup :: XML", "Typing :: Typed" ] @@ -90,7 +90,7 @@ disable_error_code = ["attr-defined"] line-length = 130 # Target Python version (used for autofixes and style rules) -target-version = "py39" +target-version = "py310" # Directories and files to exclude from linting and formatting exclude = [ @@ -154,12 +154,12 @@ line-ending = "lf" # cibuildwheel [tool.cibuildwheel] build = [ - "cp39-*", "cp310-*", "cp311-*", "cp312-*", "cp313-*", - "cp314-*" + "cp314-*", + "cp315-*" ] build-verbosity = 1 environment = {PYXMLSEC_STATIC_DEPS="true"} diff --git a/requirements.txt b/requirements.txt index 846c6eac..37466633 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -lxml==6.1.0 +lxml==6.1.3 diff --git a/tests/base.py b/tests/base.py index 1d21c89b..cd2e4eb5 100644 --- a/tests/base.py +++ b/tests/base.py @@ -39,7 +39,7 @@ def run(self, result=None): return m_usage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss - o_count = gc.get_count()[0] + o_count = len(gc.get_objects(0)) m_hits = 0 o_hits = 0 for _ in range(self.iterations): @@ -48,7 +48,7 @@ def run(self, result=None): if m_usage_n > m_usage: m_usage = m_usage_n m_hits += 1 - o_count_n = gc.get_count()[0] + o_count_n = len(gc.get_objects(0)) if o_count_n > o_count: o_count = o_count_n o_hits += 1 @@ -107,7 +107,7 @@ def assertXmlEqual(self, first, second, msg=None): cl2 = sorted(second.getchildren(), key=lambda x: x.tag) if len(cl1) != len(cl2): self.fail(f'children length differs, {len(cl1)} != {len(cl2)}. {msg}') - for c1, c2 in zip(cl1, cl2): + for c1, c2 in zip(cl1, cl2, strict=True): self.assertXmlEqual(c1, c2) From 481cc81564b0ecc5ad4e86f7ec6ed4e27120c66e Mon Sep 17 00:00:00 2001 From: Amin Solhizadeh Date: Fri, 23 Oct 2026 19:46:30 +0200 Subject: [PATCH 2/2] Fix Windows ARM64 wheels and bump bundled libraries Windows ARM64 wheel builds have failed since the windows-11-arm runner moved to the VS2026 image (MSVC 14.44 -> 14.51) with C1047: the prebuilt iconv_a.lib was compiled with /GL, which only links with the exact same compiler build. - Download Windows libs from win-binaries release 2026.10.23, where libiconv is built without /GL. - Run the AMD64 Windows jobs on windows-2025-vs2026, since the libs are now built with VS2026 and need a linker at least that new. - Windows libs: iconv 1.19-1, libxml2 2.11.9-8, libxslt 1.1.45, openssl 3.5.9, xmlsec 1.3.12, zlib 1.3.2. - Unix libs: libiconv 1.19, openssl 3.6.5, xmlsec1 1.3.12, zlib 1.3.2. libxml2 stays at 2.14.6 to match lxml, and libxslt stays at 1.1.43 because 1.1.44+ requires libxml2 >= 2.15.1. --- .github/workflows/cache_libs.yml | 22 ++++++++--------- .github/workflows/wheels.yml | 2 +- .../lib_xmlsec_dependency_builder.py | 24 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/cache_libs.yml b/.github/workflows/cache_libs.yml index 7465e4a2..79b76f98 100644 --- a/.github/workflows/cache_libs.yml +++ b/.github/workflows/cache_libs.yml @@ -4,7 +4,7 @@ on: workflow_call: inputs: LIBICONV_VERSION: - default: "1.18" + default: "1.19" required: false type: string LIBXML2_VERSION: @@ -16,39 +16,39 @@ on: required: false type: string OPENSSL_VERSION: - default: "3.6.0" + default: "3.6.5" required: false type: string XMLSEC1_VERSION: - default: "1.3.11" + default: "1.3.12" required: false type: string ZLIB_VERSION: - default: "1.3.1" + default: "1.3.2" required: false type: string WIN_LIBICONV_VERSION: - default: "1.18-1" + default: "1.19-1" required: false type: string WIN_LIBXML2_VERSION: - default: "2.11.9-3" + default: "2.11.9-8" required: false type: string WIN_LIBXSLT_VERSION: - default: "1.1.39" + default: "1.1.45" required: false type: string WIN_OPENSSL_VERSION: - default: "3.5.6" + default: "3.5.9" required: false type: string WIN_XMLSEC1_VERSION: - default: "1.3.11" + default: "1.3.12" required: false type: string WIN_ZLIB_VERSION: - default: "1.3.1" + default: "1.3.2" required: false type: string @@ -87,7 +87,7 @@ jobs: - "ubuntu-22.04" - "ubuntu-22.04-arm" - "macos-latest" - - "windows-2022" + - "windows-2025-vs2026" - "windows-11-arm" runs-on: ${{ matrix.os }} diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 9f659c45..a9a2750f 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -96,7 +96,7 @@ jobs: && cibuildwheel --print-build-identifiers --platform macos \ | jq -nRc '{"only": inputs, "os": "macos-latest"}' \ && cibuildwheel --print-build-identifiers --platform windows \ - | jq -nRc '{"only": inputs, "os": "windows-2022"}' \ + | jq -nRc '{"only": inputs, "os": "windows-2025-vs2026"}' \ && cibuildwheel --print-build-identifiers --platform windows --archs ARM64 \ | jq -nRc '{"only": inputs, "os": "windows-11-arm"}' } | jq -sc diff --git a/build_support/lib_xmlsec_dependency_builder.py b/build_support/lib_xmlsec_dependency_builder.py index 4a29ecc4..b50513e1 100644 --- a/build_support/lib_xmlsec_dependency_builder.py +++ b/build_support/lib_xmlsec_dependency_builder.py @@ -36,7 +36,7 @@ def triplet(self) -> str: class LibXmlsecDependencyBuilder: - WINDOWS_LIBS_DOWNLOAD_RELEASE_URL = 'https://github.com/mxamin/python-xmlsec-win-binaries/releases/download/2026.04.28/' + WINDOWS_LIBS_DOWNLOAD_RELEASE_URL = 'https://github.com/mxamin/python-xmlsec-win-binaries/releases/download/2026.10.23/' LIB_VERSION_ENV_VARS: ClassVar[dict[str, str]] = { 'libiconv_version': 'PYXMLSEC_LIBICONV_VERSION', 'libxml2_version': 'PYXMLSEC_LIBXML2_VERSION', @@ -46,20 +46,20 @@ class LibXmlsecDependencyBuilder: 'zlib_version': 'PYXMLSEC_ZLIB_VERSION', } UNIX_DEFAULT_LIB_VERSIONS: ClassVar[dict[str, str]] = { - 'libiconv_version': '1.18', + 'libiconv_version': '1.19', 'libxml2_version': '2.14.6', # Make sure it matches with lxml - 'libxslt_version': '1.1.43', - 'openssl_version': '3.6.0', - 'xmlsec1_version': '1.3.11', - 'zlib_version': '1.3.1', + 'libxslt_version': '1.1.43', # 1.1.44+ requires libxml2 >= 2.15.1 + 'openssl_version': '3.6.5', + 'xmlsec1_version': '1.3.12', + 'zlib_version': '1.3.2', } WINDOWS_DEFAULT_LIB_VERSIONS: ClassVar[dict[str, str]] = { - 'libiconv_version': '1.18-1', - 'libxml2_version': '2.11.9-3', # Make sure it matches with lxml - 'libxslt_version': '1.1.39', - 'openssl_version': '3.5.6', - 'xmlsec1_version': '1.3.11', - 'zlib_version': '1.3.1', + 'libiconv_version': '1.19-1', + 'libxml2_version': '2.11.9-8', # Make sure it matches with lxml + 'libxslt_version': '1.1.45', + 'openssl_version': '3.5.9', + 'xmlsec1_version': '1.3.12', + 'zlib_version': '1.3.2', } def __init__(self, platform_name, info=None, libs_dir=None, buildroot=None, plat_name=None):