From 957cef1cc7d9c33dc0cfd02324036a176643bcd2 Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:49:30 +0200 Subject: [PATCH 01/12] Update CI workflow: bump actions, fix OpenSSL build, gate heavy jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump actions/checkout v4→v6, setup-uv v5→v7, upload-artifact v4→v6, download-artifact v4→v7, cibuildwheel v2.22.0→v3.3.1 - Update OpenSSL 3.0.13→3.0.19, flatbuffers v24.3.25→v25.12.19 - Fix OpenSSL build: use `source gcc-toolset-13/enable` instead of dangling CC/CXX assignments - Add perl-Time-Piece to dnf install (needed for OpenSSL build) - Gate build_wheels/build_sdist on release, PR, or workflow_dispatch - Remove dead `master` branch from triggers - Fix stale comment: "Python 3.9-3.13" → "Python 3.10-3.14" Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build.yml | 51 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ac6b69f..83bb5dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [main, master] + branches: [main] pull_request: - branches: [main, master] + branches: [main] release: types: [published] workflow_dispatch: @@ -15,12 +15,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v7 - name: Set up Python run: uv python install 3.12 @@ -33,7 +33,7 @@ jobs: - name: Build wheel run: uv build --wheel - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: name: wheel path: dist/*.whl @@ -44,16 +44,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v7 - name: Set up Python run: uv python install 3.12 - name: Download wheel - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: wheel path: dist @@ -66,6 +66,7 @@ jobs: build_wheels: name: Build wheels on ${{ matrix.os }} + if: github.event_name == 'release' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -73,12 +74,12 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v7 - name: Install dependencies (Ubuntu) if: runner.os == 'Linux' @@ -97,11 +98,11 @@ jobs: vcpkg install openssl:x64-windows zlib:x64-windows libxml2:x64-windows flatbuffers:x64-windows - name: Build wheels - uses: pypa/cibuildwheel@v2.22.0 + uses: pypa/cibuildwheel@v3.3.1 env: # Use uv for faster builds CIBW_BUILD_FRONTEND: "build[uv]" - # Build for Python 3.9-3.13 + # Build for Python 3.10-3.14 CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*" # Skip 32-bit builds and musllinux CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_*" @@ -110,11 +111,10 @@ jobs: CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28 # Install build dependencies in the container (need GCC 13 for C++23, OpenSSL 3.x) CIBW_BEFORE_ALL_LINUX: > - dnf install -y gcc-toolset-13 perl-IPC-Cmd libxml2-devel zlib-devel && - curl -L https://www.openssl.org/source/openssl-3.0.13.tar.gz | tar xz && - cd openssl-3.0.13 && - CC=/opt/rh/gcc-toolset-13/root/usr/bin/gcc - CXX=/opt/rh/gcc-toolset-13/root/usr/bin/g++ + dnf install -y gcc-toolset-13 perl-IPC-Cmd perl-Time-Piece libxml2-devel zlib-devel && + source /opt/rh/gcc-toolset-13/enable && + curl -L https://www.openssl.org/source/openssl-3.0.19.tar.gz | tar xz && + cd openssl-3.0.19 && ./config --prefix=/usr/local/openssl3 --openssldir=/usr/local/openssl3 && make -j$(nproc) && make install_sw install_ssldirs @@ -130,8 +130,8 @@ jobs: uv pip install --system cmake swig && if ! command -v flatc; then cd /tmp && - curl -L https://github.com/google/flatbuffers/archive/refs/tags/v24.3.25.tar.gz | tar xz && - cd flatbuffers-24.3.25 && + curl -L https://github.com/google/flatbuffers/archive/refs/tags/v25.12.19.tar.gz | tar xz && + cd flatbuffers-25.12.19 && cmake -B build -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_TESTS=OFF && cmake --build build --target install; fi @@ -153,26 +153,27 @@ jobs: CIBW_TEST_REQUIRES: pytest CIBW_TEST_COMMAND: pytest {project}/tests -v - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: name: wheels-${{ matrix.os }} path: ./wheelhouse/*.whl build_sdist: name: Build source distribution + if: github.event_name == 'release' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v7 - name: Build sdist run: uv build --sdist - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: name: sdist path: dist/*.tar.gz @@ -189,13 +190,13 @@ jobs: id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v7 with: pattern: wheels-* path: dist merge-multiple: true - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v7 with: name: sdist path: dist From 2e1dec29237e5f5488de62e449e7e94ab98c1c10 Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:51:32 +0200 Subject: [PATCH 02/12] Migrate test deps to PEP 735 dependency-groups, add dev tools Move pytest from [project.optional-dependencies] to [dependency-groups] and add bump-my-version and ruff as dev dependencies. Co-Authored-By: Claude Opus 4.6 --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index cc3d38b..f7819a0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,9 @@ classifiers = [ [project.optional-dependencies] ldap = ["ldap3>=2.9"] -test = ["pytest>=7.0"] + +[dependency-groups] +dev = ["pytest>=7.0", "bump-my-version>=0.31", "ruff>=0.9"] [project.urls] Homepage = "https://github.com/namespace-ee/pycdoc" From 224dc8192c278772c2ff8bfc7b4186627cf3ecfe Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:52:23 +0200 Subject: [PATCH 03/12] Add bump-my-version config for pyproject.toml, __init__.py, CMakeLists.txt Co-Authored-By: Claude Opus 4.6 --- pyproject.toml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index f7819a0..585ce3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,3 +63,24 @@ BUILD_SHARED_LIBS = "OFF" BUILD_TOOLS = "OFF" FRAMEWORK = "OFF" LIBCDOC_WITH_DOCS = "OFF" + +[tool.bumpversion] +current_version = "0.1.0" +commit = true +tag = true +tag_name = "v{new_version}" + +[[tool.bumpversion.files]] +filename = "pyproject.toml" +search = 'version = "{current_version}"' +replace = 'version = "{new_version}"' + +[[tool.bumpversion.files]] +filename = "src/pycdoc/__init__.py" +search = '__version__ = "{current_version}"' +replace = '__version__ = "{new_version}"' + +[[tool.bumpversion.files]] +filename = "CMakeLists.txt" +search = "VERSION {current_version}" +replace = "VERSION {new_version}" From c3b3fb8b1a1c1388b6ea294cdb8cb61f9d2ff853 Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:52:42 +0200 Subject: [PATCH 04/12] Add ruff linter config targeting Python 3.10 Co-Authored-By: Claude Opus 4.6 --- pyproject.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 585ce3e..69763b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,3 +84,10 @@ replace = '__version__ = "{new_version}"' filename = "CMakeLists.txt" search = "VERSION {current_version}" replace = "VERSION {new_version}" + +[tool.ruff] +target-version = "py310" + +[tool.ruff.lint] +select = ["E", "F", "I", "UP", "B", "SIM", "RUF"] +ignore = ["I001", "RUF022"] From e9f29dc036702bd88887f425f07a000fb625d0e1 Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:53:07 +0200 Subject: [PATCH 05/12] Remove duplicate CMake args from pyproject.toml BUILD_SHARED_LIBS, BUILD_TOOLS, FRAMEWORK, and LIBCDOC_WITH_DOCS were set in cmake.args, cmake.define, and CMakeLists.txt. Keep only in CMakeLists.txt to avoid drift. Co-Authored-By: Claude Opus 4.6 --- pyproject.toml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 69763b2..b5fe86b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,24 +46,12 @@ Issues = "https://github.com/namespace-ee/pycdoc/issues" [tool.scikit-build] cmake.version = ">=3.20" cmake.build-type = "Release" -cmake.args = [ - "-DBUILD_SHARED_LIBS=OFF", - "-DBUILD_TOOLS=OFF", - "-DFRAMEWORK=OFF", - "-DLIBCDOC_WITH_DOCS=OFF", -] wheel.install-dir = "pycdoc" wheel.packages = [] build-dir = "build/{wheel_tag}" # git submodules are excluded from sdists by default (git archive ignores them) sdist.include = ["libcdoc/**"] -[tool.scikit-build.cmake.define] -BUILD_SHARED_LIBS = "OFF" -BUILD_TOOLS = "OFF" -FRAMEWORK = "OFF" -LIBCDOC_WITH_DOCS = "OFF" - [tool.bumpversion] current_version = "0.1.0" commit = true From 00c1ab476b96bc2270911ac2d797d71ac0a3f0e9 Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:53:18 +0200 Subject: [PATCH 06/12] Add Python Modules classifier to pyproject.toml Co-Authored-By: Claude Opus 4.6 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index b5fe86b..4a87917 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ classifiers = [ "Programming Language :: Python :: 3.14", "Programming Language :: C++", "Topic :: Security :: Cryptography", + "Topic :: Software Development :: Libraries :: Python Modules", ] [project.optional-dependencies] From fcadb1fcf5236788057e8c5368244eac7ca7583c Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:54:41 +0200 Subject: [PATCH 07/12] Fix director duplication: keep in patch, remove from pycdoc.i MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Director declarations existed in both the patch and pycdoc.i. Keep them in the patch (which modifies libcdoc.i) and fix ILogger→Logger to match the actual C++ class name. Co-Authored-By: Claude Opus 4.6 --- patches/libcdoc-python.patch | 2 +- swig/pycdoc.i | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/patches/libcdoc-python.patch b/patches/libcdoc-python.patch index bbd54e7..ba14892 100644 --- a/patches/libcdoc-python.patch +++ b/patches/libcdoc-python.patch @@ -110,7 +110,7 @@ index c681964..173770d 100644 +%feature("director") libcdoc::PKCS11Backend; +%feature("director") libcdoc::NetworkBackend; +%feature("director") libcdoc::Configuration; -+%feature("director") libcdoc::ILogger; ++%feature("director") libcdoc::Logger; +#endif + #ifdef SWIGJAVA diff --git a/swig/pycdoc.i b/swig/pycdoc.i index 0985fef..c0210f0 100644 --- a/swig/pycdoc.i +++ b/swig/pycdoc.i @@ -10,14 +10,6 @@ %rename("%(undercase)s", %$isfunction, %$not %$isconstructor, %$not %$isdestructor) ""; %rename("%(undercase)s", %$ismember, %$not %$isenumitem, %$not %$isconstant, %$not %$isconstructor, %$not %$isdestructor, %$not %$isenum) ""; -/* Enable directors for Python subclassing of C++ classes */ -%feature("director") libcdoc::DataSource; -%feature("director") libcdoc::CryptoBackend; -%feature("director") libcdoc::PKCS11Backend; -%feature("director") libcdoc::NetworkBackend; -%feature("director") libcdoc::Configuration; -%feature("director") libcdoc::Logger; - /* Include the upstream libcdoc SWIG interface */ %include "libcdoc.i" From 037193fe5e765480d03eea6f6d179c177567c91b Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:54:58 +0200 Subject: [PATCH 08/12] Fix template duplication: keep in patch, remove from pycdoc.i ByteVector, ByteVectorVector, and StringVector templates were declared in both the patch (libcdoc.i) and pycdoc.i. Co-Authored-By: Claude Opus 4.6 --- swig/pycdoc.i | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/swig/pycdoc.i b/swig/pycdoc.i index c0210f0..ae24585 100644 --- a/swig/pycdoc.i +++ b/swig/pycdoc.i @@ -2,7 +2,7 @@ * pycdoc - Python-specific SWIG interface for libcdoc * * This wraps the upstream libcdoc.i and adds Python-specific - * template instantiations and director support. + * camelCase → snake_case renaming. */ /* Global camelCase → snake_case renaming for all functions and methods. @@ -12,8 +12,3 @@ /* Include the upstream libcdoc SWIG interface */ %include "libcdoc.i" - -/* Python-specific std::vector template instantiations */ -%template(ByteVector) std::vector; -%template(ByteVectorVector) std::vector>; -%template(StringVector) std::vector; From be0848f440f9a699c026aaf3004bd05ba6be6ae7 Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:55:10 +0200 Subject: [PATCH 09/12] Remove unused TYPE_CHECKING block from __init__.py Union is only used in string annotations under `from __future__ import annotations`, so the import is never evaluated at runtime. Co-Authored-By: Claude Opus 4.6 --- src/pycdoc/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pycdoc/__init__.py b/src/pycdoc/__init__.py index d7c0392..a6d8a41 100644 --- a/src/pycdoc/__init__.py +++ b/src/pycdoc/__init__.py @@ -7,10 +7,6 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from typing import Union from pycdoc.libcdoc import ( # Version and utilities From ae46fb43b4f223b34e3a4155ab758989a187df0f Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:55:24 +0200 Subject: [PATCH 10/12] =?UTF-8?q?Fix=20RELINKING.md:=20Python=203.9+=20?= =?UTF-8?q?=E2=86=92=203.10+=20to=20match=20requires-python?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- RELINKING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELINKING.md b/RELINKING.md index 8fe2dab..cf90589 100644 --- a/RELINKING.md +++ b/RELINKING.md @@ -5,7 +5,7 @@ Section 6, you have the right to modify libcdoc and relink. Here's how. ## Prerequisites -- Python 3.9+ +- Python 3.10+ - CMake 3.20+ - C++23 compiler (GCC 13+, Clang 16+, MSVC 2022+) - SWIG 4.0+ From d95778413f67a7a2c08ee114adef90cecc8c0f04 Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:55:42 +0200 Subject: [PATCH 11/12] Add CLAUDE.md with project instructions Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c60bcc0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,53 @@ +# pycdoc + +Python bindings for [libcdoc](https://github.com/open-eid/libcdoc) via SWIG. Produces installable wheels for reading and writing encrypted CDOC containers. + +## Architecture + +- `libcdoc/` — upstream C++ library as a git submodule +- `patches/libcdoc-python.patch` — adds Python typemaps, templates, and director support to upstream `libcdoc.i` +- `CMakeLists.txt` — applies patch, builds upstream statically, then builds SWIG Python module +- `swig/pycdoc.i` — thin wrapper: `%rename` for snake_case + `%include "libcdoc.i"` +- `src/pycdoc/__init__.py` — re-exports SWIG symbols, provides high-level `encrypt()` API + +## Key decisions + +- **Static linking** (`BUILD_SHARED_LIBS=OFF`) — self-contained wheels, LGPL-2.1 compliance via RELINKING.md + sdist source +- **Patch-based approach** — upstream libcdoc.i is modified via `patches/libcdoc-python.patch` at CMake configure time (rather than forking) +- **SWIG `%rename` before `%include`** — order matters, rename rules must appear before upstream interface is included +- **`MACOSX_DEPLOYMENT_TARGET=15.0`** — required by Homebrew OpenSSL 3 + +## Test + +```bash +uv sync --dev +uv run pytest tests/ -v +``` + +## Build + +```bash +# System deps (Ubuntu): libssl-dev libxml2-dev zlib1g-dev flatbuffers-compiler libflatbuffers-dev +uv build --wheel +``` + +Uses scikit-build-core as the build backend bridging CMake. Requires C++23, SWIG 4.0+, CMake 3.20+. + +## Git + +Never use `git -C ` — always run git commands from the working directory. + +## CI + +`.github/workflows/build.yml` — 5 jobs: +1. `build` — every push, Ubuntu only (quick feedback) +2. `test` — runs pytest against the built wheel +3. `build_wheels` — on release/PR, cibuildwheel v3.3.1 across Linux/macOS/Windows +4. `build_sdist` — on release/PR +5. `publish` — OIDC trusted publishing to PyPI + +Linux wheels build OpenSSL and flatbuffers from source inside manylinux_2_28 with GCC 13. + +## Version management + +`bump-my-version` configured in pyproject.toml. Updates version in pyproject.toml, `__init__.py`, and CMakeLists.txt, commits, and tags. From 4340c85d5a075861a2446a9ce8131c9b9987200b Mon Sep 17 00:00:00 2001 From: Lenno Nagel Date: Tue, 17 Feb 2026 09:56:00 +0200 Subject: [PATCH 12/12] Add *.asice and *.bdoc to .gitignore Co-Authored-By: Claude Opus 4.6 --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 15b7a40..13552d0 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,11 @@ Thumbs.db # Test artifacts *.cdoc +*.asice +*.bdoc + +# Local notes +TODO.md # Lock files (library - not committed) uv.lock