From ff5c8cd61aa98c6ce9fffec3c3ae008c65320fb1 Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Wed, 15 Apr 2026 14:42:24 +0200 Subject: [PATCH 01/12] Remove duplicite Python 3.10 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a04510c..c1f30e6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "macos-latest"] - python-version: [ "3.8", "3.9", "3.10", "3.10", "3.11", "3.12", "3.13", "3.14" ] + python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14" ] name: Test (${{ matrix.python-version }}, ${{ matrix.os }}) runs-on: ${{ matrix.os }} defaults: From 0625974c3b98b34e7622d66d5216e440acd3caa5 Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Wed, 15 Apr 2026 14:52:11 +0200 Subject: [PATCH 02/12] Add Windows tests --- .github/workflows/test.yml | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c1f30e6..f1e7e21 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,13 +15,24 @@ jobs: strategy: fail-fast: false matrix: - os: ["ubuntu-latest", "macos-latest"] + os: ["ubuntu-latest", "macos-latest", "windows-latest"] python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14" ] - name: Test (${{ matrix.python-version }}, ${{ matrix.os }}) + compiler: ["default", "mingw", "msvc"] + exclude: + # MinGW and MSVC are Windows-only + - os: "ubuntu-latest" + compiler: "mingw" + - os: "ubuntu-latest" + compiler: "msvc" + - os: "macos-latest" + compiler: "mingw" + - os: "macos-latest" + compiler: "msvc" + name: Test (${{ matrix.python-version }}, ${{ matrix.os }}${{ matrix.os == 'windows-latest' && format(', {0}', matrix.compiler) || '' }}) runs-on: ${{ matrix.os }} defaults: run: - shell: bash -l {0} + shell: ${{ matrix.os == 'windows-latest' && 'pwsh' || 'bash -l {0}' }} steps: - uses: actions/checkout@v2 - uses: conda-incubator/setup-miniconda@v3 @@ -31,6 +42,10 @@ jobs: activate-environment: cppimport environment-file: environment.yml python-version: ${{ matrix.python-version }} + - name: Install MinGW-w64 compiler (Windows MinGW) + if: ${{ matrix.os == 'windows-latest' && matrix.compiler == 'mingw' }} + run: | + conda install -y -c conda-forge gxx_win-64 - name: Install cppimport run: | pip install --no-deps -e . @@ -43,12 +58,20 @@ jobs: - name: Check import ordering with isort run: | isort --check . - - name: Test + - name: Test (macOS) if: ${{ matrix.os == 'macos-latest' }} run: | CFLAGS='-stdlib=libc++' pytest --cov=./ --cov-report=xml - - name: Test - if: ${{ matrix.os != 'macos-latest' }} + - name: Test (Windows MinGW) + if: ${{ matrix.os == 'windows-latest' && matrix.compiler == 'mingw' }} + run: | + pytest --cov=./ --cov-report=xml + - name: Test (Windows MSVC) + if: ${{ matrix.os == 'windows-latest' && matrix.compiler == 'msvc' }} + run: | + pytest --cov=./ --cov-report=xml + - name: Test (Linux) + if: ${{ matrix.os == 'ubuntu-latest' }} run: | pytest --cov=./ --cov-report=xml build-and-publish: From 6423f9202125f0158ecd26cbe16eb7f5aedb0926 Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Wed, 15 Apr 2026 15:02:23 +0200 Subject: [PATCH 03/12] Fix templating.py formatting --- cppimport/templating.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cppimport/templating.py b/cppimport/templating.py index aaa9e20..b20cc32 100644 --- a/cppimport/templating.py +++ b/cppimport/templating.py @@ -65,25 +65,25 @@ def setup_pybind11(cfg): import pybind11 cfg["include_dirs"] += [pybind11.get_include(), pybind11.get_include(True)] - + # Prefix with c++11 arg instead of suffix so that if a user specifies c++14 # (or later!) then it won't be overridden. prefix_args = ["-std=c++11", "-fvisibility=hidden"] # MSVC compiler do not support "-std=c++11" nor "-fvisibility=hidden" if os.name == "nt": import sys - if sys.version_info[0] >=3 and sys.version_info[1] > 11: + if sys.version_info[0] >= 3 and sys.version_info[1] > 11: from setuptools._distutils import ccompiler else: from distutils import ccompiler compiler = ccompiler.new_compiler() if compiler.compiler_type == "msvc": - for k in ["-std=c++11", "-fvisibility=hidden"]: # remove unsupported flags + for k in ["-std=c++11", "-fvisibility=hidden"]: # remove unsupported flags try: prefix_args.remove(k) except ValueError: pass - + cfg["compiler_args"] = prefix_args + cfg["compiler_args"] From 0b95e5e0ff437a81413e9377fd00c25125c4ae6d Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Wed, 15 Apr 2026 15:11:53 +0200 Subject: [PATCH 04/12] Fix templating.py formatting again --- cppimport/templating.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cppimport/templating.py b/cppimport/templating.py index b20cc32..0a8c918 100644 --- a/cppimport/templating.py +++ b/cppimport/templating.py @@ -72,6 +72,7 @@ def setup_pybind11(cfg): # MSVC compiler do not support "-std=c++11" nor "-fvisibility=hidden" if os.name == "nt": import sys + if sys.version_info[0] >= 3 and sys.version_info[1] > 11: from setuptools._distutils import ccompiler else: From f9a1d207530a7c97a5000e4ebbcf143f1a630a01 Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Wed, 15 Apr 2026 15:30:22 +0200 Subject: [PATCH 05/12] Unload module, if previously imported --- tests/test_cppimport.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_cppimport.py b/tests/test_cppimport.py index a21942d..952d9ad 100644 --- a/tests/test_cppimport.py +++ b/tests/test_cppimport.py @@ -168,8 +168,12 @@ def test_no_rebuild_if_no_deps_change(): def test_rebuild_header_after_change(): cppimport.imp("mymodule") test_code = """ +import sys import cppimport; cppimport.settings["use_filelock"] = False; +# Remove any cached modules to ensure clean rebuild +if 'mymodule' in sys.modules: + del sys.modules['mymodule'] mymodule = cppimport.imp("mymodule"); mymodule.Thing().cheer() """ From fda75c6cb5102a7c52508c9cfbe2ae0660d3f724 Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Wed, 15 Apr 2026 15:57:08 +0200 Subject: [PATCH 06/12] Try suggestion from copilot to fix compilation error on Windows --- tests/test_cppimport.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_cppimport.py b/tests/test_cppimport.py index 952d9ad..2b1064e 100644 --- a/tests/test_cppimport.py +++ b/tests/test_cppimport.py @@ -179,7 +179,11 @@ def test_rebuild_header_after_change(): """ with appended("tests/thing.h", add_to_thing): subprocess_check(test_code) + # Ensure thing.h is empty and no stale artifacts remain assert open("tests/thing.h", "r").read() == "" + # Force cleanup of compiled artifacts before the header is reverted + cppimport.settings["force_rebuild"] = True + cppimport.settings["force_rebuild"] = False def test_raw_extensions(): From 0ae84bf41c8b3fa92f1e8dc606350e122c1ea21a Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Thu, 16 Apr 2026 17:07:52 +0200 Subject: [PATCH 07/12] Update flake8 in pre-commit hook --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14024e5..074c0a5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,8 +4,8 @@ repos: hooks: - id: black language_version: python3 - - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 + - repo: https://github.com/pycqa/flake8 + rev: 7.1.2 hooks: - id: flake8 - repo: https://github.com/pycqa/isort From 1213b5012c1fe9edacf6fdb0d3e5278dfd8a9c63 Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Thu, 16 Apr 2026 17:08:17 +0200 Subject: [PATCH 08/12] Add *.pyd, generated during running tests, to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 79dfb27..4fec0d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ *.swp *.pyc +*.pyd +*.pyd.old *.so *.cppimporthash .rendered.* From 192599f13a0cea9394d812e81ec9c95f285d51ae Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Thu, 16 Apr 2026 17:15:21 +0200 Subject: [PATCH 09/12] Update Black and isort versions in pre-commit hook --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 074c0a5..bc761e8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 24.10.0 hooks: - id: black language_version: python3 @@ -9,7 +9,7 @@ repos: hooks: - id: flake8 - repo: https://github.com/pycqa/isort - rev: 5.7.0 + rev: 5.13.2 hooks: - id: isort name: isort (python) From 3cac5bb0b62cef99a29fd0cf77d2a354ef984bc4 Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Thu, 16 Apr 2026 17:19:18 +0200 Subject: [PATCH 10/12] Fix test by renaming old file, instead of deleting, which is not possible on Windows. Revert changes in test_cppimport.py --- cppimport/build_module.py | 14 ++++++++++++++ tests/test_cppimport.py | 8 -------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/cppimport/build_module.py b/cppimport/build_module.py index 830e715..a09364e 100644 --- a/cppimport/build_module.py +++ b/cppimport/build_module.py @@ -131,6 +131,20 @@ def copy_extensions_to_source(self): src_filename = os.path.join(self.build_lib, filename) dest_filename = os.path.join(ext.libdest, os.path.basename(filename)) + # On Windows, a loaded .pyd cannot be deleted or overwritten, but + # it can be renamed. Move the old file out of the way first. + if sys.platform == "win32" and os.path.exists(dest_filename): + old_filename = dest_filename + ".old" + try: + if os.path.exists(old_filename): + os.remove(old_filename) + except OSError: + pass + try: + os.rename(dest_filename, old_filename) + except OSError: + pass + distutils.file_util.copy_file( src_filename, dest_filename, verbose=self.verbose ) diff --git a/tests/test_cppimport.py b/tests/test_cppimport.py index 2b1064e..a21942d 100644 --- a/tests/test_cppimport.py +++ b/tests/test_cppimport.py @@ -168,22 +168,14 @@ def test_no_rebuild_if_no_deps_change(): def test_rebuild_header_after_change(): cppimport.imp("mymodule") test_code = """ -import sys import cppimport; cppimport.settings["use_filelock"] = False; -# Remove any cached modules to ensure clean rebuild -if 'mymodule' in sys.modules: - del sys.modules['mymodule'] mymodule = cppimport.imp("mymodule"); mymodule.Thing().cheer() """ with appended("tests/thing.h", add_to_thing): subprocess_check(test_code) - # Ensure thing.h is empty and no stale artifacts remain assert open("tests/thing.h", "r").read() == "" - # Force cleanup of compiled artifacts before the header is reverted - cppimport.settings["force_rebuild"] = True - cppimport.settings["force_rebuild"] = False def test_raw_extensions(): From 0c72544e2744596db8c68ec3a051956fb5d018c7 Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Thu, 16 Apr 2026 17:38:22 +0200 Subject: [PATCH 11/12] Remove windows-latest, default from test matrix --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f1e7e21..d94767b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,8 @@ jobs: compiler: "mingw" - os: "macos-latest" compiler: "msvc" + - os: "windows-latest" + compiler: "default" name: Test (${{ matrix.python-version }}, ${{ matrix.os }}${{ matrix.os == 'windows-latest' && format(', {0}', matrix.compiler) || '' }}) runs-on: ${{ matrix.os }} defaults: From 35bd8e197d82a43a622c510a72213ec5e92ba94e Mon Sep 17 00:00:00 2001 From: Kuba Streit Date: Thu, 16 Apr 2026 18:01:48 +0200 Subject: [PATCH 12/12] Try to fix build-and-publish --- .github/workflows/test.yml | 3 +-- .gitignore | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d94767b..f2be675 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -84,13 +84,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: mamba-version: "*" channels: conda-forge activate-environment: cppimport environment-file: environment.yml - python-version: ${{ matrix.python-version }} - name: Get history and tags for SCM versioning to work run: | git fetch --prune --unshallow diff --git a/.gitignore b/.gitignore index 4fec0d8..d15c846 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ htmlcov **/.DS_Store .eggs cppimport/_version.py -*.lock \ No newline at end of file +*.lock +logs \ No newline at end of file