From ca39e727cafc38f99a75014e1a96c434643eb4b4 Mon Sep 17 00:00:00 2001 From: Amin Solhizadeh Date: Mon, 27 Apr 2026 18:19:51 +0200 Subject: [PATCH 1/3] Bump xmlsec1 unix lib to 1.3.11 --- .github/workflows/cache_libs.yml | 2 +- build_support/lib_xmlsec_dependency_builder.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cache_libs.yml b/.github/workflows/cache_libs.yml index e962af3..10dc803 100644 --- a/.github/workflows/cache_libs.yml +++ b/.github/workflows/cache_libs.yml @@ -20,7 +20,7 @@ on: required: false type: string XMLSEC1_VERSION: - default: "1.3.10" + default: "1.3.11" required: false type: string ZLIB_VERSION: diff --git a/build_support/lib_xmlsec_dependency_builder.py b/build_support/lib_xmlsec_dependency_builder.py index 5e3911c..eca8787 100644 --- a/build_support/lib_xmlsec_dependency_builder.py +++ b/build_support/lib_xmlsec_dependency_builder.py @@ -50,7 +50,7 @@ class LibXmlsecDependencyBuilder: '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.10', + 'xmlsec1_version': '1.3.11', 'zlib_version': '1.3.1', } WINDOWS_DEFAULT_LIB_VERSIONS: ClassVar[dict[str, str]] = { From 7c0c842b9f7f4bf90449139726f126124b91833b Mon Sep 17 00:00:00 2001 From: Amin Solhizadeh Date: Mon, 27 Apr 2026 22:44:56 +0200 Subject: [PATCH 2/3] Document shutdown as process-final with xmlsec1 1.3.11 xmlsec1 1.3.11 may call OPENSSL_cleanup() from the OpenSSL backend during shutdown. OpenSSL cannot be reinitialized in the same process after that cleanup runs. Update the lifecycle test to call init() before shutdown(), run it last, and stop testing shutdown/init reinitialization. Document the new lifecycle constraint in the module docs and runtime docstrings. --- doc/source/modules/xmlsec.rst | 11 +++++++++++ src/main.c | 13 ++++++++++--- tests/conftest.py | 11 ++++++----- tests/test_xmlsec.py | 13 ++++++++----- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/doc/source/modules/xmlsec.rst b/doc/source/modules/xmlsec.rst index 426bbaa..4d39dc6 100644 --- a/doc/source/modules/xmlsec.rst +++ b/doc/source/modules/xmlsec.rst @@ -1,6 +1,17 @@ ``xmlsec`` ---------- +Lifecycle +~~~~~~~~~ + +The module initializes the underlying xmlsec library on import. Applications +that call :func:`xmlsec.shutdown` should treat it as process-final and should +not call :func:`xmlsec.init` afterwards. + +This is required because upstream xmlsec1 versions starting with 1.3.11 may +call ``OPENSSL_cleanup()`` during shutdown when using the OpenSSL backend. +OpenSSL cannot be reinitialized in the same process after that cleanup has run. + .. automodule:: xmlsec :members: :undoc-members: diff --git a/src/main.c b/src/main.c index 61eac13..c7dac2b 100644 --- a/src/main.c +++ b/src/main.c @@ -101,8 +101,11 @@ static int PyXmlSec_Init(void) { static char PyXmlSec_PyInit__doc__[] = \ "init() -> None\n" "Initializes the library for general operation.\n\n" - "This is called upon library import and does not need to be called\n" - "again :func:`~.shutdown` is called explicitly).\n"; + "This is called upon library import and normally does not need to be\n" + "called explicitly. It is only valid before shutdown() has been called.\n\n" + "Calling init() after shutdown() is unsupported because upstream\n" + "xmlsec1 1.3.11+ may call OPENSSL_cleanup() during shutdown, and OpenSSL\n" + "cannot be reinitialized in the same process after that cleanup.\n"; static PyObject* PyXmlSec_PyInit(PyObject *self) { if (PyXmlSec_Init() < 0) { return NULL; @@ -114,7 +117,11 @@ static char PyXmlSec_PyShutdown__doc__[] = \ "shutdown() -> None\n" "Shutdowns the library and cleanup any leftover resources.\n\n" "This is called automatically upon interpreter termination and\n" - "should not need to be called explicitly."; + "should not need to be called explicitly.\n\n" + "Shutdown is process-final. Do not call init() after shutdown(),\n" + "because upstream xmlsec1 1.3.11+ may call OPENSSL_cleanup() during shutdown,\n" + "and OpenSSL cannot be reinitialized in the same process after that\n" + "cleanup."; static PyObject* PyXmlSec_PyShutdown(PyObject* self) { PyXmlSec_Free(free_mode); Py_RETURN_NONE; diff --git a/tests/conftest.py b/tests/conftest.py index a65235d..6f0441d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,10 +1,11 @@ def pytest_collection_modifyitems(items): - """Put the module init test first. + """Put the module shutdown test last. - This way, we implicitly check whether any subsequent test fails because of module reinitialization. + xmlsec shutdown is process-final with OpenSSL cleanup introduced in + xmlsec1 1.3.11, so no tests should use xmlsec after it runs. """ - def module_init_tests_first(item): - return int('test_xmlsec.py::TestModule::test_reinitialize_module' not in item.nodeid) + def module_shutdown_tests_last(item): + return int('test_xmlsec.py::TestModule::test_initialize_module' in item.nodeid) - items.sort(key=module_init_tests_first) + items.sort(key=module_shutdown_tests_last) diff --git a/tests/test_xmlsec.py b/tests/test_xmlsec.py index 52dce2b..a265ef3 100644 --- a/tests/test_xmlsec.py +++ b/tests/test_xmlsec.py @@ -3,11 +3,14 @@ class TestModule(base.TestMemoryLeaks): - def test_reinitialize_module(self): - """This test doesn't explicitly verify anything, but will be invoked first in the suite. + iterations = 0 - So if the subsequent tests don't fail, we know that the ``init()``/``shutdown()`` - function pair doesn't break anything. + def test_initialize_module(self): + """Check explicit initialization before final module shutdown. + + This test is invoked last because shutdown is process-final: since + xmlsec1 1.3.11, its OpenSSL backend may call OPENSSL_cleanup(), after + which OpenSSL cannot be reinitialized in the same process. """ - xmlsec.shutdown() xmlsec.init() + xmlsec.shutdown() From 119a52fc8340f89eb57b6d643d3c31789c57ce0b Mon Sep 17 00:00:00 2001 From: Amin Solhizadeh Date: Tue, 28 Apr 2026 07:56:41 +0200 Subject: [PATCH 3/3] Minor hxcanges and fixes --- tests/conftest.py | 6 +++--- tests/test_xmlsec.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 6f0441d..4d57ef1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,7 +5,7 @@ def pytest_collection_modifyitems(items): xmlsec1 1.3.11, so no tests should use xmlsec after it runs. """ - def module_shutdown_tests_last(item): - return int('test_xmlsec.py::TestModule::test_initialize_module' in item.nodeid) + def module_init_shutdown_tests_last(item): + return int('test_xmlsec.py::TestModule::test_init_shutdown_module' in item.nodeid) - items.sort(key=module_shutdown_tests_last) + items.sort(key=module_init_shutdown_tests_last) diff --git a/tests/test_xmlsec.py b/tests/test_xmlsec.py index a265ef3..4267ac2 100644 --- a/tests/test_xmlsec.py +++ b/tests/test_xmlsec.py @@ -5,7 +5,7 @@ class TestModule(base.TestMemoryLeaks): iterations = 0 - def test_initialize_module(self): + def test_init_shutdown_module(self): """Check explicit initialization before final module shutdown. This test is invoked last because shutdown is process-final: since